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)
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)
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)
def expand(self): with qml.tape.QuantumTape() as tape: QubitStateVector(self.parameters[0], wires=self.wires) return tape
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)
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)
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)