QAOAEmbedding(features, weights, wires, local_field='Y')[source]

Encodes \(N\) features into \(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 RX rotations. The Ising ansatz consists of trainable two-qubit ZZ interactions \(e^{-i \frac{\alpha}{2} \sigma_z \otimes \sigma_z}\) (in PennyLane represented by the MultiRZ gate), and trainable local fields \(e^{-i \frac{\beta}{2} \sigma_{\mu}}\), where \(\sigma_{\mu}\) can be chosen to be \(\sigma_{x}\), \(\sigma_{y}\) or \(\sigma_{z}\) (default choice is \(\sigma_{y}\) or the RY gate), and \(\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 \(\alpha, \beta\) parameters for each layer. The number of layers \(L\) is derived from the first dimension of weights, which has the following shape:

  • \((L, 1)\), if the embedding acts on a single wire,

  • \((L, 3)\), if the embedding acts on two wires,

  • \((L, 2n)\) else.

After the \(L\) th layer, another set of feature-encoding RX gates is applied.

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



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.

  • features (tensor_like) – array of features to encode

  • weights (tensor_like) – array of weights

  • wires (Iterable or Wires) – Wires that the template acts on. Accepts an iterable of numbers or strings, or a Wires object.

  • local_field (str) – type of local field used, one of 'X', 'Y', or 'Z'


ValueError – if inputs do not have the correct format

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

import pennylane as qml
from pennylane.templates import QAOAEmbedding

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

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 qaoa_embedding_normal():

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:

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:

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.

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.