Esempio n. 1
0
    def test_correct_queue_for_template_unitary_with_keyword(self, template, kwarg, target_queue, parameters):
        """Tests that correct gate queue is created when 'unitary' is a template that uses a keyword."""

        with pennylane._queuing.OperationRecorder() as rec:
            broadcast(unitary=template, pattern="single", wires=range(2),
                      parameters=parameters, kwargs={'a': kwarg})

        for gate, target_gate in zip(rec.queue, target_queue):
            assert isinstance(gate, target_gate)
Esempio n. 2
0
    def test_correct_queue_same_gate_unitary_different_parameter_formats(self, pars1, pars2, gate):
        """Tests that specific parameter inputs have the same output."""

        with qml.tape.OperationRecorder() as rec1:
            broadcast(unitary=gate, pattern="single", wires=range(3), parameters=pars1)

        with qml.tape.OperationRecorder() as rec2:
            broadcast(unitary=gate, pattern="single", wires=range(3), parameters=pars2)

        for g1, g2 in zip(rec1.queue, rec2.queue):
            assert g1.parameters == g2.parameters
Esempio n. 3
0
    def test_correct_queue_for_gate_unitary(self, unitary, parameters):
        """Tests that correct gate queue is created when 'unitary' is a single gate."""

        with pennylane._queuing.OperationRecorder() as rec:
            broadcast(unitary=unitary,
                      pattern="single",
                      wires=range(3),
                      parameters=parameters)

        for gate in rec.queue:
            assert isinstance(gate, unitary)
Esempio n. 4
0
    def test_correct_parameters_in_queue(self, pattern, n_wires, gate,
                                         parameters):
        """Tests that gate queue has correct parameters."""

        with pennylane._queuing.OperationRecorder() as rec:
            broadcast(unitary=gate,
                      pattern=pattern,
                      wires=range(n_wires),
                      parameters=parameters)

        for target_par, g in zip(parameters, rec.queue):
            assert g.parameters == target_par
Esempio n. 5
0
def quantum_circ(params, x, wires=None, phi_x=None):

    # Encode input x into quantum state
    broadcast(unitary=Displacement,
              pattern="single",
              wires=wires,
              parameters=list(zip(x, phi_x)))
    CVNeuralNetLayersHomeMade(params[0], params[1], params[2], params[3],
                              params[4], params[5], params[6], params[7],
                              params[8], params[9], params[10], wires)

    return [expval(X(i)) for i in range(M)]
Esempio n. 6
0
def qaoa_feature_encoding_hamiltonian(features, wires):
    """Implements the encoding Hamiltonian of the QAOA embedding.

    Args:
        features (array): array of features to encode
        wires (Wires): wires that the template acts on
    """

    feature_encoding_wires = wires[: len(features)]
    remaining_wires = wires[len(features) :]

    broadcast(unitary=RX, pattern="single", wires=feature_encoding_wires, parameters=features)
    broadcast(unitary=Hadamard, pattern="single", wires=remaining_wires)
Esempio n. 7
0
    def test_correct_queue_for_template_unitary(self, unitary, gates, parameters):
        """Tests that correct gate queue is created when 'unitary' is a template."""

        with qml.tape.OperationRecorder() as rec:
            broadcast(unitary=unitary, pattern="single", wires=range(3), parameters=parameters)

        first_gate = gates[0]
        second_gate = gates[1]
        for idx, gate in enumerate(rec.queue):
            if idx % 2 == 0:
                assert isinstance(gate, first_gate)
            else:
                assert isinstance(gate, second_gate)
def strongly_entangling_layer(weights, wires, r, imprimitive):
    r"""A layer applying rotations on each qubit followed by cascades of 2-qubit entangling gates.

    Args:
        weights (array[float]): array of weights of shape ``(len(wires), 3)``
        wires (Sequence[int]): sequence of qubit indices that the template acts on
        r (int): range of the imprimitive gates of this layer, defaults to 1
        imprimitive (pennylane.ops.Operation): two-qubit gate to use, defaults to :class:`~pennylane.ops.CNOT`
    """

    broadcast(unitary=Rot, pattern="single", wires=wires, parameters=weights)

    n_wires = len(wires)
    if n_wires > 1:
        for i in range(n_wires):
            imprimitive(wires=[wires[i], wires[(i + r) % n_wires]])
def strongly_entangling_layer_rotxy(weights, wires, r, imprimitive):
    r"""A layer applying rotations on each qubit followed by cascades of 2-qubit entangling gates.

    Args:
        weights (tensor_like): weight tensor of shape ``(len(wires), 2)``
        wires (Wires): wires that the template acts on
        r (int): range of the imprimitive gates of this layer, defaults to 1
        imprimitive (pennylane.ops.Operation): two-qubit gate to use, defaults to :class:`~pennylane.ops.CNOT`
    """
    broadcast(unitary=RotXY, pattern="single", wires=wires, parameters=weights)

    n_wires = len(wires)
    if n_wires > 1:
        for i in range(n_wires):
            act_on = wires.subset([i, i + r], periodic_boundary=True)
            imprimitive(wires=act_on)
