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:
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
QubitUnitaryoperations using theunitary_to_rot()transform.Warning
This decomposition will not be differentiable in the
unitary_to_rottransform if the matrix being decomposed depends on parameters with respect to which we would like to take the gradient. See the documentation ofunitary_to_rot()for explicit examples of the differentiable and non-differentiable cases.- Parameters:
- 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=[])]