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
QubitUnitary
operations using theunitary_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 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) >>> U array([[-0.29113625+0.56393527j, 0.39546712-0.14193837j, 0.04637428+0.01311566j, -0.62006741+0.18403743j], [-0.45479211+0.25978444j, -0.52737418-0.5549423j , -0.23429057+0.10728103j, 0.16061807-0.21769762j], [-0.4501231 +0.04065613j, -0.25558662+0.38209554j, -0.04143479-0.56598134j, 0.12983673+0.49548507j], [ 0.23899902+0.24800931j, 0.03374589-0.15784319j, 0.24898226-0.73975147j, 0.0269508 -0.49534518j]])
We can compute its decompositon like so:
>>> decomp = qml.ops.two_qubit_decomposition(np.array(U), wires=[0, 1]) >>> decomp [QubitUnitary(array([[ 0.02867704+0.82548843j, 0.5568274 -0.08769111j], [-0.5568274 -0.08769111j, 0.02867704-0.82548843j]]), wires=[0]), QubitUnitary(array([[ 0.32799033-0.78598401j, 0.40660725+0.33063881j], [-0.40660725+0.33063881j, 0.32799033+0.78598401j]]), wires=[1]), CNOT(wires=[1, 0]), RZ(0.259291854677022, wires=[0]), RY(-0.05808874413267284, wires=[1]), CNOT(wires=[0, 1]), RY(-1.6742322786950354, wires=[1]), CNOT(wires=[1, 0]), QubitUnitary(array([[ 0.91031205-0.21930866j, 0.20674186-0.28371375j], [-0.20674186-0.28371375j, 0.91031205+0.21930866j]]), wires=[1]), QubitUnitary(array([[-0.81886788-0.02979899j, 0.53279787-0.21140919j], [-0.53279787-0.21140919j, -0.81886788+0.02979899j]]), wires=[0]), GlobalPhase(0.1180587403699308, wires=[])]