Esempio n. 10
0
def strongly_entangling_layer(weights, wires, r, imprimitive):
    r"""A layer applying rotations on each qubit followed by cascades of 2-qubit entangling gates.

    Args:
        weights (array[float]): array of weights of shape ``(len(wires), 3)``
        wires (Wires): wires that the template acts on
        r (int): range of the imprimitive gates of this layer, defaults to 1
        imprimitive (pennylane.ops.Operation): two-qubit gate to use, defaults to :class:`~pennylane.ops.CNOT`
    """

    broadcast(unitary=Rot, pattern="single", wires=wires, parameters=weights)

    n_wires = len(wires)
    if n_wires > 1:
        for i in range(n_wires):
            act_on = wires.subset([i, i + r], periodic_boundary=True)
            act_on = act_on.tolist()  # Todo: remove when operator takes Wires object
            imprimitive(wires=act_on)
Esempio n. 11
0
def qaoa_feature_encoding_hamiltonian(features, wires):
    """Implements the encoding Hamiltonian of the QAOA embedding.

    Args:
        features (tensor_like): array of features to encode
        wires (Wires): wires that the template acts on
    """

    try:
        # works for tensors
        n_features = features.shape[0]
    except AttributeError:
        # works for lists and tuples
        n_features = len(features)

    feature_encoding_wires = wires[:n_features]
    remaining_wires = wires[n_features:]

    broadcast(unitary=RX, pattern="single", wires=feature_encoding_wires, parameters=features)
    broadcast(unitary=Hadamard, pattern="single", wires=remaining_wires)
def cv_neural_net_layer(
    theta_1, 
    phi_1, 
    varphi_1, 
    r, 
    phi_r, 
    theta_2, 
    phi_2, 
    varphi_2, 
    a, 
    phi_a, 
    k, 
    wires):
    
    # unitary transformation
    Interferometer(theta=theta_1, phi=phi_1, varphi=varphi_1, wires=wires)
    
    # scaling
    broadcast(unitary=Squeezing, pattern="single", wires=wires, parameters=list(zip(r, phi_r)))
    
    # unitary transformation
    Interferometer(theta=theta_2, phi=phi_2, varphi=varphi_2, wires=wires)
    
    # bias
    broadcast(unitary=Displacement, pattern="single", wires=wires, parameters=list(zip(a, phi_a)))
    
    # non-linearity
    broadcast(unitary=Kerr, pattern="single", wires=wires, parameters=k)
Esempio n. 13
0
def quantum_circ1(x=None,
                  phi_x=None,
                  U1=None,
                  U2=None,
                  r=None,
                  phi_r=None,
                  phi_rot=None):

    M = len(x)
    wires = [wire for wire in range(M)]

    broadcast(unitary=Displacement,
              pattern="single",
              wires=wires,
              parameters=list(zip(x, phi_x)))

    Interferometer(U1, wires=wires)

    broadcast(unitary=Squeezing,
              pattern="single",
              wires=wires,
              parameters=list(zip(r, phi_r)))

    broadcast(unitary=Rotation,
              pattern="single",
              wires=wires,
              parameters=list(phi_rot))

    Interferometer(U2, wires=wires)

    return [expval(X(i)) for i in range(M)]
Esempio n. 14
0
def AngleEmbedding(features, wires, rotation="X"):
    r"""
    Encodes :math:`N` features into the rotation angles of :math:`n` qubits, where :math:`N \leq n`.

    The rotations can be chosen as either :class:`~pennylane.ops.RX`, :class:`~pennylane.ops.RY`
    or :class:`~pennylane.ops.RZ` gates, as defined by the ``rotation`` parameter:

    * ``rotation='X'`` uses the features as angles of RX rotations

    * ``rotation='Y'`` uses the features as angles of RY rotations

    * ``rotation='Z'`` uses the features as angles of RZ rotations

    The length of ``features`` has to be smaller or equal to the number of qubits. If there are fewer entries in
    ``features`` than rotations, the circuit does not apply the remaining rotation gates.

    Args:
        features (array): input array of shape ``(N,)``, where N is the number of input features to embed,
            with :math:`N\leq n`
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
        rotation (str): Type of rotations used

    Raises:
        ValueError: if inputs do not have the correct format
    """

    #############
    # Input checks

    wires = Wires(wires)

    check_shape(
        features,
        (len(wires),),
        bound="max",
        msg="'features' must be of shape {} or smaller; "
        "got {}.".format((len(wires),), get_shape(features)),
    )
    check_type(rotation, [str], msg="'rotation' must be a string; got {}".format(rotation))

    check_is_in_options(
        rotation,
        ["X", "Y", "Z"],
        msg="did not recognize option {} for 'rotation'.".format(rotation),
    )

    ###############

    if rotation == "X":
        broadcast(unitary=RX, pattern="single", wires=wires, parameters=features)

    elif rotation == "Y":
        broadcast(unitary=RY, pattern="single", wires=wires, parameters=features)

    elif rotation == "Z":
        broadcast(unitary=RZ, pattern="single", wires=wires, parameters=features)
