예제 #1
0
def AmplitudeEmbedding(features, wires):
    r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.

    The absolute square of all elements in ``features`` has to add up to one.

    .. note::

        AmplitudeEmbedding uses PennyLane's :class:`~pennylane.ops.QubitStateVector` and only works in conjunction with
        devices that implement this function.

    Args:
        features (array): Input array of shape ``(2**n,)``
        wires (Sequence[int]): sequence of qubit indices that the template acts on
    """

    if not isinstance(wires, Iterable):
        raise ValueError(
            "Wires needs to be a list of wires that the embedding uses; got {}."
            .format(wires))

    if 2**len(wires) != len(features):
        raise ValueError(
            "AmplitudeEmbedding requires a feature vector of size 2**len(wires), which is {}; "
            "got {}.".format(2**len(wires), len(features)))

    QubitStateVector(features, wires=wires)
예제 #2
0
def AmplitudeEmbedding(features, wires, pad=False, normalize=False):
    r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.

    If the total number of features to embed are less than the :math:`2^n` available amplitudes,
    non-informative constants (zeros) can be padded to ``features``. To enable this, the argument
    ``pad`` should be set to ``True``.

    The L2-norm of ``features`` must be one. By default, AmplitudeEmbedding expects a normalized
    feature vector. The argument ``normalize`` can be set to ``True`` to automatically normalize it.

    .. note::

        AmplitudeEmbedding uses PennyLane's :class:`~pennylane.ops.QubitStateVector` and only works in conjunction with
        devices that implement this function.

    Args:
        features (array): input array of shape ``(2**n,)``
        wires (Sequence[int]): sequence of qubit indices that the template acts on
        pad (Boolean): controls the activation of the padding option
        normalize (Boolean): controls the activation of automatic normalization
    """

    if not isinstance(wires, Iterable):
        raise ValueError(
            "Wires needs to be a list of wires that the embedding uses; got {}."
            .format(wires))

    n_features = len(features)
    n_amplitudes = 2**len(wires)

    if n_amplitudes < n_features:
        raise ValueError(
            "AmplitudeEmbedding requires the size of feature vector to be smaller than or equal to 2**len(wires), which is {}; "
            "got {}.".format(n_amplitudes, n_features))

    if pad and n_amplitudes >= n_features:
        features = np.pad(features, (0, n_amplitudes - n_features), 'constant')

    if not pad and n_amplitudes != n_features:
        raise ValueError(
            "AmplitudeEmbedding with no padding requires a feature vector of size 2**len(wires), which is {}; "
            "got {}.".format(n_amplitudes, n_features))

    if normalize and np.linalg.norm(features, 2) != 1:
        features = features * (1 / np.linalg.norm(features, 2))

    if not normalize and np.linalg.norm(features, 2) != 1:
        raise ValueError(
            "AmplitudeEmbedding requires a normalized feature vector. The argument normalize can be set to True to automatically normalize it."
        )

    QubitStateVector(features, wires=wires)
