qml.fourier.circuit_spectrum¶
- circuit_spectrum(tape, encoding_gates=None, decimals=8)[source]¶
Compute the frequency spectrum of the Fourier representation of simple quantum circuits ignoring classical preprocessing.
The circuit must only use simple single-parameter gates of the form \(e^{-i x_j G}\) as input-encoding gates, which allows the computation of the spectrum by inspecting the gates’ generators \(G\). The most important example of such gates are Pauli rotations.
Note
More precisely, the
circuit_spectrum
function relies on the gate to define agenerator
, and will fail if gates marked as inputs do not have this attribute.Gates are marked as input-encoding gates in the quantum function by giving them an
id
. If two gates have the sameid
, they are considered to be used to encode the same input \(x_j\). Theencoding_gates
argument can be used to indicate that only gates with a specificid
should be interpreted as input-encoding gates. Otherwise, all gates with an explicitid
are considered to be input-encoding gates.Note
If no input-encoding gates are found, an empty dictionary is returned.
- Parameters
tape (QNode or QuantumTape or Callable) – a quantum circuit in which input-encoding gates are marked by their
id
attributeencoding_gates (list[str]) – list of input-encoding gate
id
strings for which to compute the frequency spectradecimals (int) – number of decimals to which to round frequencies.
- Returns
The transformed circuit as described in
qml.transform
. Executing this circuit will return a dictionary with the input-encoding gateid
as keys and their frequency spectra as values.- Return type
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
Details
A circuit that returns an expectation value which depends on \(N\) scalar inputs \(x_j\) can be interpreted as a function \(f: \mathbb{R}^N \rightarrow \mathbb{R}\). This function can always be expressed by a Fourier-type sum
\[\sum \limits_{\omega_1\in \Omega_1} \dots \sum \limits_{\omega_N \in \Omega_N} c_{\omega_1,\dots, \omega_N} e^{-i x_1 \omega_1} \dots e^{-i x_N \omega_N}\]over the frequency spectra \(\Omega_j \subseteq \mathbb{R},\) \(j=1,\dots,N\). Each spectrum has the property that \(0 \in \Omega_j\), and the spectrum is symmetric (for every \(\omega \in \Omega_j\) we have that \(-\omega \in \Omega_j\)). If all frequencies are integer-valued, the Fourier sum becomes a Fourier series.
As shown in Vidal and Theis (2019) and Schuld, Sweke and Meyer (2020), if an input \(x_j, j = 1 \dots N\), only enters into single-parameter gates of the form \(e^{-i x_j G}\) (where \(G\) is a Hermitian generator), the frequency spectrum \(\Omega_j\) is fully determined by the eigenvalues of \(G\). In many situations, the spectra are limited to a few frequencies only, which in turn limits the function class that the circuit can express.
The
circuit_spectrum
function computes all frequencies that will potentially appear in the sets \(\Omega_1\) to \(\Omega_N\).Example
Consider the following example, which uses non-trainable inputs
x
and trainable parametersw
as arguments to the qnode.import pennylane as qml import numpy as np n_layers = 2 n_qubits = 3 dev = qml.device("default.qubit", wires=n_qubits) @qml.qnode(dev) def circuit(x, w): for l in range(n_layers): for i in range(n_qubits): qml.RX(x[i], wires=i, id="x"+str(i)) qml.Rot(w[l,i,0], w[l,i,1], w[l,i,2], wires=i) qml.RZ(x[0], wires=0, id="x0") return qml.expval(qml.Z(0)) x = np.array([1, 2, 3]) w = np.random.random((n_layers, n_qubits, 3)) res = qml.fourier.circuit_spectrum(circuit)(x, w)
>>> print(qml.draw(circuit)(x, w)) 0: ──RX(1.00)──Rot(0.53,0.70,0.90)──RX(1.00)──Rot(0.81,0.38,0.43)──RZ(1.00)─┤ <Z> 1: ──RX(2.00)──Rot(0.56,0.61,0.96)──RX(2.00)──Rot(0.32,0.49,0.77)───────────┤ 2: ──RX(3.00)──Rot(0.11,0.63,0.31)──RX(3.00)──Rot(0.52,0.46,0.83)───────────┤
>>> for inp, freqs in res.items(): >>> print(f"{inp}: {freqs}") 'x0': [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0] 'x1': [-2.0, -1.0, 0.0, 1.0, 2.0] 'x2': [-2.0, -1.0, 0.0, 1.0, 2.0]
Note
While the Fourier spectrum usually does not depend on trainable circuit parameters or the actual values of the inputs, it may still change based on inputs to the QNode that alter the architecture of the circuit.
The input-encoding gates to consider can also be explicitly selected by using the
encoding_gates
keyword argument:dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def circuit(x): qml.RX(x[0], wires=0, id="x0") qml.PhaseShift(x[0], wires=0, id="x0") qml.RX(x[1], wires=0, id="x1") return qml.expval(qml.Z(0)) x = np.array([1, 2]) res = qml.fourier.circuit_spectrum(circuit, encoding_gates=["x0"])(x)
>>> for inp, freqs in res.items(): >>> print(f"{inp}: {freqs}") 'x0': [-2.0, -1.0, 0.0, 1.0, 2.0]
Note
The
circuit_spectrum
function does not check if the result of the circuit is an expectation, or if gates with the sameid
take the same value in a given call of the function.The
circuit_spectrum
function works in all interfaces:import tensorflow as tf dev = qml.device("default.qubit", wires=1) @qml.qnode(dev, interface='tf') def circuit(x): qml.RX(x[0], wires=0, id="x0") qml.PhaseShift(x[1], wires=0, id="x1") return qml.expval(qml.Z(0)) x = tf.constant([1, 2]) res = qml.fourier.circuit_spectrum(circuit)(x)
>>> for inp, freqs in res.items(): >>> print(f"{inp}: {freqs}") 'x0': [-1.0, 0.0, 1.0] 'x1': [-1.0, 0.0, 1.0]