Ejemplo n.º 1
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 (array[float]): length :math:`(L, K)` array of transmittivity angles for first interferometer
        phi_1 (array[float]): length :math:`(L, K)` array of phase angles for first interferometer
        varphi_1 (array[float]): length :math:`(L, M)` array of rotation angles to apply after first interferometer
        r (array[float]): length :math:`(L, M)` array of squeezing amounts for :class:`~pennylane.ops.Squeezing` operations
        phi_r (array[float]): length :math:`(L, M)` array of squeezing angles for :class:`~pennylane.ops.Squeezing` operations
        theta_2 (array[float]): length :math:`(L, K)` array of transmittivity angles for second interferometer
        phi_2 (array[float]): length :math:`(L, K)` array of phase angles for second interferometer
        varphi_2 (array[float]): length :math:`(L, M)` array of rotation angles to apply after second interferometer
        a (array[float]): length :math:`(L, M)` array of displacement magnitudes for :class:`~pennylane.ops.Displacement` operations
        phi_a (array[float]): length :math:`(L, M)` array of displacement angles for :class:`~pennylane.ops.Displacement` operations
        k (array[float]): length :math:`(L, M)` array of kerr parameters for :class:`~pennylane.ops.Kerr` operations
        wires (Sequence[int]): sequence of mode indices that the template acts on

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

    #############
    # Input checks
    wires, n_wires = _check_wires(wires)

    n_if = n_wires * (n_wires - 1) // 2
    weights_list = [
        theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a,
        k
    ]
    repeat = _check_number_of_layers(weights_list)

    shapes_list = [(repeat, n_if), (repeat, n_if), (repeat, n_wires),
                   (repeat, n_wires), (repeat, n_wires), (repeat, n_if),
                   (repeat, n_if), (repeat, n_wires), (repeat, n_wires),
                   (repeat, n_wires), (repeat, n_wires)]
    _check_shapes(weights_list, shapes_list)

    _check_type(repeat, [int])
    ###############

    for l in range(repeat):
        _cv_neural_net_layer(theta_1=theta_1[l],
                             phi_1=phi_1[l],
                             varphi_1=varphi_1[l],
                             r=r[l],
                             phi_r=phi_r[l],
                             theta_2=theta_2[l],
                             phi_2=phi_2[l],
                             varphi_2=varphi_2[l],
                             a=a[l],
                             phi_a=phi_a[l],
                             k=k[l],
                             wires=wires)
Ejemplo n.º 2
0
def RandomLayers(weights,
                 wires,
                 ratio_imprim=0.3,
                 imprimitive=CNOT,
                 rotations=None,
                 seed=42):
    r"""Layers of randomly chosen single qubit rotations and 2-qubit entangling gates, acting
    on randomly chosen qubits.

    The argument ``weights`` contains the weights for each layer. The number of layers :math:`L` is therefore derived
    from the first dimension of ``weights``.

    The two-qubit gates of type ``imprimitive`` and the rotations are distributed randomly in the circuit.
    The number of random rotations is derived from the second dimension of ``weights``. The number of
    two-qubit gates is determined by ``ratio_imprim``. For example, a ratio of ``0.3`` with ``30`` rotations
    will lead to the use of ``10`` two-qubit gates.

    .. note::
        If applied to one qubit only, this template will use no imprimitive gates.

    This is an example of two 4-qubit random layers with four Pauli-Y/Pauli-Z rotations :math:`R_y, R_z`,
    controlled-Z gates as imprimitives, as well as ``ratio_imprim=0.3``:

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

    .. note::
        Using the default seed (or any other fixed integer seed) generates one and the same circuit in every
        quantum node. To generate different circuit architectures, either use a different random seed, or use ``seed=None``
        together with the ``cache=False`` option when creating a quantum node.

    .. warning::
        When using a random number generator anywhere inside the quantum function without the ``cache=False`` option,
        a new random circuit architecture will be created every time the quantum node is evaluated.

    Args:
        weights (array[float]): array of weights of shape ``(L, k)``,
        wires (Sequence[int]): sequence of qubit indices that the template acts on
        ratio_imprim (float): value between 0 and 1 that determines the ratio of imprimitive to rotation gates
        imprimitive (pennylane.ops.Operation): two-qubit gate to use, defaults to :class:`~pennylane.ops.CNOT`
        rotations (list[pennylane.ops.Operation]): List of Pauli-X, Pauli-Y and/or Pauli-Z gates. The frequency
            determines how often a particular rotation type is used. Defaults to the use of all three
            rotations with equal frequency.
        seed (int): seed to generate random architecture

    Raises:
        ValueError: if inputs do not have the correct format
    """
    if seed is not None:
        np.random.seed(seed)

    if rotations is None:
        rotations = [RX, RY, RZ]

    #############
    # Input checks
    hyperparams = [ratio_imprim, imprimitive, rotations, seed]
    hyperparam_names = ['ratio_imprim', 'imprimitive', 'rotations', 'seed']
    _check_no_variable(hyperparams, hyperparam_names)

    wires, _ = _check_wires(wires)

    repeat = _check_number_of_layers([weights])
    n_rots = _get_shape(weights)[1]

    _check_shape(weights, (repeat, n_rots))

    _check_type(ratio_imprim, [float, type(None)])
    _check_type(n_rots, [int, type(None)])
    _check_type(rotations, [list, type(None)])
    _check_type(seed, [int, type(None)])
    ###############

    for l in range(repeat):
        _random_layer(weights=weights[l],
                      wires=wires,
                      ratio_imprim=ratio_imprim,
                      imprimitive=imprimitive,
                      rotations=rotations,
                      seed=seed)