Esempio n. 15
0
def AngleEmbedding(features, wires, rotation="X"):
    r"""
    Encodes :math:`N` features into the rotation angles of :math:`n` qubits, where :math:`N \leq n`.

    The rotations can be chosen as either :class:`~pennylane.ops.RX`, :class:`~pennylane.ops.RY`
    or :class:`~pennylane.ops.RZ` gates, as defined by the ``rotation`` parameter:

    * ``rotation='X'`` uses the features as angles of RX rotations

    * ``rotation='Y'`` uses the features as angles of RY rotations

    * ``rotation='Z'`` uses the features as angles of RZ rotations

    The length of ``features`` has to be smaller or equal to the number of qubits. If there are fewer entries in
    ``features`` than rotations, the circuit does not apply the remaining rotation gates.

    Args:
        features (array): input array of shape ``(N,)``, where N is the number of input features to embed,
            with :math:`N\leq n`
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
        rotation (str): type of rotations used

    """

    wires = Wires(wires)
    n_features = _preprocess(features, wires)
    wires = wires[:n_features]

    if rotation == "X":
        broadcast(unitary=qml.RX,
                  pattern="single",
                  wires=wires,
                  parameters=features)

    elif rotation == "Y":
        broadcast(unitary=qml.RY,
                  pattern="single",
                  wires=wires,
                  parameters=features)

    elif rotation == "Z":
        broadcast(unitary=qml.RZ,
                  pattern="single",
                  wires=wires,
                  parameters=features)

    else:
        raise ValueError(f"Rotation option {rotation} not recognized.")
Esempio n. 16
0
def cv_neural_net_layer(theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2,
                        varphi_2, a, phi_a, k, wires):

    Interferometer(theta=theta_1, phi=phi_1, varphi=varphi_1, wires=wires)

    broadcast(unitary=Squeezing,
              pattern="single",
              wires=wires,
              parameters=list(zip(r, phi_r)))

    Interferometer(theta=theta_2, phi=phi_2, varphi=varphi_2, wires=wires)

    broadcast(unitary=Displacement,
              pattern="single",
              wires=wires,
              parameters=list(zip(a, phi_a)))

    broadcast(unitary=Kerr, pattern="single", wires=wires, parameters=k)
Esempio n. 17
0
def cv_neural_net_layer(theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2,
                        varphi_2, a, phi_a, k, wires):
    r"""A single continuous-variable neural network layer.

    The layer acts on the :math:`M` wires modes specified in ``wires``, and includes interferometers
    of :math:`K=M(M-1)/2` beamsplitters.

    Args:
        theta_1 (tensor_like): shape :math:`(K, )` tensor of transmittivity angles for first interferometer
        phi_1 (tensor_like): shape :math:`(K, )` tensor of phase angles for first interferometer
        varphi_1 (tensor_like): shape :math:`(M, )` tensor of rotation angles to apply after first interferometer
        r (tensor_like): shape :math:`(M, )` tensor of squeezing amounts for
            :class:`~pennylane.ops.Squeezing` operations
        phi_r (tensor_like): shape :math:`(M, )` tensor of squeezing angles for
            :class:`~pennylane.ops.Squeezing` operations
        theta_2 (tensor_like): shape :math:`(K, )` tensor of transmittivity angles for second interferometer
        phi_2 (tensor_like): shape :math:`(K, )` tensor of phase angles for second interferometer
        varphi_2 (tensor_like): shape :math:`(M, )` tensor of rotation angles to apply after second interferometer
        a (tensor_like): shape :math:`(M, )` tensor of displacement magnitudes for
            :class:`~pennylane.ops.Displacement` operations
        phi_a (tensor_like): shape :math:`(M, )` tensor of displacement angles for
            :class:`~pennylane.ops.Displacement` operations
        k (tensor_like): shape :math:`(M, )` tensor of kerr parameters for :class:`~pennylane.ops.Kerr` operations
        wires (Wires): wires that the template acts on
    """
    Interferometer(theta=theta_1, phi=phi_1, varphi=varphi_1, wires=wires)

    broadcast(unitary=Squeezing,
              pattern="single",
              wires=wires,
              parameters=list(zip(r, phi_r)))

    Interferometer(theta=theta_2, phi=phi_2, varphi=varphi_2, wires=wires)

    broadcast(unitary=Displacement,
              pattern="single",
              wires=wires,
              parameters=list(zip(a, phi_a)))

    broadcast(unitary=Kerr, pattern="single", wires=wires, parameters=k)
