-- | This library provides various transformers and functions for -- performing dynamic liftings based on different mechanisms. Some use -- simulations to do the dynamic liftings; others just do them -- randomly. There are also functions for printing the simulated -- circuit. -- -- This code is experimental. module Quipper.Libraries.DynamicLiftings where import Quipper -- The following is a bunch of stuff we need to import because, -- temporarily, DynamicLiftings.hs uses low-level interfaces. It -- should be re-implemented using only high-level interfaces, or in -- some cases, more stuff should be exported from Quipper.hs. 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) -- we use the state monad to hold our \"quantum\" state import Control.Monad.State -- we use a random number generator to simulate \"quantum randomness\" import System.Random hiding (split) -- we store \"basis\" states as a map, --i.e., Map Qubit Bool represents a basis state. import Data.Map (Map) import qualified Data.Map as Map -- ====================================================================== -- * Random Dynamic Liftings and Liftings from a List -- | A State monad that holds a random generator. type RandomCirc a = StateT StdGen Circ a -- | A State monad that holds a list of booleans. type ListCirc a = StateT [Bool] Circ a -- | To evaluate a 'RandomCirc' we require a seed for the random generator. evalRandomCirc :: Int -> RandomCirc a -> Circ a evalRandomCirc seed rc = evalStateT rc (mkStdGen seed) -- | To evaluate a 'ListCirc' we require a list of booleans. evalListCirc :: [Bool] -> ListCirc a -> Circ a evalListCirc bools lc = evalStateT lc bools -- | Lift 'evalRandomCirc' to unary random circuit generating functions. evalRandomCirc_unary :: Int -> (a -> RandomCirc b) -> a -> Circ b evalRandomCirc_unary seed rcirc input = evalRandomCirc seed (rcirc input) -- | Left 'evalListCirc' to unary list circuit generating functions. evalListCirc_unary :: [Bool] -> (a -> ListCirc b) -> a -> Circ b evalListCirc_unary bools lcirc input = evalListCirc bools (lcirc input) -- | Lift the underlying 'randomR' function into the RandomCirc monad. 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 a RandomCirc by evaluating it with a seed in the IO monad. 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 a LiftCirc by evaluating it in the IO Monad, so as to read -- in a given number of booleans. 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 -- | Lift the 'identity_transformer' using any monad transformer. 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 () -- | Dynamic lifting can make use of a random result (without caring which wire is -- being lifted). random_dynamic_lift :: Bit -> RandomCirc Bool random_dynamic_lift _ = randomRRandomCirc (False,True) -- | Dynamic lifting can pop the head off of a list of booleans, and use that to -- lift the given wire. 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 -- | A dynamic transformer which is the identity transformer (lifted to -- RandomCirc), except for the dynamic lifting operation. 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 } -- | A dynamic transformer which is the identity transformer (lifted to -- ListCirc), except for the dynamic lifting operation. 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 a circuit, using random dynamic liftings. 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 a circuit, using a list of dynamic liftings. 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 -- ====================================================================== -- * Simulating the Dynamic Liftings -- | Add state to the Circ Monad so that we can simulate the circuit -- and use that data for dynamic liftings. data SimulationState = SS { s_quantum_state :: Amplitudes Double, s_classical_state :: Map Bit Bool, s_namespace :: Namespace, -- we need a namespace to keep track of subroutines s_rng :: StdGen } -- | When we start a simulation, we need an empty starting state, with -- a seed for the generator. 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} -- | A State monad that holds our SimulationState. type SimulatedCirc a = StateT SimulationState Circ a -- | Evaluate a 'SimulatedCirc'. This requires a seed for the random generator. evalSimulatedCirc :: Int -> SimulatedCirc a -> Circ a evalSimulatedCirc seed sc = evalStateT sc (empty_simulation_state seed) -- | Lift 'evalSimulatedCirc' to unary functions. evalSimulatedCirc_unary :: Int -> (a -> SimulatedCirc b) -> a -> Circ b evalSimulatedCirc_unary seed scirc input = evalSimulatedCirc seed (scirc input) -- | Lift the underlying 'randomR' function into the SimulatedCirc monad. 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 -- | A specialized put function for the quantum state that uses the current state -- instead of a previously retrieved state. putQS :: Amplitudes Double -> SimulatedCirc () putQS amps = do state <- get put (state {s_quantum_state = amps}) -- | A specialized put function for the classical state that uses the current state -- instead of a previously retrieved state. putCS :: Map Bit Bool -> SimulatedCirc () putCS bits = do state <- get put (state {s_classical_state = bits}) -- | It doesn't make sense having a quantum control on a classical gate, so -- we can throw an error if that is the case, and just lookup the boolean -- result otherwise. 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" -- | Map the 's_classical_control' function to all the controls, and take the -- 'and' of the result. s_classical_controls :: Map Bit Bool -> Ctrls Qubit Bit -> Bool s_classical_controls bits cs = and (map (s_classical_control bits) cs) -- | When we want a quantum control, we will be working with one "basis state" at -- a time, and can look up the qubit's value in that basis state to see whether -- the control fires. 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 -- | Map the 's_qc_control' function to all the controls (under the given basis -- state), and take the 'and' of the result. 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) -- | Apply the given function only if the controls fire. 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)] -- | The 'simulated_lift_transformer' is the actual transformer that does the -- simulation, while recreating the circuit. simulated_lift_transformer :: Transformer (StateT SimulationState Circ) Qubit Bit -- Translation of classical gates: 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" -- Translation of quantum gates: 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 () -- a discard is essentially a measurement, with the result thrown away, so we -- do that here, as it will reduce the size of the quantum state we are -- simulating over. 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 -- with a real quantum computer, when we terminate a qubit with an assertion -- we have no way of actually checking the assertion. The best we can do is -- measure the qubit and then throw an error if the assertion is incorrect, -- which may only occur with a small probability. Here, we are able to split -- the quantum state and see if the qubit exists in the incorrect state with -- any non-zero probability, and throw an error. 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 ++ ")" -- | Dynamic lifting can make use of a simulated result. 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" -- | A dynamic transformer which simulates the circuit, whilst -- reconstructing it with simulated lifting results. -- Note: do not handle classical controlling. 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 a RandomCirc by evaluating it with a seed in the IO monad. print_simulated :: Format -> SimulatedCirc b -> IO () print_simulated format scirc = do seed <- randomIO let circ = evalSimulatedCirc seed scirc print_unary format (\() -> circ) () -- | Print a circuit, using simulated liftings. 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' () -- | Pass a (possibly) dynamic circuit through the -- 'simulated_dynamic_lift_transformer' and evaluate the liftings so as to -- leave us with a static circuit that represents a single run of the original -- circuit, with the given inputs. We also need to pass in a seed for the RNG. 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 ())