Ejemplo n.º 3
0
def StronglyEntanglingLayers(weights, wires, ranges=None, imprimitive=CNOT):
    r"""Layers consisting of single qubit rotations and entanglers, inspired by the circuit-centric classifier design
    `arXiv:1804.00633 <https://arxiv.org/abs/1804.00633>`_.

    The argument ``weights`` contains the weights for each layer. The number of layers :math:`L` is therefore derived
    from the first dimension of ``weights``.

    The 2-qubit gates, whose type is specified by the ``imprimitive`` argument,
    act chronologically on the :math:`M` wires, :math:`i = 1,...,M`. The second qubit of each gate is given by
    :math:`(i+r)\mod M`, where :math:`r` is a  hyperparameter called the *range*, and :math:`0 < r < M`.
    If applied to one qubit only, this template will use no imprimitive gates.

    This is an example of two 4-qubit strongly entangling layers (ranges :math:`r=1` and :math:`r=2`, respectively) with
    rotations :math:`R` and CNOTs as imprimitives:

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

    Args:

        weights (array[float]): array of weights of shape ``(:math:`L`, :math:`M`, 3)``
        wires (Sequence[int] or int): qubit indices that the template acts on
        ranges (Sequence[int]): sequence determining the range hyperparameter for each subsequent layer; if None
                                using :math:`r=l \mod M` for the :math:`l`th layer and :math:`M` wires.
        imprimitive (pennylane.ops.Operation): two-qubit gate to use, defaults to :class:`~pennylane.ops.CNOT`

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

    #############
    # Input checks
    _check_no_variable([ranges, imprimitive], ['ranges', 'imprimitive'])

    wires, n_wires = _check_wires(wires)

    repeat = _check_number_of_layers([weights])

    _check_shape(weights, (repeat, n_wires, 3))

    _check_type(ranges, [list, type(None)])

    if ranges is None:
        # Tile ranges with iterations of range(1, n_wires)
        ranges = [(l % (n_wires - 1)) + 1 for l in range(repeat)]

    msg = "StronglyEntanglingLayers expects ``ranges`` to contain a range for each layer; " \
          "got {}.".format(len(ranges))
    _check_shape(ranges, (repeat, ), msg=msg)
    msg = "StronglyEntanglingLayers expects ``ranges`` to be a list of integers; got {}.".format(
        len(ranges))
    _check_type(ranges[0], [int], msg=msg)
    if any((r >= n_wires or r == 0) for r in ranges):
        raise ValueError(
            "The range hyperparameter for all layers needs to be smaller than the number of "
            "qubits; got ranges {}.".format(ranges))
    ###############

    for l in range(repeat):

        _strongly_entangling_layer(weights=weights[l],
                                   wires=wires,
                                   r=ranges[l],
                                   imprimitive=imprimitive)
Ejemplo n.º 4
0
 def test_check_num_layers_exception(self, inpt, repeat):
     """Tests that layer check throws exception for invalid arguments."""
     with pytest.raises(ValueError, match="the first dimension of the weight parameters"):
         _check_number_of_layers(inpt)
Ejemplo n.º 5
0
 def test_check_num_layers(self, inpt, repeat):
     """Tests that layer check returns correct number of layers."""
     n_layers = _check_number_of_layers(inpt)
     assert n_layers == repeat
Ejemplo n.º 6
0
def QAOAEmbedding(features, weights, wires, local_field='Y'):
    r"""
    Encodes :math:`N` features into :math:`n>N` qubits, using a layered, trainable quantum
    circuit that is inspired by the QAOA ansatz.

    A single layer applies two circuits or "Hamiltonians": The first encodes the features, and the second is
    a variational ansatz inspired by a 1-dimensional Ising model. The feature-encoding circuit associates features with
    the angles of :class:`RX` rotations. The Ising ansatz consists of trainable two-qubit ZZ interactions
    :math:`e^{-i \alpha \sigma_z \otimes \sigma_z}`,
    and trainable local fields :math:`e^{-i \frac{\beta}{2} \sigma_{\mu}}`, where :math:`\sigma_{\mu}`
    can be chosen to be :math:`\sigma_{x}`, :math:`\sigma_{y}` or :math:`\sigma_{z}`
    (default choice is :math:`\sigma_{y}` or the ``RY`` gate), and :math:`\alpha, \beta` are adjustable gate parameters.

    The number of features has to be smaller or equal to the number of qubits. If there are fewer features than
    qubits, the feature-encoding rotation is replaced by a Hadamard gate.

    The argument ``weights`` contains an array of the :math:`\alpha, \beta` parameters for each layer.
    The number of layers :math:`L` is derived from the first dimension of ``weights``, which has the following
    shape:

    * :math:`(L, )`, if the embedding acts on a single wire,
    * :math:`(L, 3)`, if the embedding acts on two wires,
    * :math:`(L, 2n)` else.

    After the :math:`L` th layer, another set of feature-encoding :class:`RX` gates is applied.

    This is an example for the full embedding circuit using 2 layers, 3 features, 4 wires, and ``RY`` local fields:

    |

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

    |

    .. note::
        ``QAOAEmbedding`` supports gradient computations with respect to both the ``features`` and the ``weights``
        arguments. Note that trainable parameters need to be passed to the quantum node as positional arguments.

    Args:
        features (array): array of features to encode
        weights (array): array of weights
        wires (Sequence[int] or int): `n` qubit indices that the template acts on
        local_field (str): type of local field used, one of ``'X'``, ``'Y'``, or ``'Z'``

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

    .. UsageDetails::

        The QAOA embedding encodes an :math:`n`-dimensional feature vector into at most :math:`n` qubits. The
        embedding applies layers of a circuit, and each layer is defined by a set of weight parameters.

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import QAOAEmbedding

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

            @qml.qnode(dev)
            def circuit(weights, f=None):
                QAOAEmbedding(features=f, weights=weights, wires=range(2))
                return qml.expval(qml.PauliZ(0))

            features = [1., 2.]
            layer1 = [0.1, -0.3, 1.5]
            layer2 = [3.1, 0.2, -2.8]
            weights = [layer1, layer2]

            print(circuit(weights, f=features))

        **Using parameter initialization functions**

        The initial weight parameters can alternatively be generated by utility functions from the
        ``pennylane.init`` module, for example using the function :func:`~.qaoa_embedding_normal`:

        .. code-block:: python

            from pennylane.init import qaoa_embedding_normal
            weights = qaoa_embedding_normal(n_layers=2, n_wires=2, mean=0, std=0.2)


        **Training the embedding**

        The embedding is typically trained with respect to a given cost. For example, one can train it to
        minimize the PauliZ expectation of the first qubit:

        .. code-block:: python

            o = GradientDescentOptimizer()
            for i in range(10):
                weights = o.step(lambda w : circuit(w, f=features), weights)
                print("Step ", i, " weights = ", weights)


        **Training the features**

        In principle, also the features are trainable, which means that gradients with respect to feature values
        can be computed. To train both weights and features, they need to be passed to the qnode as
        positional arguments. If the built-in optimizer is used, they have to be merged to one input:

        .. code-block:: python

            @qml.qnode(dev)
            def circuit2(pars):
                weights = pars[0]
                features = pars[1]
                QAOAEmbedding(features=features, weights=weights, wires=range(2))
                return qml.expval(qml.PauliZ(0))


            features = [1., 2.]
            weights = [[0.1, -0.3, 1.5], [3.1, 0.2, -2.8]]
            pars = [weights, features]

            o = GradientDescentOptimizer()
            for i in range(10):
                pars = o.step(circuit2, pars)
                print("Step ", i, " weights = ", pars[0], " features = ", pars[1])

        **Local Fields**

        While by default, ``RY`` gates are used as local fields, one may also choose ``local_field='Z'`` or
        ``local_field='X'`` as hyperparameters of the embedding.

        .. code-block:: python

            @qml.qnode(dev)
            def circuit(weights, f=None):
                QAOAEmbedding(features=f, weights=weights, wires=range(2), local_field='Z')
                return qml.expval(qml.PauliZ(0))

        Choosing ``'Z'`` fields implements a QAOAEmbedding where the second Hamiltonian is a
        1-dimensional Ising model.

    """
    #############
    # Input checks

    wires = _check_wires(wires)

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

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

    repeat = _check_number_of_layers([weights])

    if len(wires) == 1:
        expected_shape = (repeat, 1)
        _check_shape(weights,
                     expected_shape,
                     msg="'weights' must be of shape {}; got {}"
                     "".format(expected_shape, _get_shape(features)))
    elif len(wires) == 2:
        expected_shape = (repeat, 3)
        _check_shape(weights,
                     expected_shape,
                     msg="'weights' must be of shape {}; got {}"
                     "".format(expected_shape, _get_shape(features)))
    else:
        expected_shape = (repeat, 2 * len(wires))
        _check_shape(weights,
                     expected_shape,
                     msg="'weights' must be of shape {}; got {}"
                     "".format(expected_shape, _get_shape(features)))

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

    n_features = _get_shape(features)[0]

    if local_field == 'Z':
        local_fields = RZ
    elif local_field == 'X':
        local_fields = RX
    else:
        local_fields = RY

    for l in range(repeat):
        # apply alternating Hamiltonians
        qaoa_feature_encoding_hamiltonian(features, n_features, wires)
        qaoa_ising_hamiltonian(weights, wires, local_fields, l)

    # repeat the feature encoding once more at the end
    qaoa_feature_encoding_hamiltonian(features, n_features, wires)