Approximate Quantum Compiler (qiskit.transpiler.synthesis.aqc
)¶
Implementation of Approximate Quantum Compiler as described in the paper [1].
Interface¶
The main public interface of this module is reached by passing unitary_synthesis_method='aqc'
to
transpile
. This will swap the synthesis method to use AQCSynthesisPlugin
.
The individual classes are:

A generic implementation of Approximate Quantum Compiler. 
An AQCbased Qiskit unitary synthesis plugin. 


A base class that represents an approximate circuit. 
A base class for an optimization problem definition. 


A class that represents an approximate circuit based on CNOT unit blocks. 

A base class for a problem definition based on CNOT unit. 

A naive implementation of the objective function based on CNOT units. 
Mathematical Detail¶
We are interested in compiling a quantum circuit, which we formalize as finding the best circuit representation in terms of an ordered gate sequence of a target unitary matrix \(U\in U(d)\), with some additional hardware constraints. In particular, we look at representations that could be constrained in terms of hardware connectivity, as well as gate depth, and we choose a gate basis in terms of CNOT and rotation gates. We recall that the combination of CNOT and rotation gates is universal in \(SU(d)\) and therefore it does not limit compilation.
To properly define what we mean by best circuit representation, we define the metric as the Frobenius norm between the unitary matrix of the compiled circuit \(V\) and the target unitary matrix \(U\), i.e., \(\V  U\_{\mathrm{F}}\). This choice is motivated by mathematical programming considerations, and it is related to other formulations that appear in the literature. Let's take a look at the problem in more details.
Let \(n\) be the number of qubits and \(d=2^n\). Given a CNOT structure \(ct\) and a vector of rotation angles \(\theta\), the parametric circuit forms a matrix \(Vct(\theta)\in SU(d)\). If we are given a target circuit forming a matrix \(U\in SU(d)\), then we would like to compute
where the inner product is the Frobenius inner product. Note that \(\langle V,U\rangle\leq d\) for all unitaries \(U\) and \(V\), so the objective has range in \([0,1]\).
Our strategy is to maximize
using its gradient. We will now discuss the specifics by going through an example.
While the range of \(Vct\) is a subset of \(SU(d)\) by construction, the target circuit may form a general unitary matrix. However, for any \(U\in U(d)\),
Thus, we should normalize the target circuit by its global phase and then approximately compile the normalized circuit. We can add the global phase back in afterwards.
In the algorithm let \(U'\) denote the unnormalized target matrix and \(U\) the normalized target matrix. Now that we have \(U\), we give the gradient function to the Nesterov's method optimizer and compute \(\theta\).
To add the global phase back in, we can form the control circuit as
Note that while we optimized using Nesterov's method in the paper, this was for its convergence guarantees, not its speed in practice. It is much faster to use LBFGS which is used as a default optimizer in this implementation.
A basic usage of the AQC algorithm should consist of the following steps:
# Define a target circuit as a unitary matrix
unitary = ...
# Define a number of qubits for the algorithm, at least 3 qubits
num_qubits = int(round(np.log2(unitary.shape[0])))
# Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for
# a linear layout.
layout = options.get("layout") or "spin"
# Choose a connectivity type, e.g. ``full`` for full connectivity between qubits.
connectivity = options.get("connectivity") or "full"
# Define a targeted depth of the approximate circuit in the number of CNOT units.
depth = int(options.get("depth") or 0)
# Generate a network made of CNOT units
cnots = make_cnot_network(
num_qubits=num_qubits,
network_layout=layout,
connectivity_type=connectivity,
depth=depth,
)
# Create an optimizer to be used by AQC
optimizer = L_BFGS_B()
# Create an instance
aqc = AQC(optimizer)
# Create a template circuit that will approximate our target circuit
approximate_circuit = CNOTUnitCircuit(num_qubits=num_qubits, cnots=cnots)
# Create an objective that defines our optimization problem
approximating_objective = DefaultCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)
# Run optimization process to compile the unitary
aqc.compile_unitary(
target_matrix=unitary,
approximate_circuit=approximate_circuit,
approximating_objective=approximating_objective,
)
Now approximate_circuit
is a circuit that approximates the target unitary to a certain
degree and can be used instead of the original matrix.
This uses a helper function, make_cnot_network
.
 make_cnot_network(num_qubits, network_layout='spin', connectivity_type='full', depth=0)[source]¶
Generates a network consisting of building blocks each containing a CNOT gate and possibly some singlequbit ones. This network models a quantum operator in question. Note, each building block has 2 input and outputs corresponding to a pair of qubits. What we actually return here is a chain of indices of qubit pairs shared by every building block in a row.
 Parameters
num_qubits (
int
)  number of qubits.network_layout (
str
)  type of network geometry,{"sequ", "spin", "cart", "cyclic_spin", "cyclic_line"}
.connectivity_type (
str
)  type of interqubit connectivity,{"full", "line", "star"}
.depth (
int
)  depth of the CNOTnetwork, i.e. the number of layers, where each layer consists of a single CNOTblock; default value will be selected, ifL <= 0
.
 Return type
ndarray
 Returns
 A matrix of size
(2, N)
matrix that defines layers in cnotnetwork, whereN
is either equal
L
, or defined by a concrete type of the network.
 A matrix of size
 Raises
ValueError  if unsupported type of CNOTnetwork layout or number of qubits or combination of parameters are passed.
References
 [1]: Liam Madden, Andrea Simonetto, Best Approximate Quantum Compiling Problems.