State preparation and QRAM circuits
PyAQASM comes with a KP-tree implementation that generates real states using RY multiplexors.
The implementation is located in qat.lang.models.KPTree
. It implements the Kerenidis-Prakash tree structure to describe an arbitrary tensor (see [KP17] for more details).
This class can be used to either prepare an arbitrary (real) state or to perform calls to a QRAM (with arbitrary dimension, see below).
It is constructed using a numpy array of arbitrary shape \((k_1, ..., k_n)\). If the \(k_i\) are not power of two, the array will be padded with \(0\). The resulting tree will generate circuits over \(\sum_i \log_2{k'_i}\) qubits where \(k'_i\) are the padded dimensions. This information can be retrieved in the .qubits attribute of the object.
import numpy as np
from qat.lang.models import KPTree
data = np.random.random((2, 4, 8))
tree = KPTree(data)
print(tree.qubits)
[1, 2, 3]
Once a KPTree
is built, the object can be used to generate QRoutine
objects that perform either a QRAM load or a full state preparation.
An input data \(\alpha\) (once normalized) with \(k\) axis is understood as the following quantum state:
The same piece of data \(\alpha\) can also be understood as an adressable QRAM. In that case, we assume that some of the first \(l\) registers already carry a non trivial quantum state of shape:
Then one can “load” the remaining \(k-l\) registers thus producing the following quantum state:
where \(|D_{i_1 ... i_l}\rangle = \sum_{i_{l+1},...,i_k}\alpha_{i_1 ... i_k}|i_{l+1}\rangle...|i_k\rangle\)
The KPTree
class supports all type of QRAM load of this shape. These are accessible through the get_routine()
method. This method takes a single optional argument specifying the number of axis to use as address (i.e \(l\) in the example above).
import numpy as np
from qat.lang.models import KPTree
data = np.random.random((2, 4, 8))
tree = KPTree(data)
# This routine prepares a state corresponding to flatten(data)
rout_0 = tree.get_routine(0)
# This one uses the first qubit as an address
rout_1 = tree.get_routine(1)
# This one uses the first 3 qubits as address (i.e the first 2 axis)
rout_2 = tree.get_routine(2)
- class qat.lang.models.KPTree(data)
A class representing a Kerinidis-Prakash tree for a QRAM.
This class can also be used to prepare (classical) distributions.
- Parameters
data (numpy.ndarray) – a numpy array of arbitrary shape
Example of usage:
from qat.lang.models import KPTree from qat.lang.AQASM import * import numpy as np data = np.random.random(size=(2, 4)) tree = KPTree(data) # prepares a state proportional to the data routine_state_prep = tree.get_routine(0) # prepare either data[0] or data[1] depending on the state of the first qubit routine_1 = tree.get_routine(1) # The resulting routine can be used as a gate over the appropriate number of qubits (here 3) prog = Program() qbits = prog.qalloc(3) routine_state_prep(qbits) circuit = prog.to_circ() print("Full state preparation:") for op in circuit.iterate_simple(): print(op) prog = Program() qbits = prog.qalloc(3) H(qbits[0]) routine_1(qbits) circuit = prog.to_circ() print("QRAM call with 1 qubit address") for op in circuit.iterate_simple(): print(op)
Full state preparation: ('RY', [1.9599481358565838], [0]) ('RY', [1.164734201894148], [1]) ('CNOT', [], [0, 1]) ('RY', [-0.24062886139716544], [1]) ('CNOT', [], [0, 1]) ('RY', [1.410368857987665], [2]) ('CNOT', [], [1, 2]) ('RY', [0.3567354116708534], [2]) ('CNOT', [], [0, 2]) ('RY', [-0.18847912247571447], [2]) ('CNOT', [], [1, 2]) ('RY', [0.7177579394158706], [2]) ('CNOT', [], [0, 2]) QRAM call with 1 qubit address ('H', [], [0]) ('RY', [1.164734201894148], [1]) ('CNOT', [], [0, 1]) ('RY', [-0.24062886139716544], [1]) ('CNOT', [], [0, 1]) ('RY', [1.410368857987665], [2]) ('CNOT', [], [1, 2]) ('RY', [0.3567354116708534], [2]) ('CNOT', [], [0, 2]) ('RY', [-0.18847912247571447], [2]) ('CNOT', [], [1, 2]) ('RY', [0.7177579394158706], [2]) ('CNOT', [], [0, 2])
- get_routine(addresses_count=0)
Returns a
QRoutine
object implementing a call to a QRAM loading the data in a quantum memory. The resulting QRoutine is composed of a sequence of RY-mutliplexors.The routine always acts on sum(log(data.shape)) qubits. The addresses_count parameter allows to control how many initial axis of the data will be considered as addresses in case of a QRAM application.
- Parameters
addresses_count (int) – the number of axis to use as address. Defaults to 0 (i.e the routine will prepare a state proportional to the flattened data).
References
- KP17
Iordanis Kerenidis and Anupam Prakash. Quantum recommendation systems. In 8th Innovations in Theoretical Computer Science Conference (ITCS 2017). Schloss Dagstuhl-Leibniz-Zentrum fuer Informatik, 2017.