예제 #3
0
def AmplitudeEmbedding(features, wires, pad=None, normalize=False):
    r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.

    By setting ``pad`` to a real or complex number, ``features`` is automatically padded to dimension
    :math:`2^n` where :math:`n` is the number of qubits used in the embedding.

    To represent a valid quantum state vector, the L2-norm of ``features`` must be one.
    The argument ``normalize`` can be set to ``True`` to automatically normalize the features.

    If both automatic padding and normalization are used, padding is executed *before* normalizing.

    .. note::

        On some devices, ``AmplitudeEmbedding`` must be the first operation of a quantum node.


    .. note::

        ``AmplitudeEmbedding`` 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.

    .. warning::

        ``AmplitudeEmbedding`` 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 argument cannot be computed by PennyLane.

    Args:
        features (array): input array of shape ``(2^n,)``
        wires (Sequence[int] or int): :math:`n` qubit indices that the template acts on
        pad (float or complex): if not None, the input is padded with this constant to size :math:`2^n`
        normalize (Boolean): controls the activation of automatic normalization

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

    .. UsageDetails::

        Amplitude embedding encodes a normalized :math:`2^n`-dimensional feature vector into the state
        of :math:`n` qubits:

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import AmplitudeEmbedding

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

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

            circuit(f=[1/2, 1/2, 1/2, 1/2])

        Checking the final state of the device, we find that it is equivalent to the input passed to the circuit:

        >>> dev._state
        [0.5+0.j 0.5+0.j 0.5+0.j 0.5+0.j]

        **Passing features as positional arguments to a quantum node**

        The ``features`` argument of ``AmplitudeEmbedding`` can in principle also be passed to the quantum node
        as a positional argument:

        .. code-block:: python

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

        However, due to non-trivial classical processing to construct the state preparation circuit,
        the features argument is **not differentiable**.

        >>> g = qml.grad(circuit, argnum=0)
        >>> g([1,1,1,1])
        ValueError: Cannot differentiate wrt parameter(s) {0, 1, 2, 3}.


        **Normalization**

        The template will raise an error if the feature input is not normalized.
        One can set ``normalize=True`` to automatically normalize it:

        .. code-block:: python

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

            circuit(f=[15, 15, 15, 15])

        The re-normalized feature vector is encoded into the quantum state vector:

        >>> dev._state
        [0.5 + 0.j, 0.5 + 0.j, 0.5 + 0.j, 0.5 + 0.j]

        **Padding**

        If the dimension of the feature vector is smaller than the number of amplitudes,
        one can automatically pad it with a constant for the missing dimensions using the ``pad`` option:

        .. code-block:: python

            from math import sqrt

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

            circuit(f=[1/sqrt(2), 1/sqrt(2)])

        >>> dev._state
        [0.70710678 + 0.j, 0.70710678 + 0.j, 0.0 + 0.j, 0.0 + 0.j]

        **Operations before the embedding**

        On some devices, ``AmplitudeEmbedding`` must be the first operation in the quantum node.
        For example, ``'default.qubit'`` complains when running the following circuit:

        .. code-block:: python

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

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


        >>> circuit(f=[1/2, 1/2, 1/2, 1/2])
        pennylane._device.DeviceError: Operation QubitStateVector cannot be used
        after other Operations have already been applied on a default.qubit device.

    """

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

    _check_no_variable(pad, msg="'pad' cannot be differentiable")
    _check_no_variable(normalize, msg="'normalize' cannot be differentiable")

    wires = _check_wires(wires)

    n_amplitudes = 2**len(wires)
    expected_shape = (n_amplitudes,)
    if pad is None:
        shape = _check_shape(features, expected_shape, msg="'features' must be of shape {}; got {}. Use the 'pad' "
                                                           "argument for automated padding."
                                                           "".format(expected_shape, _get_shape(features)))
    else:
        shape = _check_shape(features, expected_shape, bound='max', msg="'features' must be of shape {} or smaller "
                                                                      "to be padded; got {}"
                                                                      "".format(expected_shape, _get_shape(features)))

    _check_type(pad, [float, complex, type(None)], msg="'pad' must be a float or complex; got {}".format(pad))
    _check_type(normalize, [bool], msg="'normalize' must be a boolean; got {}".format(normalize))

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

    #############
    # Preprocessing

    # pad
    n_features = shape[0]
    if pad is not None and n_amplitudes > n_features:
        features = np.pad(features, (0, n_amplitudes-n_features), mode='constant', constant_values=pad)

    # normalize
    if isinstance(features[0], Variable):
        feature_values = [s.val for s in features]
        norm = np.sum(np.abs(feature_values)**2)
    else:
        norm = np.sum(np.abs(features)**2)

    if not np.isclose(norm, 1.0, atol=TOLERANCE, rtol=0):
        if normalize or pad:
            features = features/np.sqrt(norm)
        else:
            raise ValueError("'features' must be a vector of length 1.0; got length {}."
                             "Use 'normalization=True' to automatically normalize.".format(norm))

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

    features = np.array(features)
    QubitStateVector(features, wires=wires)
예제 #4
0
    def expand(self):

        with qml.tape.QuantumTape() as tape:
            QubitStateVector(self.parameters[0], wires=self.wires)

        return tape
예제 #5
0
def AmplitudeEmbedding(features, wires, pad=False, normalize=False):
    r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.

    If the total number of features to embed are less than the :math:`2^n` available amplitudes,
    non-informative constants (zeros) can be padded to ``features``. To enable this, the argument
    ``pad`` should be set to ``True``.

    The L2-norm of ``features`` must be one. By default, AmplitudeEmbedding expects a normalized
    feature vector. The argument ``normalize`` can be set to ``True`` to automatically normalize it.

    .. note::

        AmplitudeEmbedding uses PennyLane's :class:`~pennylane.ops.QubitStateVector` and only works in conjunction with
        devices that implement this function.

    Args:
        features (array): input array of shape ``(2**n,)``
        wires (Sequence[int]): sequence of qubit indices that the template acts on
        pad (Boolean): controls the activation of the padding option
        normalize (Boolean): controls the activation of automatic normalization

    Raises:
        ValueError: if ``features`` or ``wires`` is invalid
    """

    if isinstance(wires, int):
        wires = [wires]

    features = np.array(features)

    n_features = len(features)
    n_amplitudes = 2**len(wires)

    if n_amplitudes < n_features:
        raise ValueError(
            "AmplitudeEmbedding requires the size of feature vector to be "
            "smaller than or equal to 2**len(wires), which is {}; "
            "got {}.".format(n_amplitudes, n_features))

    if pad and n_amplitudes >= n_features:
        features = np.pad(features, (0, n_amplitudes - n_features), 'constant')

    if not pad and n_amplitudes != n_features:
        raise ValueError(
            "AmplitudeEmbedding must get a feature vector of size 2**len(wires), "
            "which is {}; got {}. Use ``pad=True`` to automatically pad the "
            "features with zeros.".format(n_amplitudes, n_features))

    # Get normalization
    norm = 0
    for f in features:
        if isinstance(f, Variable):
            norm += np.conj(f.val) * f.val
        else:
            norm += np.conj(f) * f

    if not np.isclose(norm, 1):
        if normalize:
            features = features / np.sqrt(norm)
        else:
            raise ValueError(
                "AmplitudeEmbedding requires a normalized feature vector. "
                "Set ``normalize=True`` to automatically normalize it.")

    QubitStateVector(features, wires=wires)
