{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE BangPatterns #-}
module Quipper.Utils.Stabilizers.Clifford where
import Prelude hiding (lookup,negate)
import Quipper.Utils.Stabilizers.Pauli
import Data.List (foldl')
import Data.Map (Map)
import qualified Data.Map as Map
import Control.Monad.State
import System.Random
import Quantum.Synthesis.Ring (Cplx (..), i)
type Qubit = Int
data Tableau = ST Qubit !(Map Qubit Sign) !(Map (Qubit,Qubit) Pauli) !(Map Qubit Sign) !(Map (Qubit,Qubit) Pauli)
next_qubit :: Tableau -> Qubit
next_qubit (ST nq _ _ _ _) = nq
sign :: Tableau -> Map Qubit Sign
sign (ST _ s _ _ _) = s
tableau :: Tableau -> Map (Qubit,Qubit) Pauli
tableau (ST _ _ t _ _) = t
de_sign :: Tableau -> Map Qubit Sign
de_sign (ST _ _ _ de_s _) = de_s
de_tableau :: Tableau -> Map (Qubit,Qubit) Pauli
de_tableau (ST _ _ _ _ de_t) = de_t
lookup :: (Ord k,Show k, Show v) => k -> Map k v -> v
lookup k m =
case Map.lookup k m of
Just b -> b
Nothing -> error ("key: " ++ show k ++ " not in map: " ++ show m)
instance Show Tableau where
show tab = unlines $ ("Stabilizer:":map show_row qs) ++ ("Destabilizer:":map show_de_row qs)
where
qs :: [Qubit]
qs = [0..(next_qubit tab) - 1]
show_row :: Qubit -> String
show_row q_row = show (lookup q_row (sign tab))
++ unwords (map (show_pauli q_row) qs)
show_pauli :: Qubit -> Qubit -> String
show_pauli q_row q_column = show (lookup (q_row,q_column) (tableau tab))
show_de_row :: Qubit -> String
show_de_row q_row = show (lookup q_row (de_sign tab))
++ unwords (map (show_de_pauli q_row) qs)
show_de_pauli :: Qubit -> Qubit -> String
show_de_pauli q_row q_column = show (lookup (q_row,q_column) (de_tableau tab))
empty_tableau :: Tableau
empty_tableau = ST 0 Map.empty Map.empty Map.empty Map.empty
add_qubit :: Bool -> Tableau -> Tableau
add_qubit b tab = ST (nq + 1) sign' tableau' de_sign' de_tableau'
where
nq = next_qubit tab
sign' = Map.insert nq (if b then Minus else Plus) (sign tab)
de_sign' = Map.insert nq Plus (de_sign tab)
tableau' = foldl' insertI (foldl' insertZ (tableau tab) [0..nq]) [0..nq-1]
de_tableau' = foldl' insertI (foldl' insertX (de_tableau tab) [0..nq]) [0..nq-1]
insertZ :: Map (Qubit,Qubit) Pauli -> Qubit -> Map (Qubit,Qubit) Pauli
insertZ tab cq = Map.insert (nq,cq) (if nq == cq then Z else I) tab
insertX :: Map (Qubit,Qubit) Pauli -> Qubit -> Map (Qubit,Qubit) Pauli
insertX tab cq = Map.insert (nq,cq) (if nq == cq then X else I) tab
insertI :: Map (Qubit,Qubit) Pauli -> Qubit -> Map (Qubit,Qubit) Pauli
insertI tab cq = Map.insert (cq,nq) I tab
type Unitary = Pauli -> (Sign,Pauli)
instance Eq Unitary where
u1 == u2 = and [ u1 x == u2 x | x <- ixyz]
where ixyz = [I,X,Y,Z]
data MinPauli = Xmin | Zmin
type MinUnitary = MinPauli -> (Sign,Pauli)
from_minimal :: MinUnitary -> Unitary
from_minimal f I = (Plus,I)
from_minimal f X = f Xmin
from_minimal f Z = f Zmin
from_minimal f Y = (sign,pauli)
where
(sx,px) = f Xmin
(sz,pz) = f Zmin
(spc,pauli) = commute px pz
sign = signPlus_to_sign $ multiply_signPlus (multiply_signPlus (One sx) (One sz)) (multiply_signPlus (PlusI) (spc))
from_matrix :: (Floating r, Eq r, Show r) => Matrix1 (Cplx r) -> Unitary
from_matrix m = from_minimal minimal
where
minimal xy = sp
where
xy' = case xy of
Xmin -> X
Zmin -> Z
m_dagger = transpose1 m
sp = fromMatrix1 $ multiplyMatrix1 m (multiplyMatrix1 (toMatrix1 xy') m_dagger)
apply_unitary :: Unitary -> Qubit -> Tableau -> Tableau
apply_unitary u q_column tab = foldl' (apply_unitary_row u q_column) tab [0..nq-1]
where
nq = next_qubit tab
apply_unitary_row :: Unitary -> Qubit -> Tableau -> Qubit -> Tableau
apply_unitary_row u q_column tab q_row = ST (next_qubit tab) sign' tableau' de_sign' de_tableau'
where
s = sign tab
current_sign = lookup q_row s
t = tableau tab
current_pauli = lookup (q_row,q_column) t
(change_sign,new_pauli) = u current_pauli
new_sign = if negative change_sign then negate current_sign else current_sign
sign' = Map.insert q_row new_sign s
tableau' = Map.insert (q_row,q_column) new_pauli t
de_s = de_sign tab
de_current_sign = lookup q_row de_s
de_t = de_tableau tab
de_current_pauli = lookup (q_row,q_column) de_t
(de_change_sign,de_new_pauli) = u de_current_pauli
de_new_sign = if negative de_change_sign then negate de_current_sign else de_current_sign
de_sign' = Map.insert q_row de_new_sign de_s
de_tableau' = Map.insert (q_row,q_column) de_new_pauli de_t
type Unitary2 = (Pauli,Pauli) -> (Sign,Pauli,Pauli)
instance Eq Unitary2 where
u1 == u2 = and [ u1 (x,y) == u2 (x,y) | x <- ixyz, y <- ixyz]
where ixyz = [I,X,Y,Z]
data MinPauli2 = IX | XI | IZ | ZI
type MinUnitary2 = MinPauli2 -> (Sign,Pauli,Pauli)
from_minimal2 :: MinUnitary2 -> Unitary2
from_minimal2 f (I,I) = (Plus,I,I)
from_minimal2 f (I,X) = f IX
from_minimal2 f (X,I) = f XI
from_minimal2 f (I,Z) = f IZ
from_minimal2 f (Z,I) = f ZI
from_minimal2 f (I,Y) = (sign,p1,p2)
where
(six,pix1,pix2) = from_minimal2 f (I,X)
(siz,piz1,piz2) = from_minimal2 f (I,Z)
(spc1,p1) = commute pix1 piz1
(spc2,p2) = commute pix2 piz2
sign = signPlus_to_sign $ multiply_signPlus (PlusI) (multiply_signPlus (multiply_signPlus (One six) (One siz)) (multiply_signPlus (spc1) (spc2)))
from_minimal2 f (Y,I) = (sign,p1,p2)
where
(six,pix1,pix2) = from_minimal2 f (X,I)
(siz,piz1,piz2) = from_minimal2 f (Z,I)
(spc1,p1) = commute pix1 piz1
(spc2,p2) = commute pix2 piz2
sign = signPlus_to_sign $ multiply_signPlus (PlusI) (multiply_signPlus (multiply_signPlus (One six) (One siz)) (multiply_signPlus (spc1) (spc2)))
from_minimal2 f (pauli1,pauli2) = (sign,p1,p2)
where
(six,pix1,pix2) = from_minimal2 f (pauli1,I)
(siz,piz1,piz2) = from_minimal2 f (I,pauli2)
(spc1,p1) = commute pix1 piz1
(spc2,p2) = commute pix2 piz2
sign = signPlus_to_sign $ multiply_signPlus (multiply_signPlus (One six) (One siz)) (multiply_signPlus (spc1) (spc2))
from_matrix2 :: (Floating r, Eq r, Show r) => Matrix2 (Cplx r) -> Unitary2
from_matrix2 m = from_minimal2 minimal
where
minimal xy = sp
where
xy' = case xy of
IX -> (I,X)
XI -> (X,I)
IZ -> (I,Z)
ZI -> (Z,I)
m_dagger = transpose2 m
sp = fromMatrix2 $ multiplyMatrix2 m (multiplyMatrix2 (toMatrix2 xy') m_dagger)
from_matrix_controlled :: (Floating r, Show r, Eq r) => Matrix1 (Cplx r) -> Unitary2
from_matrix_controlled m1 = from_matrix2 (control1 m1)
apply_unitary2 :: Unitary2 -> (Qubit,Qubit) -> Tableau -> Tableau
apply_unitary2 u (q1,q2) tab = foldl' apply_unitary2' tab [0..nq-1]
where
nq = next_qubit tab
apply_unitary2' :: Tableau -> Qubit -> Tableau
apply_unitary2' tab q_row = ST (next_qubit tab) sign' tableau'' de_sign' de_tableau''
where
s = sign tab
current_sign = lookup q_row s
t = tableau tab
current_pauli1 = lookup (q_row,q1) t
current_pauli2 = lookup (q_row,q2) t
(change_sign,new_pauli1,new_pauli2) = u (current_pauli1,current_pauli2)
new_sign = if negative change_sign then negate current_sign else current_sign
sign' = Map.insert q_row new_sign s
tableau' = Map.insert (q_row,q1) new_pauli1 t
tableau'' = Map.insert (q_row,q2) new_pauli2 tableau'
de_s = de_sign tab
de_current_sign = lookup q_row de_s
de_t = de_tableau tab
de_current_pauli1 = lookup (q_row,q1) de_t
de_current_pauli2 = lookup (q_row,q2) de_t
(de_change_sign,de_new_pauli1,de_new_pauli2) = u (de_current_pauli1,de_current_pauli2)
de_new_sign = if negative de_change_sign then negate de_current_sign else de_current_sign
de_sign' = Map.insert q_row de_new_sign de_s
de_tableau' = Map.insert (q_row,q1) de_new_pauli1 de_t
de_tableau'' = Map.insert (q_row,q2) de_new_pauli2 de_tableau'
measure :: Qubit -> Tableau -> IO (Bool,Tableau)
measure q tab = case anticommute_with_z of
[] ->
case (filter (\(row,_) -> row == z_row) z_rows) of
[] -> do
let tab' = reduce q tab
(res,_) <- measure q tab'
return (res,tab)
[(_,row)] -> return (negative (lookup row s),tab)
_ -> error "measure: multiple Zs found"
where
z_row :: [Pauli]
z_row = map (\q_col -> if q_col == q then Z else I) [0..(nq-1)]
z_rows :: [([Pauli],Qubit)]
z_rows = map (\q_row -> ((map (\q_col ->(lookup (q_row,q_col) t)) [0..(nq-1)]),q_row)) [0..(nq-1)]
[(_,q_row)] -> do
let tab' = foldl' (\tab r -> if lookup (r,q) (de_tableau tab) `elem` [X,Y] then multiply_de r q_row tab else tab) tab [0..(nq-1)]
let de_t = de_tableau tab'
let de_s = de_sign tab'
let de_s' = Map.insert q_row (lookup q_row s) de_s
let de_t' = foldl' (\de_t q' -> Map.insert (q_row,q') (lookup (q_row,q') t) de_t) de_t [0..(nq-1)]
b <- randomIO
let eigen_value = if b then Minus else Plus
let s' = Map.insert q_row eigen_value s
let t' = foldl' (\t q' -> Map.insert (q_row,q') (if q == q' then Z else I) t) t [0..(nq-1)]
let tab'' = ST nq s' t' de_s' de_t'
return (negative eigen_value,tab'')
((_,q_row1):((_,q_row2):_)) ->
measure q (multiply q_row2 q_row1 tab)
where
nq = next_qubit tab
t = tableau tab
s = sign tab
de_t = de_tableau tab
de_s = de_sign tab
gs = map (\q_row -> (lookup (q_row,q) t,q_row)) [0..(nq-1)]
anticommute_with_z = filter (\(ixyz,_) -> ixyz == X || ixyz == Y) gs
reduce :: Qubit -> Tableau -> Tableau
reduce qubit tab = foldl' (\t q -> multiply r q t) tab ows
where
nq = next_qubit tab
t = tableau tab
de_t = de_tableau tab
(r:ows) = filter (\q_row -> isXY (lookup (q_row,qubit) de_t) ) [0..nq-1]
isXY p = p == X || p == Y
multiply :: Qubit -> Qubit -> Tableau -> Tableau
multiply q_row1 q_row2 tab = ST nq s' t' (de_sign tab) (de_tableau tab)
where
nq = next_qubit tab
t = tableau tab
s = sign tab
sign1 = lookup q_row1 s
sign2 = lookup q_row2 s
sp = One (multiply_sign sign1 sign2)
(t',sp') = foldl' mul_col (t,sp) [0..(nq-1)]
s' = Map.insert q_row1 (signPlus_to_sign sp') s
mul_col :: (Map (Qubit,Qubit) Pauli, SignPlus) -> Qubit -> (Map (Qubit,Qubit) Pauli, SignPlus)
mul_col (tab,sp) q_col = (Map.insert (q_row1,q_col) p' tab,multiply_signPlus sp sp')
where
p1 = lookup (q_row1,q_col) tab
p2 = lookup (q_row2,q_col) tab
(sp',p') = commute p1 p2
multiply_de :: Qubit -> Qubit -> Tableau -> Tableau
multiply_de q_row1 q_row2 tab = ST nq s t s_de' t_de'
where
nq = next_qubit tab
t = tableau tab
s = sign tab
t_de = de_tableau tab
s_de = de_sign tab
sign1 = lookup q_row1 s_de
sign2 = lookup q_row2 s
sp = One (multiply_sign sign1 sign2)
(t_de',sp') = foldl' mul_col (t_de,sp) [0..(nq-1)]
s_de' = Map.insert q_row1 (signPlus_to_sign sp') s_de
mul_col :: (Map (Qubit,Qubit) Pauli, SignPlus) -> Qubit -> (Map (Qubit,Qubit) Pauli, SignPlus)
mul_col (tab_de,sp) q_col = (Map.insert (q_row1,q_col) p' tab_de,multiply_signPlus sp sp')
where
p1 = lookup (q_row1,q_col) tab_de
p2 = lookup (q_row2,q_col) t
(sp',p') = commute p1 p2
x :: Unitary
x I = (Plus,I)
x X = (Plus,X)
x Y = (Minus,Y)
x Z = (Minus,Z)
x_min :: MinUnitary
x_min Xmin = (Plus,X)
x_min Zmin = (Minus,Z)
x' :: Unitary
x' = from_minimal x_min
x'' :: Unitary
x'' = from_matrix (0,1,1,0)
y :: Unitary
y I = (Plus,I)
y X = (Minus,X)
y Y = (Plus,Y)
y Z = (Minus,Z)
y_min :: MinUnitary
y_min Xmin = (Minus,X)
y_min Zmin = (Minus,Z)
y' :: Unitary
y' = from_minimal y_min
y'' :: Unitary
y'' = from_matrix (0,-i,i,0)
z :: Unitary
z I = (Plus,I)
z X = (Minus,X)
z Y = (Minus,Y)
z Z = (Plus,Z)
z_min :: MinUnitary
z_min Xmin = (Minus,X)
z_min Zmin = (Plus,Z)
z' :: Unitary
z' = from_minimal z_min
z'' :: Unitary
z'' = from_matrix (1,0,0,-1)
h :: Unitary
h I = (Plus,I)
h X = (Plus,Z)
h Y = (Minus,Y)
h Z = (Plus,X)
h_min :: MinUnitary
h_min Xmin = (Plus,Z)
h_min Zmin = (Plus,X)
h' :: Unitary
h' = from_minimal h_min
h'' :: Unitary
h'' = from_matrix $ scale1 (Cplx (1/sqrt 2) 0) (1,1,1,-1)
s :: Unitary
s I = (Plus,I)
s X = (Plus,Y)
s Y = (Minus,X)
s Z = (Plus,Z)
s_min :: MinUnitary
s_min Xmin = (Plus,Y)
s_min Zmin = (Plus,Z)
s' :: Unitary
s' = from_minimal s_min
s'' :: Unitary
s'' = from_matrix (1,0,0,i)
e :: Unitary
e I = (Plus,I)
e X = (Plus,Y)
e Y = (Plus,Z)
e Z = (Plus,X)
e_min :: MinUnitary
e_min Xmin = (Plus,Y)
e_min Zmin = (Plus,X)
e' :: Unitary
e' = from_minimal e_min
e'' :: Unitary
e'' = from_matrix ((-1+i)/2, (1+i)/2, (-1+i)/2, (-1-i)/2)
cnot :: Unitary2
cnot (I,I) = (Plus,I,I)
cnot (I,X) = (Plus,I,X)
cnot (I,Y) = (Plus,Z,Y)
cnot (I,Z) = (Plus,Z,Z)
cnot (X,I) = (Plus,X,X)
cnot (X,X) = (Plus,X,I)
cnot (X,Y) = (Plus,Y,Z)
cnot (X,Z) = (Minus,Y,Y)
cnot (Y,I) = (Plus,Y,X)
cnot (Y,X) = (Plus,Y,I)
cnot (Y,Y) = (Minus,X,Z)
cnot (Y,Z) = (Plus,X,Y)
cnot (Z,I) = (Plus,Z,I)
cnot (Z,X) = (Plus,Z,X)
cnot (Z,Y) = (Plus,I,Y)
cnot (Z,Z) = (Plus,I,Z)
cnot_min :: MinUnitary2
cnot_min IX = (Plus,I,X)
cnot_min XI = (Plus,X,X)
cnot_min IZ = (Plus,Z,Z)
cnot_min ZI = (Plus,Z,I)
cnot' :: Unitary2
cnot' = from_minimal2 cnot_min
cnot'' :: Unitary2
cnot'' = from_matrix2 ((1,0,0,1),(0,0,0,0),(0,0,0,0),(0,1,1,0))
cz :: Unitary2
cz (I,I) = (Plus,I,I)
cz (I,X) = (Plus,Z,X)
cz (I,Y) = (Plus,Z,Y)
cz (I,Z) = (Plus,I,Z)
cz (X,I) = (Plus,X,Z)
cz (X,X) = (Plus,Y,Y)
cz (X,Y) = (Minus,Y,X)
cz (X,Z) = (Plus,X,I)
cz (Y,I) = (Plus,Y,Z)
cz (Y,X) = (Minus,X,Y)
cz (Y,Y) = (Plus,X,X)
cz (Y,Z) = (Plus,Y,I)
cz (Z,I) = (Plus,Z,I)
cz (Z,X) = (Plus,I,X)
cz (Z,Y) = (Plus,I,Y)
cz (Z,Z) = (Plus,Z,Z)
cz_min :: MinUnitary2
cz_min IX = (Plus,Z,X)
cz_min XI = (Plus,X,Z)
cz_min IZ = (Plus,I,Z)
cz_min ZI = (Plus,Z,I)
cz' :: Unitary2
cz' = from_minimal2 cz_min
cz'' :: Unitary2
cz'' = from_matrix2 ((1,0,0,1),(0,0,0,0),(0,0,0,0),(1,0,0,-1))
type CliffordCirc a = StateT Tableau IO a
init_qubit :: Bool -> CliffordCirc Qubit
init_qubit b = do
tab <- get
let nq = next_qubit tab
put (add_qubit b tab)
return nq
init_qubits :: [Bool] -> CliffordCirc [Qubit]
init_qubits = mapM init_qubit
gate_X :: Qubit -> CliffordCirc ()
gate_X q = do
tab <- get
put (apply_unitary x q tab)
gate_Y :: Qubit -> CliffordCirc ()
gate_Y q = do
tab <- get
put (apply_unitary y q tab)
gate_Z :: Qubit -> CliffordCirc ()
gate_Z q = do
tab <- get
put (apply_unitary z q tab)
gate_H :: Qubit -> CliffordCirc ()
gate_H q = do
tab <- get
put (apply_unitary h q tab)
gate_S :: Qubit -> CliffordCirc ()
gate_S q = do
tab <- get
put (apply_unitary s q tab)
gate_Unitary :: Unitary -> Qubit -> CliffordCirc ()
gate_Unitary u q = do
tab <- get
put (apply_unitary u q tab)
controlled_X :: Qubit -> Qubit -> CliffordCirc ()
controlled_X q1 q2 = do
tab <- get
put (apply_unitary2 cnot (q1,q2) tab)
controlled_Z :: Qubit -> Qubit -> CliffordCirc ()
controlled_Z q1 q2 = do
tab <- get
put (apply_unitary2 cz (q1,q2) tab)
gate_Unitary2 :: Unitary2 -> Qubit -> Qubit -> CliffordCirc ()
gate_Unitary2 u q1 q2 = do
tab <- get
put (apply_unitary2 u (q1,q2) tab)
measure_qubit :: Qubit -> CliffordCirc Bool
measure_qubit q = do
tab <- get
(res,tab') <- lift $ measure q tab
put tab'
return res
measure_qubits :: [Qubit] -> CliffordCirc [Bool]
measure_qubits = mapM measure_qubit
show_tableau :: CliffordCirc ()
show_tableau = do
tab <- get
lift $ putStrLn (show tab)
eval :: CliffordCirc a -> IO Tableau
eval cc = execStateT cc empty_tableau
sim :: CliffordCirc a -> IO a
sim cc = evalStateT cc empty_tableau
swap :: Qubit -> Qubit -> CliffordCirc ()
swap q1 q2 = do
controlled_X q1 q2
controlled_X q2 q1
controlled_X q1 q2
controlled_Z' :: Qubit -> Qubit -> CliffordCirc ()
controlled_Z' q1 q2 = do
gate_H q2
controlled_X q1 q2
gate_H q2
bell :: (Bool,Bool) -> CliffordCirc (Qubit,Qubit)
bell (bx,by) = do
x <- init_qubit bx
y <- init_qubit by
gate_H x
controlled_X x y
return (x,y)
measure_bell00 :: CliffordCirc (Bool,Bool)
measure_bell00 = do
(bx,by) <- bell (False,False)
mx <- measure_qubit bx
my <- measure_qubit by
return (mx,my)
controlled_if :: Bool -> (Qubit -> CliffordCirc ()) -> Qubit -> CliffordCirc ()
controlled_if b u q = if b then u q else return ()
teleport :: Qubit -> CliffordCirc Qubit
teleport q1 = do
(q2,q3) <- bell (False,False)
controlled_X q1 q2
gate_H q1
[b1,b2] <- measure_qubits [q1,q2]
controlled_if b2 gate_X q3
controlled_if b1 gate_Z q3
return q3
test_teleport :: Bool -> CliffordCirc Bool
test_teleport b = do
q <- init_qubit b
q' <- teleport q
measure_qubit q'
random_bool :: CliffordCirc Bool
random_bool = do
q <- init_qubit False
gate_H q
measure_qubit q