qml.math.decomposition.givens_decomposition¶
- givens_decomposition(unitary)[source]¶
Decompose a unitary into a sequence of Givens rotation gates with phase shifts and a diagonal phase matrix.
This decomposition is based on the construction scheme given in Optica, 3, 1460 (2016), which allows one to write any \(N\times N\) unitary matrix \(U\) as:
\[U = D \left(\prod_{m \in G} T_m(\theta, \phi)\right),\]where \(D\) is a diagonal phase matrix, \(T_m(\theta, \phi)\) is the Givens rotation (with phase shift) between matrix indices \(m\) and \(m+1\) (zero-based indexing), and \(G\) defines the ordered sequence of the Givens rotations. The explicit form of the Givens rotation with phase shift reads:
\[\begin{split}T(\theta, \phi) = \begin{bmatrix} \mathbb{I}_{m} & 0 & 0 & 0 \\ 0 & e^{i \phi} \cos(\theta) & -\sin(\theta) & 0 \\ 0 & e^{i \phi} \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 0 & \mathbb{I}_{N-m-2} \end{bmatrix},\end{split}\]where \(\theta \in [0, \pi/2]\) is the angle of rotation and \(\phi \in [0, 2 \pi]\) represents the phase shift.
For real-valued matrices with unit determinant, i.e. special orthogonal \(U\), all phase angles \(\phi\) vanish and \(D\) can be fixed to the identity, absorbing its phases \(\pm 1\) (with an even number of \(-1\)s due to the determinant constraint) into the \(T\) matrices. Whether \(U\) is orthogonal is inferred from the data type of
unitary
. If the determinant of an orthogonal \(U\) is negative, this will show as a single negative phase in the first output value ofgivens_decomposition
.Example
unitary = np.array([[ 0.73678+0.27511j, -0.5095 +0.10704j, -0.06847+0.32515j], [-0.21271+0.34938j, -0.38853+0.36497j, 0.61467-0.41317j], [ 0.41356-0.20765j, -0.00651-0.66689j, 0.32839-0.48293j]]) phase_mat, ordered_rotations = givens_decomposition(unitary)
>>> phase_mat tensor([-0.20604358+0.9785369j , -0.82993272+0.55786114j, 0.56230612-0.82692833j], requires_grad=True) >>> ordered_rotations [(tensor([[-0.65087861-0.63937521j, -0.40933651-0.j ], [-0.29201359-0.28685265j, 0.91238348-0.j ]], requires_grad=True), (0, 1)), (tensor([[ 0.47970366-0.33308926j, -0.8117487 -0.j ], [ 0.66677093-0.46298215j, 0.5840069 -0.j ]], requires_grad=True), (1, 2)), (tensor([[ 0.36147547+0.73779454j, -0.57008306-0.j ], [ 0.2508207 +0.51194108j, 0.82158706-0.j ]], requires_grad=True), (0, 1))]
- Parameters:
unitary (tensor) – unitary matrix on which decomposition will be performed
- Returns:
diagonal elements of the phase matrix \(D\) and Givens rotation matrix \(T\) with their indices
- Return type:
(tensor_like, list[(tensor_like, tuple)])
- Raises:
ValueError – if the provided matrix is not square.
Theory and Pseudocode
Pseudocode
The algorithm that implements the decomposition is the following:
def givens_decomposition(U): for i in range(1, N): if i % 2: for j in range(0, i): # Find T^-1(i-j, i-j+1) matrix that nulls element (N-j, i-j) of U # Update U = U @ T^-1(i-j, i-j+1) else: for j in range(i): # Find T(N+j-i-1, N+j-i) matrix that nulls element (N+j-i, j) of U # Update U = T(N+j-i-1, N+j-i) @ U if real_data_type: # Absorb the diagonal phases, which can only be 1s and an even number of # -1s, in the T gates. Adjust ordering and/or matrices of left-applied # and right-applied T matrices so that they make up U instead of U^{-1}. else: # Commute the diagonal phases from between the left-applied and right-applied # T matrices to the left. Adjust ordering and/or matrices of left-applied # and right-applied T matrices so that they make up U instead of U^{-1}.