Esempio n. 18
0
def CVNeuralNetLayers(
    theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, wires
):
    r"""A sequence of layers of a continuous-variable quantum neural network,
    as specified in `arXiv:1806.06871 <https://arxiv.org/abs/1806.06871>`_.

    The layer consists
    of interferometers, displacement and squeezing gates mimicking the linear transformation of
    a neural network in the x-basis of the quantum system, and uses a Kerr gate
    to introduce a 'quantum' nonlinearity.

    The layers act on the :math:`M` modes given in ``wires``,
    and include interferometers of :math:`K=M(M-1)/2` beamsplitters. The different weight parameters
    contain the weights for each layer. The number of layers :math:`L` is therefore derived
    from the first dimension of ``weights``.

    This example shows a 4-mode CVNeuralNet layer with squeezing gates :math:`S`, displacement gates :math:`D` and
    Kerr gates :math:`K`. The two big blocks are interferometers of type
    :mod:`pennylane.templates.layers.Interferometer`:

    .. figure:: ../../_static/layer_cvqnn.png
        :align: center
        :width: 60%
        :target: javascript:void(0);

    .. note::
       The CV neural network architecture includes :class:`~pennylane.ops.Kerr` operations.
       Make sure to use a suitable device, such as the :code:`strawberryfields.fock`
       device of the `PennyLane-SF <https://github.com/XanaduAI/pennylane-sf>`_ plugin.

    Args:
        theta_1 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for first interferometer
        phi_1 (tensor_like): shape :math:`(L, K)` tensor of phase angles for first interferometer
        varphi_1 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after first interferometer
        r (tensor_like): shape :math:`(L, M)` tensor of squeezing amounts for :class:`~pennylane.ops.Squeezing` operations
        phi_r (tensor_like): shape :math:`(L, M)` tensor of squeezing angles for :class:`~pennylane.ops.Squeezing` operations
        theta_2 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for second interferometer
        phi_2 (tensor_like): shape :math:`(L, K)` tensor of phase angles for second interferometer
        varphi_2 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after second interferometer
        a (tensor_like): shape :math:`(L, M)` tensor of displacement magnitudes for :class:`~pennylane.ops.Displacement` operations
        phi_a (tensor_like): shape :math:`(L, M)` tensor of displacement angles for :class:`~pennylane.ops.Displacement` operations
        k (tensor_like): shape :math:`(L, M)` tensor of kerr parameters for :class:`~pennylane.ops.Kerr` operations
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
    Raises:
        ValueError: if inputs do not have the correct format
    """

    wires = Wires(wires)
    repeat = _preprocess(
        theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, wires
    )

    for l in range(repeat):

        Interferometer(theta=theta_1[l], phi=phi_1[l], varphi=varphi_1[l], wires=wires)

        r_and_phi_r = qml.math.stack([r[l], phi_r[l]], axis=1)
        broadcast(unitary=Squeezing, pattern="single", wires=wires, parameters=r_and_phi_r)

        Interferometer(theta=theta_2[l], phi=phi_2[l], varphi=varphi_2[l], wires=wires)

        a_and_phi_a = qml.math.stack([a[l], phi_a[l]], axis=1)
        broadcast(unitary=Displacement, pattern="single", wires=wires, parameters=a_and_phi_a)

        broadcast(unitary=Kerr, pattern="single", wires=wires, parameters=k[l])
Esempio n. 19
0
 def circuit(pars):
     broadcast(unitary=RX, wires=range(2), pattern="hello", parameters=pars)
     return qml.expval(qml.PauliZ(0))
Esempio n. 20
0
 def circuit():
     broadcast(unitary=qml.PauliX,
               wires=range(4),
               pattern=custom_pattern)
     return [qml.expval(qml.PauliZ(w)) for w in range(4)]
Esempio n. 21
0
 def circuit2():
     broadcast(unitary=qml.CNOT, pattern=pattern, wires=range(4))
     return [qml.expval(qml.PauliZ(wires=w)) for w in range(4)]
Esempio n. 22
0
 def circuit():
     broadcast(unitary=RX,
               wires=range(n_wires),
               pattern="single",
               parameters=parameters)
     return qml.expval(qml.PauliZ(0))