예제 #6
0
def AmplitudeEmbedding(features, wires, pad=None, normalize=False):
    r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.

    If the total number of features to embed is less than the :math:`2^n` available amplitudes,
    non-informative constants (zeros) can be padded to ``features``. To enable this, the argument
    ``pad`` should be set to ``True``.

    The L2-norm of ``features`` must be one. By default, ``AmplitudeEmbedding`` expects a normalized
    feature vector. The argument ``normalize`` can be set to ``True`` to automatically normalize it.

    .. warning::

        ``AmplitudeEmbedding`` 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 argument cannot be computed by PennyLane.

    Args:
        features (array): input array of shape ``(2^n,)``
        wires (Sequence[int] or int): qubit indices that the template acts on
        pad (float or complex): if not None, the input is padded with this constant to size :math:`2^n`
        normalize (Boolean): controls the activation of automatic normalization

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

    #############
    # Input checks
    _check_no_variable([pad, normalize], ['pad', 'normalize'])
    wires, n_wires = _check_wires(wires)

    n_ampl = 2**n_wires
    if pad is None:
        msg = "AmplitudeEmbedding must get a feature vector of size 2**len(wires), which is {}. Use 'pad' " \
               "argument for automated padding.".format(n_ampl)
        shp = _check_shape(features, (n_ampl,), msg=msg)
    else:
        msg = "AmplitudeEmbedding must get a feature vector of at least size 2**len(wires) = {}.".format(n_ampl)
        shp = _check_shape(features, (n_ampl,), msg=msg, bound='max')

    _check_type(pad, [float, complex, type(None)])
    _check_type(normalize, [bool])
    ###############

    # Pad
    n_feats = shp[0]
    if pad is not None and n_ampl > n_feats:
        features = np.pad(features, (0, n_ampl-n_feats), mode='constant', constant_values=pad)

    # Normalize
    if isinstance(features[0], Variable):
        feature_values = [s.val for s in features]
        norm = np.sum(np.abs(feature_values)**2)
    else:
        norm = np.sum(np.abs(features)**2)

    if not np.isclose(norm, 1.0, atol=TOLERANCE, rtol=0):
        if normalize or pad:
            features = features/np.sqrt(norm)
        else:
            raise ValueError("Vector of features has to be normalized to 1.0, got {}."
                             "Use 'normalization=True' to automatically normalize.".format(norm))

    features = np.array(features)
    QubitStateVector(features, wires=wires)
