{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LINE 1 "Quipper/Libraries/Synthesis.hs" #-}
{-# LANGUAGE DoAndIfThenElse #-}
module Quipper.Libraries.Synthesis where
import Quipper
import Quipper.Internal
import Quantum.Synthesis.CliffordT
import Quantum.Synthesis.MultiQubitSynthesis
import Quantum.Synthesis.Ring
import Quantum.Synthesis.Matrix
import Quantum.Synthesis.Newsynth
import Quantum.Synthesis.SymReal
import Quantum.Synthesis.EulerAngles
import Quipper.Utils.Auxiliary (boollist_of_int_bh)
import Control.Monad (when)
import Data.Bits (xor, (.&.))
import System.Random
import Text.Printf
type Precision = Double
bits :: Precision
bits = 1
digits :: Precision
digits = logBase 2 10
type KeepPhase = Bool
apply_gate_at :: Gate -> Qubit -> Circ ()
apply_gate_at X q = do
gate_X_at q
apply_gate_at Y q = do
gate_Y_at q
apply_gate_at Z q = do
gate_Z_at q
apply_gate_at H q = do
hadamard_at q
apply_gate_at S q = do
gate_S_at q
apply_gate_at T q = do
gate_T_at q
apply_gate_at E q = do
gate_E_at q
apply_gate_at W q = do
gate_omega_at q
apply_gates_at :: [Gate] -> Qubit -> Circ ()
apply_gates_at gates q = do
sequence_ [ apply_gate_at g q | g <- reverse gates ]
apply_gates2_at :: [Gate] -> Qubit -> Qubit -> Circ ()
apply_gates2_at gates q1 q2 = do
sequence_ [ do
apply_gate_at g q1
apply_gate_at g q2
| g <- reverse gates ]
twolevel :: Index -> Index -> [Qubit] -> (Qubit -> Circ ()) -> Circ ()
twolevel i j qlist body = aux l1 l2 qlist where
n = length qlist
l1 = boollist_of_int_bh n i
l2 = boollist_of_int_bh n j
aux [] [] [] = error "twolevel: i=j"
aux (h1:t1) (h2:t2) (q:qs)
| h1 == h2 =
with_controls (q .==. h1) $ do
aux t1 t2 qs
| h1 == True =
with_basis_change (qnot_at q) $ do
aux2 t1 t2 q qs
| otherwise =
aux2 t1 t2 q qs
aux _ _ _ = error "twolevel: internal error 1"
aux2 [] [] q [] =
body q
aux2 (h1:t1) (h2:t2) q0 (q:qs)
| h1 == h2 =
with_controls (q .==. h1) $ do
aux2 t1 t2 q0 qs
| otherwise =
with_basis_change (qnot_at q `controlled` q0) $ do
with_controls (q .==. h1) $ do
aux2 t1 t2 q0 qs
aux2 _ _ _ _ = error "twolevel: internal error 2"
gate_T_power_at :: Int -> Qubit -> Circ ()
gate_T_power_at 0 q = do
return ()
gate_T_power_at 1 q = do
gate_T_at q
gate_T_power_at 2 q = do
gate_S_at q
gate_T_power_at 3 q = do
gate_S_at q
gate_T_at q
gate_T_power_at 4 q = do
gate_Z_at q
gate_T_power_at 5 q = do
gate_Z_at q
gate_T_at q
gate_T_power_at 6 q = do
gate_S_inv_at q
gate_T_power_at 7 q = do
gate_T_inv_at q
gate_T_power_at m q = gate_T_power_at (m `mod` 8) q
apply_twolevel_at :: TwoLevel -> [Qubit] -> Circ ()
apply_twolevel_at (TL_X i j) qlist =
twolevel i j qlist $ \q -> do
gate_X_at q
apply_twolevel_at (TL_H i j) qlist =
twolevel i j qlist $ \q -> do
hadamard_at q
apply_twolevel_at (TL_T m i j) qlist
| m `mod` 8 == 0 = return ()
| otherwise =
twolevel i j qlist $ \q -> do
gate_T_power_at m q
apply_twolevel_at (TL_omega m i) qlist
| m' == 0 = do
return ()
| qlist == [] = do
global_phase (fromIntegral m' * 0.25)
| otherwise =
apply_twolevel_at (TL_T m j i) qlist
where
j = if i == 0 then 1 else i .&. (i-1)
m' = m `mod` 8
apply_twolevels_at :: [TwoLevel] -> [Qubit] -> Circ ()
apply_twolevels_at ops qlist =
sequence_ [ apply_twolevel_at g qlist | g <- reverse ops ]
apply_twolevel_alt_at :: TwoLevelAlt -> [Qubit] -> Circ ()
apply_twolevel_alt_at (TL_iX j l) qlist = do
twolevel j l qlist $ \q -> do
gate_iX_at q
apply_twolevel_alt_at (TL_TiHT m j l) qlist = do
twolevel j l qlist $ \q -> do
let basischange = do
gate_T_power_at m q
gate_S_at q
gate_H_at q
gate_T_at q
with_basis_change basischange $ do
gate_iX_at q
apply_twolevel_alt_at (TL_W m j l) qlist
| m' == 0 = do
return ()
| otherwise = do
twolevel j l qlist $ \q -> do
gate_iX_at q
let basischange = do
gate_T_power_at m' q
with_basis_change basischange $ do
gate_iX_inv_at q
where
m' = m `mod` 8
apply_twolevel_alt_at (TL_omega_alt m j) qlist
| m' == 0 = do
return ()
| qlist == [] = do
global_phase (fromIntegral m' * 0.25)
| otherwise = do
apply_twolevel_at (TL_T m l j) qlist
where
l = if j == 0 then 1 else j .&. (j-1)
m' = m `mod` 8
apply_twolevels_alt_at :: [TwoLevelAlt] -> [Qubit] -> Circ ()
apply_twolevels_alt_at ops qlist =
sequence_ [ apply_twolevel_alt_at g qlist | g <- reverse ops ]
exact_synthesis1 :: (ToGates a) => a -> Qubit -> Circ Qubit
exact_synthesis1 op q = do
comment_with_label "ENTER: exact_synthesis1" q "q"
apply_gates_at gates q
comment_with_label "EXIT: exact_synthesis1" q "q"
return q
where
gates = convert (normalize op) :: [Gate]
exact_synthesis :: (ToQOmega a, Nat n) => Matrix n n a -> [Qubit] -> Circ [Qubit]
exact_synthesis op qlist = do
comment_with_label "ENTER: exact_synthesis" qlist "q"
apply_twolevels_at twolevels qlist
comment_with_label "EXIT: exact_synthesis" qlist "q"
return qlist
where
op_DOmega = matrix_map (to_dyadic . toQOmega) op
twolevels = synthesis_nqubit op_DOmega
exact_synthesis_alt :: (ToQOmega a, Nat n) => Matrix n n a -> [Qubit] -> Circ [Qubit]
exact_synthesis_alt op qlist = do
comment_with_label "ENTER: exact_synthesis_alt" qlist "q"
apply_twolevels_alt_at twolevels qlist
comment_with_label "EXIT: exact_synthesis_alt" qlist "q"
return qlist
where
op_DOmega = matrix_map (to_dyadic . toQOmega) op
twolevels = synthesis_nqubit_alt op_DOmega
approximate_synthesis_zrot :: (RandomGen g) => Precision -> SymReal -> g -> Qubit -> Circ Qubit
approximate_synthesis_zrot b theta g q = do
comment_with_label (printf "ENTER: approximate_synthesis_zrot (b=%.2f, theta=%s)" b (show theta)) q "q"
q <- without_comments $ do
exact_synthesis1 op q
comment_with_label "EXIT: approximate_synthesis_zrot" q "q"
return q
where
op = newsynth b theta g
approximate_synthesis_phase :: (RandomGen g) => KeepPhase -> Precision -> SymReal -> g -> Circ ()
approximate_synthesis_phase False b theta g = do
return ()
approximate_synthesis_phase True b theta g = do
comment (printf "ENTER: approximate_synthesis_phase (b=%.2f, theta=%s)" b (show theta))
when (gates /= []) $ do
q <- qinit 0
apply_gates_at gates q
qterm 0 q
comment "EXIT: approximate_synthesis_phase"
where
op = newsynth b (-2*theta) g
gates = convert (normalize op) :: [Gate]
approximate_synthesis_euler :: (RandomGen g) => KeepPhase -> Precision -> (SymReal, SymReal, SymReal, SymReal) -> g -> Qubit -> Circ Qubit
approximate_synthesis_euler keepphase b (alpha, beta, gamma, delta) g q = do
comment_with_label (printf "ENTER: approximate_synthesis_euler (b=%.2f, alpha=%s, beta=%s, gamma=%s, delta=%s, keepphase=%s)" b (show alpha) (show beta) (show gamma) (show delta) (show keepphase)) q "q"
without_comments $ do
exact_synthesis1 (op_beta * u2_H * op_gamma * u2_H * op_delta) q
approximate_synthesis_phase keepphase b' alpha g1
comment_with_label "EXIT: approximate_synthesis_euler" q "q"
return q
where
op_beta = newsynth b' beta g2
op_gamma = newsynth b' gamma g3
op_delta = newsynth b' delta g4
(g', g'') = split g
(g1, g2) = split g'
(g3, g4) = split g''
b' = b + 2
approximate_synthesis_u2 :: (RandomGen g) => KeepPhase -> Precision -> U2 (Cplx SymReal) -> g -> Qubit -> Circ Qubit
approximate_synthesis_u2 keepphase b op g q = do
comment_with_label (printf "ENTER: approximate_synthesis_u2 (b=%.2f, op=%s, keepphase=%s)" b (show op) (show keepphase)) q "q"
q <- without_comments $ do
approximate_synthesis_euler keepphase b (alpha, beta, gamma, delta) g q
comment_with_label "EXIT: approximate_synthesis_u2" q "q"
return q
where
(alpha, beta, gamma, delta) = euler_angles op
approximate_synthesis_zrot_ctrl :: (RandomGen g) => Precision -> SymReal -> g -> Qubit -> Qubit -> Circ Qubit
approximate_synthesis_zrot_ctrl b theta g q1 c2 = do
comment_with_label (printf "ENTER: approximate_synthesis_zrot_ctrl (b=%.2f, theta=%s)" b (show theta)) (q1,c2) ("q", "c")
qnot_at c2 `controlled` q1 .==. False
apply_gates2_at gates q1 c2
qnot_at c2 `controlled` q1 .==. False
comment_with_label "EXIT: approximate_synthesis_zrot_ctrl" (q1,c2) ("q", "c")
return q1
where
gates = convert (normalize op) :: [Gate]
op = newsynth (b+1) (theta/2) g
approximate_synthesis_phase_ctrl :: (RandomGen g) => KeepPhase -> Precision -> SymReal -> g -> Qubit -> Circ Qubit
approximate_synthesis_phase_ctrl keepphase b theta g q1 = do
comment_with_label (printf "ENTER: approximate_synthesis_phase_ctrl (b=%.2f, theta=%s keepphase=%s)" b (show theta) (show keepphase)) q1 "q"
if keepphase then do
q2 <- qinit True
apply_gates2_at gates q2 q1
qterm True q2
else do
approximate_synthesis_zrot b theta g q1
return ()
comment_with_label "EXIT: approximate_synthesis_phase_ctrl" q1 "q"
return q1
where
gates = convert (normalize op) :: [Gate]
op = newsynth (b+1) theta g