Esempio n. 23
0
def IQPEmbedding(features, wires, n_repeats=1, pattern=None):
    r"""
    Encodes :math:`n` features into :math:`n` qubits using diagonal gates of an IQP circuit.

    The embedding has been proposed by `Havlicek et al. (2018) <https://arxiv.org/pdf/1804.11326.pdf>`_.

    The basic IQP circuit can be repeated by specifying ``n_repeats``. Repetitions can make the
    embedding "richer" through interference.

    .. warning::

        ``IQPEmbedding`` calls a circuit that involves non-trivial classical processing of the
        features. The ``features`` argument is therefore **not differentiable** when using the template, and
        gradients with respect to the features cannot be computed by PennyLane.

    An IQP circuit is a quantum circuit of a block of Hadamards, followed by a block of gates that are
    diagonal in the computational basis. Here, the diagonal gates are single-qubit ``RZ`` rotations, applied to each
    qubit and encoding the :math:`n` features, followed by two-qubit ZZ entanglers,
    :math:`e^{-i x_i x_j \sigma_z \otimes \sigma_z}`. The entangler applied to wires ``(wires[i], wires[j])``
    encodes the product of features ``features[i]*features[j]``. The pattern in which the entanglers are
    applied is either the default, or a custom pattern:

    * If ``pattern`` is not specified, the default pattern will be used, in which the entangling gates connect all
      pairs of neighbours:

      |

      .. figure:: ../../_static/templates/embeddings/iqp.png
          :align: center
          :width: 50%
          :target: javascript:void(0);

      |

    * Else, ``pattern`` is a list of wire pairs ``[[a, b], [c, d],...]``, applying the entangler
      on wires ``[a, b]``, ``[c, d]``, etc. For example, ``pattern = [[0, 1], [1, 2]]`` produces
      the following entangler pattern:

      |

      .. figure:: ../../_static/templates/embeddings/iqp_custom.png
          :align: center
          :width: 50%
          :target: javascript:void(0);

      |

      Since diagonal gates commute, the order of the entanglers does not change the result.

    Args:
        features (tensor_like): array of features to encode
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
        n_repeats (int): number of times the basic embedding is repeated
        pattern (list[int]): specifies the wires and features of the entanglers

    Raises:
        ValueError: if inputs do not have the correct format

    .. UsageDetails::

        A typical usage example of the template is the following:

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import IQPEmbedding

            dev = qml.device('default.qubit', wires=3)

            @qml.qnode(dev)
            def circuit(features=None):
                IQPEmbedding(features=features, wires=range(3))
                return [qml.expval(qml.PauliZ(w)) for w in range(3)]

            circuit(features=[1., 2., 3.])

        **Do not pass features as a positional argument to the qnode**

        The ``features`` argument cannot be passed to the quantum node
        as a positional argument. This is due to the fact that the embedding performs non-trivial calculations
        on the features. As a consequence, the following code **will produce an error**:

        .. code-block:: python

            @qml.qnode(dev)
            def circuit(features):
               IQPEmbedding(features=features, wires=range(3), n_repeats=2)
               return [qml.expval(qml.PauliZ(w)) for w in range(3)]

            circuit([1., 2., 3.])

        >>> ValueError: 'features' cannot be differentiable

        **Repeating the embedding**

        The embedding can be repeated by specifying the ``n_repeats`` argument:

        .. code-block:: python

            @qml.qnode(dev)
            def circuit(features=None):
                IQPEmbedding(features=features, wires=range(3), n_repeats=4)
                return [qml.expval(qml.PauliZ(w)) for w in range(3)]

            circuit(features=[1., 2., 3.])

        Every repetition uses exactly the same quantum circuit.

        **Using a custom entangler pattern**

        A custom entangler pattern can be used by specifying the ``pattern`` argument. A pattern has to be
        a nested list of dimension ``(K, 2)``, where ``K`` is the number of entanglers to apply.

        .. code-block:: python

            pattern = [[1, 2], [0, 2], [1, 0]]

            @qml.qnode(dev)
            def circuit(features=None):
                IQPEmbedding(features=features, wires=range(3), pattern=pattern)
                return [qml.expval(qml.PauliZ(w)) for w in range(3)]

            circuit(features=[1., 2., 3.])

        Since diagonal gates commute, the order of the wire pairs has no effect on the result.

        .. code-block:: python

            from pennylane import numpy as np

            pattern1 = [[1, 2], [0, 2], [1, 0]]
            pattern2 = [[1, 0], [0, 2], [1, 2]]  # a reshuffling of pattern1

            @qml.qnode(dev)
            def circuit(features=None, pattern=None):
                IQPEmbedding(features=features, wires=range(3), pattern=pattern, n_repeats=3)
                return [qml.expval(qml.PauliZ(w)) for w in range(3)]

            res1 = circuit(features=[1., 2., 3.], pattern=pattern1)
            res2 = circuit(features=[1., 2., 3.], pattern=pattern2)

            assert np.allclose(res1, res2)

        **Non-consecutive wires**

        In principle, the user can also pass a non-consecutive wire list to the template.
        For single qubit gates, the i'th feature is applied to the i'th wire index (which may not be the i'th wire).
        For the entanglers, the product of i'th and j'th features is applied to the wire indices at the i'th and j'th
        position in ``wires``.

        For example, for ``wires=[2, 0, 1]`` the ``RZ`` block applies the first feature to wire 2,
        the second feature to wire 0, and the third feature to wire 1.

        Likewise, using the default pattern, the entangler block applies the product of the first and second
        feature to the wire pair ``[2, 0]``, the product of the second and third feature to ``[2, 1]``, and so
        forth.

    """

    wires = Wires(wires)
    pattern = _preprocess(features, wires, pattern, n_repeats)

    for i in range(n_repeats):

        # first block of Hadamards
        broadcast(unitary=Hadamard, pattern="single", wires=wires)
        # encode features into block of RZ rotations
        broadcast(unitary=RZ,
                  pattern="single",
                  wires=wires,
                  parameters=features)

        # create new features for entangling block
        products = []
        for wire_pair in pattern:
            # get the position of the wire indices in the array
            idx1, idx2 = wires.indices(wire_pair)
            # create products of parameters
            products.append(features[idx1] * features[idx2])

        broadcast(unitary=MultiRZ,
                  pattern=pattern,
                  wires=wires,
                  parameters=products)
