{-# LANGUAGE OverlappingInstances #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} -- | This module provides an efficient symbolic representation of the -- Clifford group on one qubit. This group is generated by /S/, /H/, -- and the scalar ω = [exp /i/π\/4]. It has 192 elements. module Quantum.Synthesis.Clifford ( -- * The Clifford group Clifford, -- ** Constructors clifford_X, clifford_Y, clifford_Z, clifford_H, clifford_S, clifford_SH, clifford_E, clifford_W, ToClifford(to_clifford), -- ** Deconstructors clifford_decompose, Axis(..), clifford_decompose_coset, -- ** Group operations clifford_id, clifford_mult, clifford_inv, -- ** Conjugation by /T/ clifford_tconj ) where -- ---------------------------------------------------------------------- -- * The Clifford group -- $ We could, in principle, implement the Clifford group as an -- enumerated type with 192 elements, and a large 192×192 lookup -- table for the group multiplication. Instead, we take advantage of -- some of the internal structure of the group to reduce the size of -- the lookup tables. The resulting implementation is still very -- efficient. -- | A type representing single-qubit Clifford operators. data Clifford = Clifford Int Int Int Int deriving (Eq, Ord) instance Show Clifford where show (Clifford a b c d) = "C" ++ show a ++ show b ++ show c ++ show d -- ---------------------------------------------------------------------- -- ** Constructors -- | The Pauli /X/-gate as a Clifford operator. clifford_X :: Clifford clifford_X = Clifford 0 1 0 0 -- | The Pauli /Y/-gate as a Clifford operator. clifford_Y :: Clifford clifford_Y = Clifford 0 1 2 2 -- | The Pauli /Z/-gate as a Clifford operator. clifford_Z :: Clifford clifford_Z = Clifford 0 0 2 0 -- | The Hadamard gate as a Clifford operator. clifford_H :: Clifford clifford_H = Clifford 1 0 1 5 -- | The Clifford operator /S/. clifford_S :: Clifford clifford_S = Clifford 0 0 1 0 -- | The Clifford operator /SH/. clifford_SH :: Clifford clifford_SH = clifford_S `clifford_mult` clifford_H -- | The Clifford operator /E/ = /H//S/[sup 3]ω[sup 3]. This operator is -- uniquely determined by the properties /E/³ = /I/, -- /EXE/⁻¹ = /Y/, /EYE/⁻¹ = /Z/, and /EZE/⁻¹ = /X/. -- -- \[image E.png] clifford_E :: Clifford clifford_E = Clifford 1 0 0 0 -- | The Clifford operator ω = [exp /i/π\/4]. clifford_W :: Clifford clifford_W = Clifford 0 0 0 1 -- | A type class for things that can be exactly converted to a -- Clifford operator. One particular instance of this is 'String', so -- that Clifford operators can be denoted, e.g., -- -- > to_clifford "-iX" -- -- The valid characters for such string conversions are @\"XYZHSEIWi-\"@. class ToClifford a where -- | Convert any suitable thing to a Clifford operator. to_clifford :: a -> Clifford instance ToClifford Clifford where to_clifford = id instance ToClifford Char where to_clifford 'E' = clifford_E to_clifford 'X' = clifford_X to_clifford 'S' = clifford_S to_clifford 'W' = clifford_W to_clifford 'I' = clifford_id to_clifford 'i' = Clifford 0 0 0 2 to_clifford '-' = Clifford 0 0 0 4 to_clifford 'H' = clifford_H to_clifford 'Y' = clifford_Y to_clifford 'Z' = clifford_Z to_clifford x = error $ "ToClifford Char: unknown gate " ++ show x instance ToClifford a => ToClifford [a] where to_clifford [] = clifford_id to_clifford (h:t) = to_clifford h `clifford_mult` to_clifford t -- ---------------------------------------------------------------------- -- ** Deconstructors -- | Given a Clifford operator /U/, return (/a/, /b/, /c/, /d/) such that -- -- * /U/ = /E/[sup /a/]/X/[sup /b/]/S/[sup /c/]ω[sup /d/], -- -- * /a/ ∈ {0, 1, 2}, /b/ ∈ {0, 1}, /c/ ∈ {0, …, 3}, and /d/ ∈ {0, …, -- 7}. -- -- Here, /E/ = /H//S/[sup 3]ω[sup 3]. Note that /E/, /X/, /S/, and ω have order -- 3, 2, 4, and 8, respectively. Moreover, each Clifford operator can -- be uniquely represented as above. clifford_decompose :: (ToClifford a) => a -> (Int, Int, Int, Int) clifford_decompose m = (a,b,c,d) where Clifford a b c d = to_clifford m -- | A axis is either /I/, /H/, or /SH/. data Axis = Axis_I | Axis_H | Axis_SH deriving (Eq, Show) instance ToClifford Axis where to_clifford Axis_I = to_clifford "I" to_clifford Axis_H = to_clifford "H" to_clifford Axis_SH = to_clifford "SH" -- | Given a Clifford operator /U/, return (/K/, /b/, /c/, /d/) such that -- -- * /U/ = /K//X/[sup /b/]/S/[sup /c/]ω[sup /d/], -- -- * /K/ ∈ {/I/, /H/, /SH/}, /b/ ∈ {0, 1}, /c/ ∈ {0, …, 3}, and /d/ ∈ {0, …, -- 7}. clifford_decompose_coset :: (ToClifford a) => a -> (Axis, Int, Int, Int) clifford_decompose_coset u = case op of Clifford 0 b c d -> (Axis_I, b, c, d) Clifford 1 b c d -> (Axis_H, b', c', d') where Clifford 0 b' c' d' = clifford_inv "H" `clifford_mult` op Clifford 2 b c d -> (Axis_SH, b', c', d') where Clifford 0 b' c' d' = clifford_inv "SH" `clifford_mult` op where op = to_clifford u -- ---------------------------------------------------------------------- -- ** Group operations -- | The identity Clifford operator. clifford_id :: Clifford clifford_id = Clifford 0 0 0 0 -- | Clifford multiplication. clifford_mult :: Clifford -> Clifford -> Clifford clifford_mult u1 u2 = u where -- U = U1 U2 -- = A1 B1 C1 D1 A2 B2 C2 D2 -- = A1 (B1 C1 A2) B2 C2 D1 D2 -- = A1 (A3 B3 C3 D3) B2 C2 D1 D2 -- = A1 A3 B3 (C3 B2) C2 D3 D1 D2 -- = A1 A3 B3 (B2 C4 D4) C2 D3 D1 D2 -- = (A1 A3) (B3 B2) (C4 C2) (D4 D3 D1 D2) -- = A B C D Clifford a1 b1 c1 d1 = u1 Clifford a2 b2 c2 d2 = u2 (a3, b3, c3, d3) = conj3 b1 c1 a2 (c4, d4) = conj2 c3 b2 a = (a1 + a3) `mod` 3 b = (b3 + b2) `mod` 2 c = (c4 + c2) `mod` 4 d = (d4 + d3 + d1 + d2) `mod` 8 u = Clifford a b c d -- | Clifford inverse. clifford_inv :: (ToClifford a) => a -> Clifford clifford_inv op = Clifford a2 b2 c2 d3 where -- U⁻¹ = (A B C)⁻¹ D⁻¹ = (A2 B2 C2 D2) D⁻¹ Clifford a b c d = to_clifford op (a2, b2, c2, d2) = cinv a b c d3 = (d2 - d) `mod` 8 -- ---------------------------------------------------------------------- -- ** Conjugation by /T/ -- | Given a Clifford gate /C/, return an axis /K/ ∈ {/I/, /H/, /SH/} -- and a Clifford gate /C'/ such that -- -- * /C//T/ = /K//T//C/'. clifford_tconj :: Clifford -> (Axis, Clifford) clifford_tconj u = (k, v) where -- U T = A1 B1 C1 D1 T -- = (A1 B1 T) C1 D1 -- = (K T B1 C2 D2) C1 D1 -- = K T B1 (C2 C1) (D2 D1) Clifford a1 b1 c1 d1 = u (k, c2, d2) = tconj a1 b1 c = (c2 + c1) `mod` 4 d = (d2 + d1) `mod` 8 v = Clifford 0 b1 c d -- ---------------------------------------------------------------------- -- ** Lookup tables -- | 'conj2' /c/ /b/ returns (/c/', /d/') such that -- -- * /S/[sup /c/]/X/[sup /b/] = /X/[sup /b/]/S/[sup /c/']ω[sup /d/']. conj2 :: Int -> Int -> (Int, Int) conj2 0 0 = (0,0) conj2 0 1 = (0,0) conj2 1 0 = (1,0) conj2 1 1 = (3,2) conj2 2 0 = (2,0) conj2 2 1 = (2,4) conj2 3 0 = (3,0) conj2 3 1 = (1,6) -- | 'conj3' /b/ /c/ /a/ returns (/a/', /b/', /c/', /d/') such that -- -- * /X/[sup /b/]/S/[sup /c/]/E/[sup /a/] = /E/[sup /a/']/X/[sup /b/']/S/[sup /c/']ω[sup /d/']. conj3 :: Int -> Int -> Int -> (Int, Int, Int, Int) conj3 0 0 0 = (0,0,0,0) conj3 0 0 1 = (1,0,0,0) conj3 0 0 2 = (2,0,0,0) conj3 0 1 0 = (0,0,1,0) conj3 0 1 1 = (2,0,3,6) conj3 0 1 2 = (1,1,3,4) conj3 0 2 0 = (0,0,2,0) conj3 0 2 1 = (1,1,2,2) conj3 0 2 2 = (2,1,0,0) conj3 0 3 0 = (0,0,3,0) conj3 0 3 1 = (2,1,3,6) conj3 0 3 2 = (1,0,1,2) conj3 1 0 0 = (0,1,0,0) conj3 1 0 1 = (1,0,2,0) conj3 1 0 2 = (2,1,2,2) conj3 1 1 0 = (0,1,1,0) conj3 1 1 1 = (2,1,1,0) conj3 1 1 2 = (1,1,1,0) conj3 1 2 0 = (0,1,2,0) conj3 1 2 1 = (1,1,0,6) conj3 1 2 2 = (2,0,2,6) conj3 1 3 0 = (0,1,3,0) conj3 1 3 1 = (2,0,1,4) conj3 1 3 2 = (1,0,3,2) -- | 'cinv' /a/ /b/ /c/ returns (/a/', /b/', /c/', /d/') such that -- -- * (/E/[sup /a/]/X/[sup /b/]/S/[sup /c/])⁻¹ = /E/[sup /a/']/X/[sup /b/']/S/[sup /c/']ω[sup /d/']. cinv :: Int -> Int -> Int -> (Int, Int, Int, Int) cinv 0 0 0 = (0,0,0,0) cinv 0 0 1 = (0,0,3,0) cinv 0 0 2 = (0,0,2,0) cinv 0 0 3 = (0,0,1,0) cinv 0 1 0 = (0,1,0,0) cinv 0 1 1 = (0,1,1,6) cinv 0 1 2 = (0,1,2,4) cinv 0 1 3 = (0,1,3,2) cinv 1 0 0 = (2,0,0,0) cinv 1 0 1 = (1,0,1,2) cinv 1 0 2 = (2,1,0,0) cinv 1 0 3 = (1,1,3,4) cinv 1 1 0 = (2,1,2,2) cinv 1 1 1 = (1,1,1,6) cinv 1 1 2 = (2,0,2,2) cinv 1 1 3 = (1,0,3,4) cinv 2 0 0 = (1,0,0,0) cinv 2 0 1 = (2,1,3,6) cinv 2 0 2 = (1,1,2,2) cinv 2 0 3 = (2,0,3,6) cinv 2 1 0 = (1,0,2,0) cinv 2 1 1 = (2,1,1,6) cinv 2 1 2 = (1,1,0,2) cinv 2 1 3 = (2,0,1,6) -- | 'tconj2' /a/ /b/ returns (/K/, /c/, /d/) such that -- -- * /E/[sup /a/]/X/[sup /b/]/T/ = /K//T//X/[sup /b/]/S/[sup /c/]ω[sup /d/]. tconj 0 0 = (Axis_I, 0, 0) tconj 0 1 = (Axis_I, 1, 7) tconj 1 0 = (Axis_H, 3, 3) tconj 1 1 = (Axis_H, 2, 0) tconj 2 0 = (Axis_SH, 0, 5) tconj 2 1 = (Axis_SH, 1, 4)