-- | This module provides functions for decomposing a unitary /n/×/n/ -- operator into one- and two-level unitaries. -- -- The algorithm is adapted from Section 4.5.1 of Nielsen and -- Chuang. In addition to what is described in Nielsen and Chuang, our -- algorithm produces two-level operators that can be decomposed using -- only two Euler angles. The algorithm produces at most /n/(/n/−1)\/2 -- two-level operators of type /R/[sub /z/](δ)/R/[sub /x/](γ), as well -- as /n/ one-level operators of type [exp /i/θ]. Therefore, the -- decomposition of a unitary /n/×/n/ operator yields /n/[sup 2] real -- parameters, which is optimal. module Quantum.Synthesis.RotationDecomposition where import Quantum.Synthesis.Matrix import Quantum.Synthesis.MultiQubitSynthesis import Quantum.Synthesis.Ring import Quantum.Synthesis.EulerAngles import Quantum.Synthesis.ArcTan2 import Data.List import System.Random -- ---------------------------------------------------------------------- -- * Elementary rotations -- | An elementary rotation is either a combined /x/- and -- /z/-rotation, applied at indices /j/ and /k/, or a phase change -- applied at index /j/. -- -- * 'ERot_zx' δ γ /j/ /k/ represents the operator -- /R/[sub /z/](δ)/R/[sub /x/](γ), applied to levels /j/ and /k/. -- -- \[image ERot_zx.png] -- -- * 'ERot_phase' θ /j/ represents the operator [exp /i/θ] applied to level -- /j/. -- -- \[image ERot_phase.png] -- -- Note: when we use a list of 'ElementaryRot's to express a sequence of -- operators, the operators are meant to be applied right-to-left, -- i.e., as in the mathematical notation for matrix multiplication. -- This is the opposite of the quantum circuit notation. data ElementaryRot a = ERot_zx a a Index Index | ERot_phase a Index deriving (Show) -- | Convert a symbolic elementary rotation to a concrete matrix. matrix_of_elementary :: (Ring a, Floating a, Nat n) => ElementaryRot a -> Matrix n n (Cplx a) matrix_of_elementary (ERot_zx delta gamma j k) = twolevel_matrix (a, b) (c, d) j k where a = ed' * cg b = -i * ed' * sg c = -i * ed * sg d = ed * cg cg = Cplx (cos (gamma/2)) 0 sg = Cplx (sin (gamma/2)) 0 ed = Cplx cd sd ed' = Cplx cd (-sd) cd = cos (delta/2) sd = sin (delta/2) matrix_of_elementary (ERot_phase theta j) = onelevel_matrix (Cplx c s) j where c = cos theta s = sin theta -- | Convert a sequence of elementary rotations to an /n/×/n/-matrix. matrix_of_elementaries :: (Ring a, Floating a, Nat n) => [ElementaryRot a] -> Matrix n n (Cplx a) matrix_of_elementaries ops = foldl' (*) 1 [ matrix_of_elementary op | op <- ops ] -- ---------------------------------------------------------------------- -- * Decomposition into elementary rotations -- | Convert an /n/×/n/-matrix to a sequence of elementary rotations. -- -- Note: the list of elementary rotations will be returned in -- right-to-left order, i.e., as in the mathematical notation for -- matrix multiplication. This is the opposite of the quantum circuit -- notation. rotation_decomposition :: (Eq a, Fractional a, Floating a, Adjoint a, ArcTan2 a, Nat n) => Matrix n n (Cplx a) -> [ElementaryRot a] rotation_decomposition op = concat gates ++ reverse gates' where (op', gates) = mapAccumL rowop op [ (i,j) | j <- [0..n-2], i <- [j+1..n-1] ] gates' = [ get_phase op' i | i <- [0..n-1] ] (n', _) = matrix_size op n = fromInteger n' -- ---------------------------------------------------------------------- -- * Auxiliary functions -- | Construct a two-level /n/×/n/-matrix from a given 2×2-matrix and -- indices /j/ and /k/. twolevel_matrix_of_matrix :: (Ring a, Nat n) => Matrix Two Two a -> Index -> Index -> Matrix n n a twolevel_matrix_of_matrix u j k = op where op = twolevel_matrix (a,b) (c,d) j k ((a,b), (c,d)) = from_matrix2x2 u -- | Extract the phase of the /j/th diagonal entry of the given -- matrix. get_phase :: (ArcTan2 a) => Matrix n n (Cplx a) -> Index -> ElementaryRot a get_phase op j = ERot_phase theta j where a = matrix_index op j j theta = arctan2 y x Cplx x y = a -- | Perform a two-level operation on rows /j/ and /k/ of a matrix /U/, -- such that the resulting matrix has a 0 in the (/j/,/k/)-position. -- Return the inverse of the two-level operation used, as well as the -- updated matrix. rowop :: (Eq a, Fractional a, Floating a, Adjoint a, ArcTan2 a, Nat n) => Matrix n n (Cplx a) -> (Index, Index) -> (Matrix n n (Cplx a), [ElementaryRot a]) rowop op (j,k) | b == 0 = (op, []) | otherwise = (op', gates) where a = matrix_index op k k b = matrix_index op j k matrix = 1/Cplx (sqrt(real (a * adj a + b * adj b))) 0 `scalarmult` matrix2x2 (adj a, adj b) (b, -a) (alpha, beta, gamma, delta) = euler_angles matrix matrix2 = matrix_of_euler_angles (0, 0, gamma, delta) op' = twolevel_matrix_of_matrix matrix2 k j .*. op gates = [ ERot_zx (-delta) (-gamma) k j ] -- ---------------------------------------------------------------------- -- * Testing -- | Return a \"random\" unitary /n/×/n/-matrix. These matrices will -- not quite be uniformly distributed; this function is primarily -- meant to generate test cases. random_unitary :: (RandomGen g, Nat n, Floating a, Random a) => g -> Matrix n n (Cplx a) random_unitary g = op where op = matrix_of_elementaries gates gates = random_gates g (20*n^2) random_gates g 0 = [] random_gates g m = h:t where (gamma, g1) = randomR (0, 2*pi) g (delta, g1') = randomR (0, 2*pi) g1 (c, g2) = randomR (0, 1) g1' (j, g3) = randomR (0, n-2) g2 (k, g4) = randomR (j+1, n-1) g3 h = case c :: Int of 0 -> ERot_zx delta gamma j k _ -> ERot_phase delta j t = random_gates g4 (m-1) (n', _) = matrix_size op n = fromInteger n' -- | Generate a random matrix, decompose it, and then re-calculate the -- matrix from the decomposition. test :: IO () test = do g <- newStdGen let m = random_unitary g :: Matrix Four Four CDouble let gates = rotation_decomposition m let m' = matrix_of_elementaries gates :: Matrix Four Four CDouble putStrLn $ "m = " ++ show m putStrLn $ "gates = " ++ show gates putStrLn $ "m' = " ++ show m'