Esempio n. 24
0
def DisplacementEmbedding(features, wires, method="amplitude", c=0.1):
    r"""Encodes :math:`N` features into the displacement amplitudes :math:`r` or phases :math:`\phi` of :math:`M` modes,
    where :math:`N\leq M`.

    The mathematical definition of the displacement gate is given by the operator

    .. math::
            D(\alpha) = \exp(r (e^{i\phi}\ad -e^{-i\phi}\a)),

    where :math:`\a` and :math:`\ad` are the bosonic creation and annihilation operators.

    ``features`` has to be an array of at most ``len(wires)`` floats. If there are fewer entries in
    ``features`` than wires, the circuit does not apply the remaining displacement gates.

    Args:
        features (array): Array of features of size (N,)
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
        method (str): ``'phase'`` encodes the input into the phase of single-mode displacement, while
            ``'amplitude'`` uses the amplitude
        c (float): value of the phase of all displacement gates if ``execution='amplitude'``, or
            the amplitude of all displacement gates if ``execution='phase'``

    Raises:
        ValueError: if inputs do not have the correct format
    """

    #############
    # Input checks

    wires = Wires(wires)

    expected_shape = (len(wires), )
    check_shape(
        features,
        expected_shape,
        bound="max",
        msg="'features' must be of shape {} or smaller; got {}."
        "".format(expected_shape, get_shape(features)),
    )

    check_is_in_options(
        method,
        ["amplitude", "phase"],
        msg="did not recognize option {} for 'method'"
        "".format(method),
    )

    #############

    constants = [c] * len(features)

    if method == "amplitude":
        broadcast(
            unitary=Displacement,
            pattern="single",
            wires=wires,
            parameters=list(zip(features, constants)),
        )

    elif method == "phase":
        broadcast(
            unitary=Displacement,
            pattern="single",
            wires=wires,
            parameters=list(zip(constants, features)),
        )
Esempio n. 25
0
 def circuit():
     broadcast(unitary=qml.Hadamard,
               wires=[0, 1],
               pattern=custom_pattern)
     return qml.expval(qml.PauliZ(0))
Esempio n. 26
0
 def circuit():
     broadcast(unitary=RX,
               wires=[0, 1],
               pattern="single",
               parameters=RX)
     return qml.expval(qml.PauliZ(0))