예제 #7
0
def AmplitudeEmbedding(features,
                       wires,
                       pad_with=None,
                       normalize=False,
                       pad=None):
    r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.

    By setting ``pad_with`` to a real or complex number, ``features`` is automatically padded to dimension
    :math:`2^n` where :math:`n` is the number of qubits used in the embedding.

    To represent a valid quantum state vector, the L2-norm of ``features`` must be one.
    The argument ``normalize`` can be set to ``True`` to automatically normalize the features.

    If both automatic padding and normalization are used, padding is executed *before* normalizing.

    .. note::

        On some devices, ``AmplitudeEmbedding`` must be the first operation of a quantum circuit.

    .. warning::

        At the moment, the ``features`` argument is **not differentiable** when using the template, and
        gradients with respect to the features cannot be computed by PennyLane.

    Args:
        features (tensor-like): input vector of length ``2^n``, or less if `pad_with` is specified
        wires (Iterable or :class:`.wires.Wires`): Wires that the template acts on.
            Accepts an iterable of numbers or strings, or a Wires object.
        pad_with (float or complex): if not None, the input is padded with this constant to size :math:`2^n`
        normalize (bool): whether to automatically normalize the features
        pad (float or complex): same as `pad`, to be deprecated

    Example:

        Amplitude embedding encodes a normalized :math:`2^n`-dimensional feature vector into the state
        of :math:`n` qubits:

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import AmplitudeEmbedding

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

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

            circuit(f=[1/2, 1/2, 1/2, 1/2])

        The final state of the device is - up to a global phase - equivalent to the input passed to the circuit:

        >>> dev.state
        [0.5+0.j 0.5+0.j 0.5+0.j 0.5+0.j]

        **Differentiating with respect to the features**

        Due to non-trivial classical processing to construct the state preparation circuit,
        the features argument is **not always differentiable**.

        .. code-block:: python

            from pennylane import numpy as np

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

        >>> g = qml.grad(circuit, argnum=0)
        >>> f = np.array([1, 1, 1, 1], requires_grad=True)
        >>> g(f)
        ValueError: Cannot differentiate wrt parameter(s) {0, 1, 2, 3}.

        **Normalization**

        The template will raise an error if the feature input is not normalized.
        One can set ``normalize=True`` to automatically normalize it:

        .. code-block:: python

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

            circuit(f=[15, 15, 15, 15])

        >>> dev.state
        [0.5 + 0.j, 0.5 + 0.j, 0.5 + 0.j, 0.5 + 0.j]

        **Padding**

        If the dimension of the feature vector is smaller than the number of amplitudes,
        one can automatically pad it with a constant for the missing dimensions using the ``pad_with`` option:

        .. code-block:: python

            from math import sqrt

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

            circuit(f=[1/sqrt(2), 1/sqrt(2)])

        >>> dev.state
        [0.70710678 + 0.j, 0.70710678 + 0.j, 0.0 + 0.j, 0.0 + 0.j]

        **Operations before the embedding**

        On some devices, ``AmplitudeEmbedding`` must be the first operation in the quantum node.
        For example, ``'default.qubit'`` complains when running the following circuit:

        .. code-block:: python

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

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


        >>> circuit(f=[1/2, 1/2, 1/2, 1/2])
        pennylane._device.DeviceError: Operation QubitStateVector cannot be used
        after other Operations have already been applied on a default.qubit device.

    """

    wires = Wires(wires)

    # pad is replaced with the more verbose pad_with
    if pad is not None:
        warnings.warn(
            "The pad argument will be replaced by the pad_with option in future versions of PennyLane.",
            PendingDeprecationWarning,
        )
        if pad_with is None:
            pad_with = pad

    features = _preprocess(features, wires, pad_with, normalize)

    QubitStateVector(features, wires=wires)