How to embed data into a quantum state

Isidor Schoch (Xanadu resident)

Be it measurement data, images or coordinates in phase-space, almost all forms of data we encounter in day-to-day life are classical. So does it make sense for quantum computers to deal with classical data? The short answer is yes! In this how-to, we will outline the first few steps of how to encode classical data into a quantum state. You will then be able to use this state to perform quantum computations.

Different embedding types

To encode your classical data into a quantum state, you first have to find out what type of classical data you have. We will distinguish between three different types of data in \(N\)-dimensions.

  1. Discrete data, represented as binary \(\mathbf{b} \in \{0, 1\}^{N}\) or integer values \(\mathbf{k} \in \mathbb{Z}^{N}\).
  2. Real continuous data, represented as floating-point values \(\mathbf{x} \in \mathbb{R}^{N}\).
  3. Complex continuous data, represented as complex values \(\boldsymbol{\alpha} \in \mathbb{C}^{2^{N}}\).

Keeping the subset relations \(\{0, 1\}\subset\mathbb{Z}\subset\mathbb{R}\subset\mathbb{C}\) in mind, one could always choose to interpret the data in the domain \(\mathcal{D}\) to be in the superset \(\mathcal{D}^{\prime}\supset \mathcal{D}\).

1. Discrete data, represented as binary or integer values

A suitable encoding for binary data is the so-called BasisEmbedding. The BasisEmbedding class interprets a binary string as a qubit basis state with the following mapping:

$$ \mathbf{b}=(b_{0}, \ldots, b_{N-1}) \mapsto |b_{0}, \ldots, b_{N-1}\rangle. $$

See below for a simple example of the BasisEmbedding used to initialize three qubits.

import pennylane as qml

N = 3
wires = range(N)
dev = qml.device("default.qubit", wires)

def circuit(b):
    qml.BasisEmbedding(b, wires)
    return qml.state()
>>> circuit([1, 1, 1])
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j]

As expected, the result corresponds to the state \(\lvert 111 \rangle = \lvert 1 \rangle \otimes \lvert 1 \rangle \otimes \lvert 1 \rangle\). Representing the \(\lvert 1 \rangle\) state as the second standard basis vector and the tensor product as the Kronecker product, we can confirm the result with a quick calculation.

$$ \lvert 1 \rangle \otimes \lvert 1 \rangle \otimes \lvert 1 \rangle = \begin{bmatrix}0 \\ 1\end{bmatrix} \otimes \begin{bmatrix} 0 \\ 1\end{bmatrix} \otimes \begin{bmatrix} 0 \\ 1\end{bmatrix} = \begin{bmatrix}0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\end{bmatrix}^{\top} $$

You can also just pass an integer value to the basis embedding function and it will automatically convert it into its binary representation. We can perform a quick sanity check of this functionality by running

>>> print(circuit(7))
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j]

which is the same state vector we saw before. Unsurprisingly, the binary label corresponding to this state vector is also consistent with the binary representation of the integer seven.

2. Continuous data, represented as floating-point values

The simplest type of encoding for floating-point data is called AngleEmbedding. This type of embedding encodes a single floating-point value \(x\in \mathbb{R}\) into a quantum state with the mapping

$$ x \mapsto R_{k}(x)|0\rangle = e^{-i x\sigma_{k}/2}|0\rangle, $$

where \(k\in\{x, y, z\}\) is the axis of rotation in the Bloch sphere. The default axis of rotation is set to \(k=x\) in the AngleEmbedding class. You may also choose to set it to \(k=y\), but make sure to avoid \(k=z\). The latter case is not useful because every \(x\) will be mapped to the \(|0\rangle\) state; the encoded value will be lost. Note that you can also input a tensor-like object and encode each component as a qubit. Examine the code snippet below to see how to encode a classical floating-point value as a quantum state!

import pennylane as qml
from pennylane import numpy as np

N = 3
wires = range(3)
dev = qml.device("default.qubit", wires)

def circuit(val_list):
    qml.AngleEmbedding(val_list, wires)
    return [qml.expval(qml.PauliZ(w)) for w in wires]
>>> circuit([0.0, np.pi / 2, np.pi])
tensor([ 1.0,  0.0, -1.0], requires_grad=True)

Keep in mind that Pauli rotations are \(2\pi\)-periodic up to a global phase, meaning that you should normalize your data to be in \(\Omega:=[0, \pi)\subset \mathbb{R}\) if possible. This can be helpful in order to avoid encoding two different values as the same quantum state. While the AngleEmbedding allows you to encode a lot of information in a single qubit, this comes at the cost of a difficult construction process.

3. Continuous data, represented as complex values

The last type of embedding we want to look at is the AmplitudeEmbedding. As the name suggests, an array of values can be used as the amplitudes of the state with the mapping

$$ \boldsymbol{\alpha}=(\alpha_0, \ldots, \alpha_{2^N-1})\mapsto \sum_{k=0}^{2^N-1}\alpha_{k}|k\rangle $$

and can be implemented with the following code.

import pennylane as qml

N = 3
wires = range(N)
dev = qml.device("default.qubit", wires)

def circuit(features):
    qml.AmplitudeEmbedding(features, wires)
    return qml.state()
>>> circuit([0.625, 0.0, 0.0, 0.0, 0.625j, 0.375, 0.25, 0.125])
tensor([0.625+0.j   , 0.   +0.j   , 0.   +0.j   , 0.   +0.j   ,
        0.   +0.625j, 0.375+0.j   , 0.25 +0.j   , 0.125+0.j   ], requires_grad=True)

Here, the values were chosen to be normalized, i.e. \(\lVert\boldsymbol{\alpha}\rVert=1\). Note that one can use unnormalized data by setting the normalize parameter of the AmplitudeEmbedding class to True.


More advanced embeddings also exist as templated implementations in PennyLane. Besides taking advantage of quantum superposition as we have seen so far, they also take advantage of quantum entanglement. Two such templated embeddings in PennyLane are the QAOAEmbedding and IQPEmbedding. For a complete view of possible embeddings, you can visit the templates documentation. The BasisEmbedding and AmplitudeEmbedding are also covered in the Key Concepts section of the PennyLane website.

Isidor Schoch

Isidor Schoch

Isidor is a Quantum Engineering MSc student at ETH Zurich. He is passionate about exploring the connections between mathematics, physics and computer science. Besides his studies, he currently also works in the PennyLane performance team as a summer resident.