Esempio n. 27
0
def SqueezingEmbedding(features, wires, method="amplitude", c=0.1):
    r"""Encodes :math:`N` features into the squeezing amplitudes :math:`r \geq 0` or phases :math:`\phi \in [0, 2\pi)`
    of :math:`M` modes, where :math:`N\leq M`.

    The mathematical definition of the squeezing gate is given by the operator

    .. math::

        S(z) = \exp\left(\frac{r}{2}\left(e^{-i\phi}\a^2 -e^{i\phi}{\ad}^{2} \right) \right),

    where :math:`\a` and :math:`\ad` are the bosonic creation and annihilation operators.

    ``features`` has to be an iterable of at most ``len(wires)`` floats. If there are fewer entries in
    ``features`` than wires, the circuit does not apply the remaining squeezing gates.

    Args:
        features (array): Array of features of size (N,)
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
        method (str): ``'phase'`` encodes the input into the phase of single-mode squeezing, while
            ``'amplitude'`` uses the amplitude
        c (float): value of the phase of all squeezing gates if ``execution='amplitude'``, or the
            amplitude of all squeezing gates if ``execution='phase'``

    Raises:
        ValueError: if inputs do not have the correct format
    """

    #############
    # Input checks

    wires = Wires(wires)

    check_no_variable(method, msg="'method' cannot be differentiable")
    check_no_variable(c, msg="'c' cannot be differentiable")

    check_type(c, [float, int], msg="'c' must be of type float or integer; got {}".format(type(c)))

    expected_shape = (len(wires),)
    check_shape(
        features,
        expected_shape,
        bound="max",
        msg="'features' must be of shape {} or smaller; got {}"
        "".format(expected_shape, get_shape(features)),
    )

    check_is_in_options(
        method,
        ["amplitude", "phase"],
        msg="did not recognize option {} for 'method'".format(method),
    )

    ##############

    constants = [c] * len(features)

    if method == "amplitude":
        broadcast(
            unitary=Squeezing,
            pattern="single",
            wires=wires,
            parameters=list(zip(features, constants)),
        )

    elif method == "phase":
        broadcast(
            unitary=Squeezing,
            pattern="single",
            wires=wires,
            parameters=list(zip(constants, features)),
        )
Esempio n. 28
0
 def circuit():
     for w in range(4):
         qml.PauliX(wires=w)
     broadcast(unitary=unitary, pattern=pattern, wires=range(4), parameters=parameters)
     return [qml.expval(qml.PauliZ(wires=w)) for w in range(4)]
