qml.ops.two_qubit_decomposition

two_qubit_decomposition(U, wires)[source]

Decompose a two-qubit unitary \(U\) in terms of elementary operations.

It is known that an arbitrary two-qubit operation can be implemented using a maximum of 3 CNOTs. This transform first determines the required number of CNOTs, then decomposes the operator into a circuit with a fixed form. These decompositions are based a number of works by Shende, Markov, and Bullock (1), (2), (3), though we note that many alternative decompositions are possible.

For the 3-CNOT case, we recover the following circuit, which is Figure 2 in reference (1) above:

../../_images/two_qubit_decomposition_3_cnots.svg

where \(A, B, C, D\) are \(SU(2)\) operations, and the rotation angles are computed based on features of the input unitary \(U\).

For the 2-CNOT case, the decomposition is currently not supported and will instead produce a 3-CNOT circuit like above.

For a single CNOT, we have a CNOT surrounded by one \(SU(2)\) per wire on each side. The special case of no CNOTs simply returns a tensor product of two \(SU(2)\) operations.

This decomposition can be applied automatically to all two-qubit QubitUnitary operations using the unitary_to_rot() transform.

Warning

This decomposition will not be differentiable in the unitary_to_rot transform if the matrix being decomposed depends on parameters with respect to which we would like to take the gradient. See the documentation of unitary_to_rot() for explicit examples of the differentiable and non-differentiable cases.

Parameters:
  • U (tensor) – A \(4 \times 4\) unitary matrix.

  • wires (Union[Wires, Sequence[int] or int]) – The wires on which to apply the operation.

Returns:

A list of operations that represent the decomposition of the matrix U.

Return type:

list[Operation]

Example

Suppose we create a random element of \(U(4)\), and would like to decompose it into elementary gates in our circuit.

>>> from scipy.stats import unitary_group
>>> U = unitary_group.rvs(4, random_state=42)

We can compute its decompositon like so:

>>> from pprint import pprint
>>> decomp = qml.ops.two_qubit_decomposition(np.array(U), wires=[0, 1])
>>> pprint(decomp) 
[QubitUnitary(array([[ 0.35935497-0.35945703j, -0.81150079+0.28830732j],
       [ 0.81150079+0.28830732j,  0.35935497+0.35945703j]]), wires=[0]),
 QubitUnitary(array([[ 0.73465919-0.15696895j,  0.51629531-0.41118825j],
       [-0.51629531-0.41118825j,  0.73465919+0.15696895j]]), wires=[1]),
 CNOT(wires=[1, 0]),
 RZ(np.float64(0.028408953417448358), wires=[0]),
 RY(np.float64(0.6226823676455966), wires=[1]),
 CNOT(wires=[0, 1]),
 RY(np.float64(-0.7259987841675299), wires=[1]),
 CNOT(wires=[1, 0]),
 QubitUnitary(array([[ 0.85429569-0.34743933j,  0.14569083+0.35810469j],
       [-0.14569083+0.35810469j,  0.85429569+0.34743933j]]), wires=[0]),
 QubitUnitary(array([[-0.30052527-0.4826478j ,  0.74833925-0.34164898j],
       [-0.74833925-0.34164898j, -0.30052527+0.4826478j ]]), wires=[1]),
 GlobalPhase(np.float64(0.07394316416802127), wires=[])]