{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LINE 1 "Quipper/Libraries/DynamicLiftings.hs" #-}
module Quipper.Libraries.DynamicLiftings where
import Quipper
import Quipper.Internal.Circuit (Namespace, namespace_empty, TypedSubroutine(..), OCircuit(..), reverse_ocircuit, showNames)
import Quipper.Internal (BType)
import Quipper.Internal.Transformer
import Quipper.Internal.Monad
import Quipper.Internal.Generic (transform_unary_dynamic_shape)
import Quipper.Internal.Printing (getBit)
import Quipper.Libraries.Simulation.QuantumSimulation
import Quipper.Utils.Auxiliary (map_provide)
import Control.Monad.State
import System.Random hiding (split)
import Data.Map (Map)
import qualified Data.Map as Map
type RandomCirc a = StateT StdGen Circ a
type ListCirc a = StateT [Bool] Circ a
evalRandomCirc :: Int -> RandomCirc a -> Circ a
evalRandomCirc seed rc = evalStateT rc (mkStdGen seed)
evalListCirc :: [Bool] -> ListCirc a -> Circ a
evalListCirc bools lc = evalStateT lc bools
evalRandomCirc_unary :: Int -> (a -> RandomCirc b) -> a -> Circ b
evalRandomCirc_unary seed rcirc input = evalRandomCirc seed (rcirc input)
evalListCirc_unary :: [Bool] -> (a -> ListCirc b) -> a -> Circ b
evalListCirc_unary bools lcirc input = evalListCirc bools (lcirc input)
randomRRandomCirc :: Random a => (a,a) -> RandomCirc a
randomRRandomCirc (a0,a1) = do
stdgen <- get
let (random_a,stdgen') = randomR (a0,a1) stdgen
put stdgen'
return random_a
print_unary_random :: (QCData qa) => Format -> (qa -> RandomCirc b) -> qa -> IO ()
print_unary_random format rcirc input = do
seed <- randomIO
let circ = evalRandomCirc_unary seed rcirc
print_unary format circ input
print_unary_list :: (QCData qa) => Format -> Int -> (qa -> ListCirc b) -> qa -> IO ()
print_unary_list format liftings lcirc input = do
bools <- mapM (\() -> getBit) (replicate liftings ())
let circ = evalListCirc_unary bools lcirc
print_unary format circ input
lifted_identity_transformer :: (MonadTrans t) => Transformer (t Circ) Qubit Bit
lifted_identity_transformer (T_QGate "not" 1 0 _ ncf f) = f $
\[q] [] cs -> lift $ without_controls_if ncf $ do
q' <- qnot q `controlled` cs
return ([q'], [] ,cs)
lifted_identity_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $
\ws [] cs -> lift $ without_controls_if ncf $ do
ws' <- qmultinot_list (map (\x -> (x,True)) ws) `controlled` cs
return (ws', [], cs)
lifted_identity_transformer (T_QGate "H" 1 0 _ ncf f) = f $
\[q] [] cs -> lift $ without_controls_if ncf $ do
q' <- hadamard q `controlled` cs
return ([q'], [], cs)
lifted_identity_transformer (T_QGate "swap" 2 0 _ ncf f) = f $
\[w,v] [] cs -> lift $ without_controls_if ncf $ do
(w',v') <- swap_qubit w v `controlled` cs
return ([w',v'], [], cs)
lifted_identity_transformer (T_QGate "W" 2 0 _ ncf f) = f $
\[w,v] [] cs -> lift $ without_controls_if ncf $ do
(w',v') <- gate_W w v `controlled` cs
return ([w',v'], [], cs)
lifted_identity_transformer (T_QGate name _ _ inv ncf f) = f $
\ws vs c -> lift $ without_controls_if ncf $ do
(ws', vs') <- named_gate_qulist name inv ws vs `controlled` c
return (ws', vs', c)
lifted_identity_transformer (T_QRot name _ _ inv t ncf f) = f $
\ws vs c -> lift $ without_controls_if ncf $ do
(ws', vs') <- named_rotation_qulist name inv t ws vs `controlled` c
return (ws', vs', c)
lifted_identity_transformer (T_GPhase t ncf f) = f $
\qs c -> lift $ without_controls_if ncf $ do
global_phase_anchored_list t qs `controlled` c
return c
lifted_identity_transformer (T_CNot ncf f) = f $
\q c -> lift $ without_controls_if ncf $ do
q' <- cnot q `controlled` c
return (q', c)
lifted_identity_transformer (T_CGate name ncf f) = f $
\ws -> lift $ without_controls_if ncf $ do
v <- cgate name ws
return (v, ws)
lifted_identity_transformer (T_CGateInv name ncf f) = f $
\v ws -> lift $ without_controls_if ncf $ do
cgateinv name v ws
return ws
lifted_identity_transformer (T_CSwap ncf f) = f $
\w v c -> lift $ without_controls_if ncf $ do
(w',v') <- swap_bit w v `controlled` c
return (w',v',c)
lifted_identity_transformer (T_QPrep ncf f) = f $
\w -> lift $ without_controls_if ncf $ do
v <- prepare_qubit w
return v
lifted_identity_transformer (T_QUnprep ncf f) = f $
\w -> lift $ without_controls_if ncf $ do
v <- unprepare_qubit w
return v
lifted_identity_transformer (T_QInit b ncf f) = f $
lift $ without_controls_if ncf $ do
w <- qinit_qubit b
return w
lifted_identity_transformer (T_CInit b ncf f) = f $
lift $ without_controls_if ncf $ do
w <- cinit_bit b
return w
lifted_identity_transformer (T_QTerm b ncf f) = f $
\w -> lift $ without_controls_if ncf $ do
qterm_qubit b w
return ()
lifted_identity_transformer (T_CTerm b ncf f) = f $
\w -> lift $ without_controls_if ncf $ do
cterm_bit b w
return ()
lifted_identity_transformer (T_QMeas f) = f $
\w -> lift $ do
v <- measure_qubit w
return v
lifted_identity_transformer (T_QDiscard f) = f $
\w -> lift $ do
qdiscard_qubit w
return ()
lifted_identity_transformer (T_CDiscard f) = f $
\w -> lift $ do
cdiscard_bit w
return ()
lifted_identity_transformer (T_DTerm b f) = f $
\w -> lift $ do
dterm_bit b w
return ()
lifted_identity_transformer (T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $
\ns ws c -> lift $ without_controls_if ncf $ do
vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c
return (vs,c)
lifted_identity_transformer (T_Comment s inv f) = f $
\ws -> lift $ do
comment_label s inv [ (wire_of_endpoint e, s) | (e,s) <- ws ]
return ()
random_dynamic_lift :: Bit -> RandomCirc Bool
random_dynamic_lift _ = randomRRandomCirc (False,True)
list_dynamic_lift :: Bit -> ListCirc Bool
list_dynamic_lift _ = do
xs <- get
case xs of
[] -> error "ListCirc list of liftings exhausted"
(x:xs') -> do
put xs'
return x
random_dynamic_lift_transformer :: DynamicTransformer (StateT StdGen Circ) Qubit Bit
random_dynamic_lift_transformer = DT {
transformer = lifted_identity_transformer,
define_subroutine = \name subroutine -> do
lift $ put_subroutine_definition name subroutine,
lifting_function = random_dynamic_lift
}
list_dynamic_lift_transformer :: DynamicTransformer (StateT [Bool] Circ) Qubit Bit
list_dynamic_lift_transformer = DT {
transformer = lifted_identity_transformer,
define_subroutine = \name subroutine -> do
lift $ put_subroutine_definition name subroutine,
lifting_function = list_dynamic_lift
}
print_unary_with_random_liftings :: (QCData a,QCData b) => Format -> (a -> Circ b) -> a -> IO ()
print_unary_with_random_liftings format circ shape = do
let lifted_circ = transform_unary_dynamic_shape random_dynamic_lift_transformer circ shape
print_unary_random format lifted_circ shape
print_unary_with_list_liftings :: (QCData a,QCData b) => Format -> Int -> (a -> Circ b) -> a -> IO ()
print_unary_with_list_liftings format liftings circ shape = do
let lifted_circ = transform_unary_dynamic_shape list_dynamic_lift_transformer circ shape
print_unary_list format liftings lifted_circ shape
data SimulationState = SS {
s_quantum_state :: Amplitudes Double,
s_classical_state :: Map Bit Bool,
s_namespace :: Namespace,
s_rng :: StdGen
}
empty_simulation_state :: Int -> SimulationState
empty_simulation_state seed = SS { s_quantum_state = Vector [(Map.empty,1.0)], s_classical_state = Map.empty, s_namespace = namespace_empty, s_rng = mkStdGen seed}
type SimulatedCirc a = StateT SimulationState Circ a
evalSimulatedCirc :: Int -> SimulatedCirc a -> Circ a
evalSimulatedCirc seed sc = evalStateT sc (empty_simulation_state seed)
evalSimulatedCirc_unary :: Int -> (a -> SimulatedCirc b) -> a -> Circ b
evalSimulatedCirc_unary seed scirc input = evalSimulatedCirc seed (scirc input)
randomRSimulatedCirc :: Random a => (a,a) -> SimulatedCirc a
randomRSimulatedCirc (a0,a1) = do
state <- get
let stdgen = s_rng state
let (random_a,stdgen') = randomR (a0,a1) stdgen
put (state {s_rng = stdgen'})
return random_a
putQS :: Amplitudes Double -> SimulatedCirc ()
putQS amps = do
state <- get
put (state {s_quantum_state = amps})
putCS :: Map Bit Bool -> SimulatedCirc ()
putCS bits = do
state <- get
put (state {s_classical_state = bits})
s_classical_control :: Map Bit Bool -> Signed (B_Endpoint Qubit Bit) -> Bool
s_classical_control bits (Signed bep val) = case bep of
(Endpoint_Bit bit) -> val == val' where val' = bits Map.! bit
(Endpoint_Qubit _) -> error "CNot: Quantum Control on Classical Gate"
s_classical_controls :: Map Bit Bool -> Ctrls Qubit Bit -> Bool
s_classical_controls bits cs = and (map (s_classical_control bits) cs)
s_qc_control :: Map Bit Bool -> Map Qubit Bool -> Signed (B_Endpoint Qubit Bit) -> Bool
s_qc_control bits mqb (Signed bep val) = case bep of
(Endpoint_Bit bit) -> val == val' where val' = bits Map.! bit
(Endpoint_Qubit q) -> val == val' where val' = mqb Map.! q
s_qc_controls :: Map Bit Bool -> Map Qubit Bool -> Ctrls Qubit Bit -> Bool
s_qc_controls bits mqb cs = and (map (s_qc_control bits mqb) cs)
s_if_controls :: Map Bit Bool -> Ctrls Qubit Bit -> (Map Qubit Bool -> Amplitudes Double) -> Map Qubit Bool -> Amplitudes Double
s_if_controls bits c f mqb = if (s_qc_controls bits mqb c) then f mqb else Vector [(mqb,1)]
simulated_lift_transformer :: Transformer (StateT SimulationState Circ) Qubit Bit
simulated_lift_transformer (T_CNot ncf f) = f $
\b c -> do
(b,c) <- lift $ without_controls_if ncf $ do
b' <- cnot b `controlled` c
return (b', c)
state <- get
let bits = s_classical_state state
let ctrl = s_classical_controls bits c
let val = bits Map.! b
let bits' = if ctrl then (Map.insert b (not val) bits) else bits
putCS bits'
return (b,c)
simulated_lift_transformer (T_CInit val ncf f) = f $
do
b <- lift $ without_controls_if ncf $ do
w <- cinit_bit val
return w
state <- get
let bits = s_classical_state state
putCS (Map.insert b val bits)
return b
simulated_lift_transformer (T_CTerm b ncf f) = f $
\w -> do
lift $ without_controls_if ncf $ do
cterm_bit b w
return ()
state <- get
let bits = s_classical_state state
let val = bits Map.! w
if val /= b then error "CTerm: Assertion Incorrect"
else do
putCS (Map.delete w bits)
simulated_lift_transformer (T_CDiscard f) = f $
\w -> do
lift $ do
cdiscard_bit w
state <- get
let bits = s_classical_state state
putCS (Map.delete w bits)
simulated_lift_transformer (T_DTerm b f) = f $
\w -> do
lift $ do
dterm_bit b w
state <- get
let bits = s_classical_state state
putCS (Map.delete w bits)
simulated_lift_transformer (T_CGate name ncf f) = f $
\ws -> do
(v,ws) <- lift $ without_controls_if ncf $ do
v <- cgate name ws
return (v, ws)
state <- get
let bits = s_classical_state state
let list = map (\w -> bits Map.! w) ws
let result = gateC name list
putCS (Map.insert v result bits)
return (v,ws)
simulated_lift_transformer g@(T_CGateInv name ncf f) = f $
\v ws -> do
ws <- lift $ without_controls_if ncf $ do
cgateinv name v ws
return ws
state <- get
let bits = s_classical_state state
let list = map (\w -> bits Map.! w) ws
let result = bits Map.! v
let result' = gateC name list
if result == result' then return ws else error "CGateInv: Uncomputation error"
simulated_lift_transformer (T_QGate "not" 1 0 _ ncf f) = f $
\[q] [] c -> do
(q,c) <- lift $ without_controls_if ncf $ do
q' <- qnot q `controlled` c
return (q', c)
let gate = gateQ "x"
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps
putQS amps'
return ([q], [], c)
simulated_lift_transformer (T_QGate "multinot" _ 0 _ ncf f) = f $
\qs [] c -> do
(qs,c) <- lift $ without_controls_if ncf $ do
qs' <- qmultinot_list (map (\x -> (x,True)) qs) `controlled` c
return (qs', c)
let gate = gateQ "x"
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = foldr (\q a -> apply (s_if_controls bits c (performGateQ gate q)) a) amps qs
putQS amps'
return (qs, [], c)
simulated_lift_transformer (T_QGate "H" 1 0 _ ncf f) = f $
\[q] [] c -> do
(q,c) <- lift $ without_controls_if ncf $ do
q' <- hadamard q `controlled` c
return (q', c)
let gate = gateQ "hadamard"
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps
putQS amps'
return ([q], [], c)
simulated_lift_transformer (T_QGate "swap" 2 0 _ ncf f) = f $
\[w, v] [] c -> do
(w,v,c) <- lift $ without_controls_if ncf $ do
(w',v') <- swap_qubit w v `controlled` c
return (w',v',c)
let gate = gateQ "x"
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gate v)) amps
let amps'' = apply (s_if_controls bits ((Signed (Endpoint_Qubit v) True):c) (performGateQ gate w)) amps'
let amps''' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gate v)) amps''
putQS amps'''
return ([w, v], [], c)
simulated_lift_transformer (T_QGate "W" 2 0 _ ncf f) = f $
\[w, v] [] c -> do
(w,v,c) <- lift $ without_controls_if ncf $ do
(w',v') <- gate_W w v `controlled` c
return (w',v',c)
let gateX = gateQ "x"
let gateH = gateQ "hadamard"
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gateX v)) amps
let amps'' = apply (s_if_controls bits ((Signed (Endpoint_Qubit v) True):c) (performGateQ gateH w)) amps'
let amps''' = apply (s_if_controls bits ((Signed (Endpoint_Qubit w) True):c) (performGateQ gateX v)) amps''
putQS amps'''
return ([w, v], [], c)
simulated_lift_transformer (T_QGate "trace" _ _ inv ncf f) = f $
\ws vs c -> lift $ without_controls_if ncf $ do
(ws', vs') <- named_gate_qulist "trace" inv ws vs `controlled` c
return (ws', vs', c)
simulated_lift_transformer (T_QGate name _ _ inv ncf f) = f $
\[q] [] c -> do
([q],[],c) <- lift $ without_controls_if ncf $ do
(ws', vs') <- named_gate_qulist name inv [q] [] `controlled` c
return (ws', vs', c)
let gate = gateQinv name inv
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps
putQS amps'
return ([q],[],c)
simulated_lift_transformer (T_QRot name _ _ inv theta ncf f) = f $
\[q] [] c -> do
([q],[],c) <- lift $ without_controls_if ncf $ do
(ws', vs') <- named_rotation_qulist name inv theta [q] [] `controlled` c
return (ws', vs', c)
let gate = rotQinv name inv theta
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits c (performGateQ gate q)) amps
putQS amps'
return ([q],[],c)
simulated_lift_transformer (T_GPhase t ncf f) = f $
\w c -> do
c <-lift $ without_controls_if ncf $ do
global_phase_anchored_list t w `controlled` c
return c
state <- get
let gate = rotQ "exp(% pi i)" t
let wire = -1
let q = qubit_of_wire wire
let amps = s_quantum_state state
let bits = s_classical_state state
let amps' = apply (s_if_controls bits c (vector (Map.insert q False))) amps
let amps'' = apply (s_if_controls bits c (performGateQ gate q)) amps'
let (p,_,ampsf) = split amps'' q
case p of
0.0 -> do
let ampsf' = apply (vector (Map.delete q)) ampsf'
putQS ampsf'
return c
_ -> error "GPhase"
simulated_lift_transformer (T_QInit val ncf f) = f $
do
q <- lift $ without_controls_if ncf $ do
w <- qinit_qubit val
return w
state <- get
let amps = s_quantum_state state
let amps' = apply (vector (Map.insert q val)) amps
putQS amps'
return q
simulated_lift_transformer (T_QMeas f) = f $
\q -> do
b <- lift $ do
b <- measure_qubit q
return b
state <- get
let amps = s_quantum_state state
let bits = s_classical_state state
let (p,ift,iff) = split amps q
pp <- randomRSimulatedCirc (0,1.0)
let (val,amps') = if p > pp then (True,ift) else (False,iff)
let amps'' = apply (vector (Map.delete q)) amps'
let bits' = Map.insert b val bits
putQS amps''
putCS bits'
return b
simulated_lift_transformer (T_QDiscard f) = f $
\q -> do
lift $ do
qdiscard_qubit q
return ()
state <- get
let (p,ift,iff) = split (s_quantum_state state) q
pp <- randomRSimulatedCirc (0,1.0)
let amps = if p > pp then ift else iff
let amps' = apply (vector (Map.delete q)) amps
putQS amps'
return ()
simulated_lift_transformer (T_QTerm b ncf f) = f $
\q -> do
lift $ without_controls_if ncf $ qterm_qubit b q
state <- get
let amps = s_quantum_state state
let (p,ampst,ampsf) = split amps q
case (b,p) of
(True,1.0) -> do
let ampst' = apply (vector (Map.delete q)) ampst
putQS ampst'
return ()
(False,0.0) -> do
let ampsf' = apply (vector (Map.delete q)) ampsf
putQS ampsf'
return ()
(True,pt) -> error ("QTerm: Assertion Incorrect (True only has probability " ++ show pt ++ ")")
(False,pt) -> error ("QTerm: Assertion Incorrect (False only has probability " ++ show (1.0 - pt) ++ ")")
simulated_lift_transformer (T_Comment name inv f) = f $
\ws -> do
lift $ do
comment_label name inv [ (wire_of_endpoint e, s) | (e,s) <- ws ]
return ()
simulated_lift_transformer g@(T_CSwap ncf f) = f $
\w v c -> do
(w,v,c) <- lift $ without_controls_if ncf $ do
(w',v') <- swap_bit w v `controlled` c
return (w',v',c)
error ("simulated_lift_transformer: unimplemented gate: " ++ show g)
simulated_lift_transformer g@(T_QPrep ncf f) = f $
\w -> do
w <- lift $ without_controls_if ncf $ do
v <- prepare_qubit w
return v
error ("simulated_lift_transformer: unimplemented gate: " ++ show g)
simulated_lift_transformer g@(T_QUnprep ncf f) = f $
\w -> do
w <- lift $ without_controls_if ncf $ do
v <- unprepare_qubit w
return v
error ("simulated_lift_transformer: unimplemented gate: " ++ show g)
simulated_lift_transformer g@(T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rep f) = f $
\ns ws c -> do
(ws,c) <- lift $ without_controls_if ncf $ do
vs <- subroutine n inv scf rep ws_pat a1 vs_pat a2 ws `controlled` c
return (vs,c)
case Map.lookup n ns of
Just (TypedSubroutine sub_ocirc _ _ _) -> do
let OCircuit (in_wires, sub_circ, out_wires) = if inv then reverse_ocircuit sub_ocirc else sub_ocirc
let in_bindings = bind_list in_wires ws bindings_empty
let sub_bcirc = (sub_circ,ns)
out_bind <- transform_bcircuit_rec simulated_lift_transformer sub_bcirc in_bindings
return (unbind_list out_bind out_wires, c)
Nothing -> error $ "simulated_lift_transformer: subroutine " ++ show n ++ " not found (in " ++ showNames ns ++ ")"
simulated_dynamic_lift :: Bit -> SimulatedCirc Bool
simulated_dynamic_lift b = do
state <- get
let bits = s_classical_state state
case Map.lookup b bits of
Just val -> return val
Nothing -> error $ "simulated_dynamic_lift: bit " ++ show b ++ " not found"
simulated_dynamic_lift_transformer :: DynamicTransformer (StateT SimulationState Circ) Qubit Bit
simulated_dynamic_lift_transformer = DT {
transformer = simulated_lift_transformer,
define_subroutine = \name subroutine -> do
lift $ do
s <- get_namespace
let s' = map_provide name subroutine s
set_namespace s'
put_subroutine_definition name subroutine,
lifting_function = simulated_dynamic_lift
}
print_simulated :: Format -> SimulatedCirc b -> IO ()
print_simulated format scirc = do
seed <- randomIO
let circ = evalSimulatedCirc seed scirc
print_unary format (\() -> circ) ()
print_unary_with_simulated_liftings :: (QCData a,QCData b) => Format -> (a -> Circ b) -> BType a -> IO ()
print_unary_with_simulated_liftings format circ input = print_simulated format (lifted_circ ())
where
circ' = \ () -> do
a <- qc_init input
circ a
lifted_circ = transform_unary_dynamic_shape simulated_dynamic_lift_transformer circ' ()
simulate_liftings_unary :: (QCData a, QCData b) => Int -> (a -> Circ b) -> BType a -> Circ b
simulate_liftings_unary seed fcirc_in input = out_circ
where
circ_in = \() -> do
a <- qc_init input
fcirc_in a
s_circ = transform_unary_dynamic_shape simulated_dynamic_lift_transformer circ_in ()
out_circ = evalSimulatedCirc seed (s_circ ())