Esempio n. 29
0
def SimplifiedTwoDesign(initial_layer_weights, weights, wires):
    r"""
    Layers consisting of a simplified 2-design architecture of Pauli-Y rotations and controlled-Z entanglers
    proposed in `Cerezo et al. (2020) <https://arxiv.org/abs/2001.00550>`_.

    A 2-design is an ensemble of unitaries whose statistical properties are the same as sampling random unitaries
    with respect to the Haar measure up to the first 2 moments.

    The template is not a strict 2-design, since
    it does not consist of universal 2-qubit gates as building blocks, but has been shown in
    `Cerezo et al. (2020) <https://arxiv.org/abs/2001.00550>`_ to exhibit important properties to study "barren plateaus"
    in quantum optimization landscapes.

    The template starts with an initial layer of single qubit Pauli-Y rotations, before the main
    :math:`L` layers are applied. The basic building block of the main layers are controlled-Z entanglers
    followed by a pair of Pauli-Y rotation gates (one for each wire).
    Each layer consists of an "even" part whose entanglers start with the first qubit,
    and an "odd" part that starts with the second qubit.

    This is an example of two layers, including the initial layer:

    .. figure:: ../../_static/templates/layers/simplified_two_design.png
        :align: center
        :width: 40%
        :target: javascript:void(0);

    |

    The argument ``initial_layer_weights`` contains the rotation angles of the initial layer of Pauli-Y rotations,
    while ``weights`` contains the pairs of Pauli-Y rotation angles of the respective layers. Each layer takes
    :math:`\lfloor M/2 \rfloor + \lfloor (M-1)/2 \rfloor = M-1` pairs of angles, where :math:`M` is the number of wires.
    The number of layers :math:`L` is derived from the first dimension of ``weights``.

    Args:
        initial_layer_weights (tensor_like): weight tensor for the initial rotation block, shape ``(M,)``
        weights (tensor_like): tensor of rotation angles for the layers, shape ``(L, M-1, 2)``
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.

    Raises:
        ValueError: if inputs do not have the correct format

    .. UsageDetails::

        template - here shown for two layers - is used inside a :class:`~.QNode`:

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import SimplifiedTwoDesign
            from math import pi

            n_wires = 3
            dev = qml.device('default.qubit', wires=n_wires)

            @qml.qnode(dev)
            def circuit(init_weights, weights):
                SimplifiedTwoDesign(initial_layer_weights=init_weights, weights=weights, wires=range(n_wires))
                return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)]

            init_weights = [pi, pi, pi]
            weights_layer1 = [[0., pi],
                              [0., pi]]
            weights_layer2 = [[pi, 0.],
                              [pi, 0.]]
            weights = [weights_layer1, weights_layer2]

            >>> circuit(init_weights, weights)
            [1., -1., 1.]

        **Parameter initialization function**

        The :mod:`~pennylane.init` module contains four parameter initialization functions:

        * ``simplified_two_design_initial_layer_normal``
        * ``simplified_two_design_initial_layer_uniform``
        * ``simplified_two_design_weights_normal``.
        * ``simplified_two_design_weights_uniform``.

        They can be used as follows:

        .. code-block:: python

            from pennylane.init import (simplified_two_design_initial_layer_normal,
                                        simplified_two_design_weights_normal)

            n_layers = 4
            init_weights = simplified_two_design_initial_layer_normal(n_wires)
            weights = simplified_two_design_weights_normal(n_layers, n_wires)

            >>> circuit(initial_layer_weights, weights)

    """

    wires = Wires(wires)
    repeat = _preprocess(weights, initial_layer_weights, wires)

    # initial rotations
    broadcast(unitary=RY,
              pattern="single",
              wires=wires,
              parameters=initial_layer_weights)

    # alternate layers
    for layer in range(repeat):

        # even layer
        weights_even = weights[layer][:len(wires) // 2]
        broadcast(unitary=entangler,
                  pattern="double",
                  wires=wires,
                  parameters=weights_even)

        # odd layer
        weights_odd = weights[layer][len(wires) // 2:]
        broadcast(unitary=entangler,
                  pattern="double_odd",
                  wires=wires,
                  parameters=weights_odd)
Esempio n. 30
0
def BasicEntanglerLayers(weights, wires, rotation=None):
    r"""Layers consisting of one-parameter single-qubit rotations on each qubit, followed by a closed chain
    or *ring* of CNOT gates.

    The ring of CNOT gates connects every qubit with its neighbour,
    with the last qubit being considered as a neighbour to the first qubit.

    .. figure:: ../../_static/templates/layers/basic_entangler.png
        :align: center
        :width: 40%
        :target: javascript:void(0);

    The number of layers :math:`L` is determined by the first dimension of the argument ``weights``.
    When using a single wire, the template only applies the single
    qubit gates in each layer.

    .. note::

        This template follows the convention of dropping the entanglement between the last and the first
        qubit when using only two wires, so the entangler is not repeated on the same wires.
        In this case, only one CNOT gate is applied in each layer:

        .. figure:: ../../_static/templates/layers/basic_entangler_2wires.png
            :align: center
            :width: 30%
            :target: javascript:void(0);

    Args:
        weights (array[float]): array of weights with shape ``(L, len(wires))``, each weight is used as a parameter
                                for the rotation
        wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or
            a Wires object.
        rotation (pennylane.ops.Operation): one-parameter single-qubit gate to use,
                                            if ``None``, :class:`~pennylane.ops.RX` is used as default
    Raises:
        ValueError: if inputs do not have the correct format

    .. UsageDetails::

        The template is used inside a qnode:

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import BasicEntanglerLayers
            from math import pi

            n_wires = 3
            dev = qml.device('default.qubit', wires=n_wires)

            @qml.qnode(dev)
            def circuit(weights):
                BasicEntanglerLayers(weights=weights, wires=range(n_wires))
                return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)]

        >>> circuit([[pi, pi, pi]])
        [1., 1., -1.]

        **Parameter initialization function**

        The :mod:`~pennylane.init` module has two parameter initialization functions, ``basic_entangler_layers_normal``
        and ``basic_entangler_layers_uniform``.

        .. code-block:: python

            from pennylane.init import basic_entangler_layers_normal

            n_layers = 4
            weights = basic_entangler_layers_normal(n_layers=n_layers, n_wires=n_wires)

            circuit(weights)


        **No periodic boundary for two wires**

        When using two wires, the convention is to drop the periodic boundary condition.
        This means that the connection from the second to the first wire is omitted.

        .. code-block:: python

            n_wires = 2
            dev = qml.device('default.qubit', wires=n_wires)

            @qml.qnode(dev)
            def circuit(weights):
                BasicEntanglerLayers(weights=weights, wires=range(n_wires))
                return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)]

        >>> circuit([[pi, pi]])
        [-1, 1]


        **Changing the rotation gate**

        Any single-qubit gate can be used as a rotation gate, as long as it only takes a single parameter. The default is the ``RX`` gate.

        .. code-block:: python

            @qml.qnode(dev)
            def circuit(weights):
                BasicEntanglerLayers(weights=weights, wires=range(n_wires), rotation=qml.RZ)
                return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_wires)]

        Accidentally using a gate that expects more parameters throws a
        ``ValueError: Wrong number of parameters``.
    """

    #############
    # Input checks

    if rotation is None:
        rotation = RX

    wires = Wires(wires)

    repeat = check_number_of_layers([weights])

    expected_shape = (repeat, len(wires))
    check_shape(
        weights,
        expected_shape,
        msg="'weights' must be of shape {}; got {}"
        "".format(expected_shape, get_shape(weights)),
    )

    ###############

    for layer in range(repeat):

        broadcast(unitary=rotation,
                  pattern="single",
                  wires=wires,
                  parameters=weights[layer])
        broadcast(unitary=CNOT, pattern="ring", wires=wires)