Source code for qnetvo.ansatz_library

import pennylane as qml
from pennylane.operation import Channel
import numpy as np
import math

eps = 1e-7  # constant


[docs]def max_entangled_state(settings, wires): """Ansatz function for maximally entangled two-qubit state preparation. A general ``qml.Rot`` unitary is applied to one-side of a Bell state creating a general parameterization of all maximally entangled states. :param settings: A list with three elements parameterizing a general single-qubit unitary :type settings: list[float] :param wires: The two wires on which the maximally entangled state is prepared. :type wires: qml.Wires """ # prepare bell state qml.Hadamard(wires=wires[0]) qml.CNOT(wires=wires[0:2]) # perform general rotation on first qubit qml.Rot(*settings, wires=wires[0])
[docs]def nonmax_entangled_state(settings, wires): """Initializes a nonmaximally entangled GHZ-like state that has a bias between its two outcomes. The state takes the form .. math:: |\\psi_\\theta\\rangle = \\cos(\\frac{\\theta}{2})|0\\dots 0\\rangle + \\sin(\\frac{\\theta}{2})|1 \\dots 1\\rangle :param settings: A list of length 1 containing paraameter :math:`\\theta` :type settings: list[float] :param wires: A list of wires to prepare the nonmaximally entangled GHZ-like state. :type wires: list[int] or qml.Wires """ qml.RY(settings[0], wires=wires[0]) for i in range(1, len(wires)): qml.CNOT(wires=[wires[0], wires[i]])
[docs]def bell_state_copies(settings, wires): """Initializes :math:`n` Bell states on :math:`2n` wires. The first :math:`n` wires represent Alice's half of the entangled state while Bob's half consists of the remaining :math:`n` wires. Entanglement is shared between Alice and Bob, however, the independent Bell states are not initially entangled with each other. :param settings: A placeholder parameter that is not used. :type settings: list[empty] :param wires: The wires on which the bell states are prepared. :type wires: qml.Wires """ for i in range(len(wires) // 2): qml.Hadamard(wires=wires[i]) qml.CNOT(wires=[wires[i], wires[i + len(wires) // 2]])
[docs]def ghz_state(settings, wires): """Initializes a GHZ state on the provided wires. :param settings: A placeholder parameter that is not used. :type settings: list[empty] :param wires: The wires on which the GHZ state is prepared. :type wires: qml.Wires """ qml.Hadamard(wires=wires[0]) for i in range(1, len(wires)): qml.CNOT(wires=[wires[0], wires[i]])
[docs]def W_state(settings, wires): """Initializes the three-qubit :math:`W`-state on the specified wires. .. math:: |\\psi^W\\rangle = \\frac{1}{\\sqrt{3}}(|100\\rangle + |010\\rangle + |001 \\rangle) :param settings: A placeholder parameter that is not used. :type settings: list[empty] :param wires: The wires on which the :math:`W`-state is prepared. :type wires: qml.Wires """ phi = 2 * np.arccos(1 / np.sqrt(3)) qml.RY(phi, wires=wires[0]) qml.CRY(np.pi / 2, wires=wires[0:2]) qml.CNOT(wires=wires[1:3]) qml.CNOT(wires=wires[0:2]) qml.PauliX(wires=wires[0])
[docs]def graph_state_fn(edges): """Constructs a quantum function that prepares a graph state where each qubit wire is a vertex and the ``edge`` are tuple pairs of interacting wires. A graph state takes the form .. math:: |\\psi^G \\rangle = \\prod_{(a,b) \\in Edges} CZ^{(a,b)} |+\\rangle^{\\otimes N} where :math:`N` is the number of qubits, :math:`|+\\rangle = \\frac{1}{\\sqrt{2}}(|0\\rangle + |1\\rangle)`, :math:`(a,b)` is a pair of wires defining an edge, and :math:`CZ^{(a,b)}` is a controlled-phase operation operating upon the qubit pair :math:`(a,b)`. For more details, see the :meth:`qml.CZ` function in the `PennyLane docs <https://docs.pennylane.ai/en/stable/>`_. :param edges: A list of qubit pair tuples defining the wires to which controlled-phase operations are applied. :type settings: list[tuple[int]] :returns: A quantum circuit `graph_state(settings, wires)` that prepares the specified graph state. Note that ``wires`` must contain all qubits specified in ``edges``. :rtype: Function """ def graph_state(settings, wires): for wire in wires: qml.Hadamard(wire) for edge in edges: qml.CZ(wires=[wires[edge[0]], wires[edge[1]]]) return graph_state
[docs]def shared_coin_flip_state(settings, wires): """Initializes a state that mirrors a biased coin flip shared amongst the qubits on ``wires[0:-1]`` where ``wires[-1]`` is an ansatz used to generate shared randomness. The shared coin flip is represented by the density matrix. .. math:: \\rho_\\theta = \\cos^2(\\frac{\\theta}{2})|0\\dots 0\\rangle\\langle 0 \\dots 0| + \\sin^2(\\frac{\\theta}{2})|1\\dots 1 \\rangle \\langle 1 \\dots 1| :param settings: A list of 1 real value. :type settings: list[float] :param wires: The wires used to prepare the shaared coin flip state. Note that the last wire is used as an ancilla. :type wires: qml.Wires """ nonmax_entangled_state(settings, wires)
[docs]def local_RY(settings, wires): """Performs a rotation about :math:`y`-axis on each qubit specified by ``wires``. :param settings: A list of ``len(wires)`` real values. :type settings: list[float] :param wires: The wires on which the rotations are applied. :type wires: qml.Wires """ qml.broadcast(qml.RY, wires, "single", settings)
[docs]def local_Rot(settings, wires): """Performs an arbitrary qubit unitary as defined by :meth:`qml.Rot` on each qubit specified by ``wires``. For more details on :meth:`qml.Rot` please refer to the `PennyLane docs <https://docs.pennylane.ai/en/stable/>`_. :param settings: A list of ``3 * len(wires)`` real values. :type settings: list[float] :param wires: The wires to which the qubit unitaries are applied. :type wires: qml.Wires """ for i in range(len(wires)): qml.Rot(*settings[3 * i : 3 * i + 3], wires=wires[i])
[docs]def local_RXRY(settings, wires): """Performs a rotation about the :math:`x` and :math:`y` axes on each qubit specified by ``wires``. :param settings: A list of ``2*len(wires)`` real values. :type settings: list[float] :param wires: The wires on which the rotations are applied. :type wires: qml.Wires """ for i, wire in enumerate(wires): qml.RX(settings[2 * i], wires=wire) qml.RY(settings[2 * i + 1], wires=wire)
[docs]def pure_amplitude_damping(noise_params, wires): """Implements a single qubit amplitude damping channel as a two-qubit gate. This allows amplitude damping noise to be applied on quantum hardware or on a state-vector simulator such as ``"default.qubit"``. This method is equivalent to the `pennylane.AmplitudeDamping <https://pennylane.readthedocs.io/en/stable/code/api/pennylane.AmplitudeDamping.html>`_ method. The corresponding Kraus operators are expressed as: .. math:: K_0 = \\begin{pmatrix}1 & 0 \\\\ 0 & \\sqrt{1-\\gamma} \\end{pmatrix}, \\quad K_1 = \\begin{pmatrix}0 & \\sqrt{\\gamma} \\\\ 0 & 0 \\end{pmatrix}. :param noise_params: The parameter ``[gamma]`` quantifying the amount of amplitude damping noise. :type noise_params: List[Float] :param wires: Two wires on which to implement the amplitude damping channel. The channel input and output is on ``wires[0]`` while ``wires[1]`` serves as the ancilla register. :type wires: qml.Wires """ ry_setting = 2 * math.asin(math.sqrt(noise_params[0])) qml.ctrl(qml.RY, control=wires[0])(ry_setting, wires=wires[1]) qml.CNOT(wires=[wires[1], wires[0]])
[docs]def pure_phase_damping(noise_params, wires): """Implements a single qubit phase damping channel as a two-qubit gate. This allows phase damping noise to be applied on quantum hardware or on a state-vector simulator such as ``"default.qubit"``. This method is equivalent to the `pennylane.PhaseDamping <https://pennylane.readthedocs.io/en/stable/code/api/pennylane.PhaseDamping.html>`_ method. The corresponding Kraus operators are expressed as: .. math:: K_0 = \\begin{pmatrix}1 & 0 \\\\ 0 & \\sqrt{1-\\gamma} \\end{pmatrix}, \\quad K_1 = \\begin{pmatrix}0 & 0 \\\\ 0 & \\sqrt{\\gamma} \\end{pmatrix}. :param noise_params: The parameter ``[gamma]`` quantifying the amount of phase damping noise. :type noise_params: List[Float] :param wires: Two wires on which to implement the phase damping channel. The channel input and output is on ``wires[0]`` while ``wires[1]`` serves as the ancilla register. :type wires: qml.Wires """ ry_setting = 2 * math.asin(math.sqrt(noise_params[0])) qml.ctrl(qml.RY, control=wires[0])(ry_setting, wires=wires[1])
[docs]class two_qubit_depolarizing(Channel): """Applies a two-qubit depolarizing channel using Kraus operators. The channel is called as a quantum function ``two_qubit_depolarizing(gamma, wires)`` :param gamma: The amount of depolarizing noise in the channel. :type gamma: Float :param wires: Two wires on which to apply the depolarizing noise. :type wires: qml.Wires For a noise parameter :math:`\\gamma`, the two-qubit depolarizing noise model is represented by the following function: .. math:: \\mathcal{N}(\\rho) = (1-\\frac{16}{15}\\gamma)\\rho + (\\frac{16}{15})(\\frac{\\gamma}{4})) \\mathbb{I} :raises ValueError: if ``0 <= gamma <= 1`` is not satisfied. """ num_params = 1 num_wires = 2 grad_method = "F" @staticmethod def compute_kraus_matrices(gamma): """Kraus matrices representing the ``two_qubit_depolarizing`` channel. :param gamma: The amount of depolarizing noise in the channel. :type gamma: Float :returns: The Kraus matrices. :rtype: List[Array] """ if not 0.0 - eps <= gamma <= 1.0 + eps: raise ValueError("gamma must be in the interval [0,1].") elif np.isclose(gamma, 0): gamma = 0 paulis = [ np.array([[1, 0], [0, 1]]), np.array([[0, 1], [1, 0]]), np.array([[0, -1j], [1j, 0]]), np.array([[1, 0], [0, -1]]), ] kraus_ops = [] for i in range(4): for j in range(4): scalar = np.sqrt(1 - gamma) if i + j == 0 else np.sqrt(gamma / 15) kraus_ops.append(scalar * np.kron(paulis[i], paulis[j])) return kraus_ops
[docs]class colored_noise(Channel): """Applies a two-qubit colored noise channel using Kraus operators. The channel is called as a quantum function ``colored_noise(gamma, wires)``. :param gamma: The amount of colored noise in the channel. :type gamma: Float :param wires: Two wires on which to apply the colored noise. :type wires: qml.Wires For the noise parameter :math:`\\gamma`, the colored noise model is represented by the following function: .. math:: \\mathcal{N}(\\rho) = (1-\\gamma)\\rho + \\frac{\\gamma}{2}(|01\\rangle\\langle 01| + |10\\rangle\\langle 10|) :raises ValueError: if ``0 <= gamma <= 1`` is not satisfied. """ num_params = 1 num_wires = 2 grad_method = "F" @staticmethod def compute_kraus_matrices(gamma): """Kraus matrices representing the ``colored_noise`` channel. :param gamma: The amount of colored noise in the channel. :type gamma: Float :returns: The Kraus matrices. :rtype: List[Array] """ if not 0.0 - eps <= gamma <= 1.0 + eps: raise ValueError("gamma must be in the interval [0,1].") elif np.isclose(gamma, 0): gamma = 0 phi_plus = np.array([1, 0, 0, 1]) / np.sqrt(2) phi_min = np.array([1, 0, 0, -1]) / np.sqrt(2) psi_plus = np.array([0, 1, 1, 0]) / np.sqrt(2) psi_min = np.array([0, 1, -1, 0]) / np.sqrt(2) scalar = np.sqrt(gamma / 2) K0 = np.sqrt(1 - gamma) * np.eye(4) K1 = scalar * np.outer(psi_plus, phi_plus) K2 = scalar * np.outer(psi_plus, phi_min) K3 = scalar * np.outer(psi_plus, psi_plus) K4 = scalar * np.outer(psi_plus, psi_min) K5 = scalar * np.outer(psi_min, phi_plus) K6 = scalar * np.outer(psi_min, phi_min) K7 = scalar * np.outer(psi_min, psi_plus) K8 = scalar * np.outer(psi_min, psi_min) return [K0, K1, K2, K3, K4, K5, K6, K7, K8]