quipper-tools-0.9.0.0: Quipper/Programs/Tools/Depth.hs
-- ----------------------------------------------------------------------
-- | This tool reads a circuit from standard input and outputs its depth.
module Main where
import Quipper
import Quipper.Internal.Circuit
import Quipper.Internal.Monad
import Quipper.Internal.Transformer
import Quipper.Utils.Auxiliary
import Quipper.Libraries.QuipperASCIIParser
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import Data.Maybe
import Control.Monad
import Data.List
import Control.Monad.State
import System.Environment
import System.Exit
import System.IO
-- | Print a usage message to 'stdout'.
usage :: IO ()
usage = do
name <- getProgName
putStr (header name)
where header name =
name ++ ": read a circuit from standard input and output its depth.\n"
-- | Main function: read from 'stdin' and calculate the depth.
main :: IO ()
main = do
argv <- getArgs
case argv of
[] -> return ()
"-h" : _ -> do
usage
exitSuccess
"--help" : _ -> do
usage
exitSuccess
o : _ -> do
hPutStrLn stderr ("Bad argument or option: '" ++ o ++ "'. Try --help for more info.")
exitFailure
(ins,circuit) <- parse_from_stdin
let depth_circ = transform_generic_shape depth_transformer circuit ins
let ws = map wire_of_endpoint ins
let ws' = map endpoint_of_endpoint ins
let state = execState (depth_circ ws') (initial_depthstate ws)
let result = global_depth state
putStrLn ("Depth: " ++ show result)
putStrLn ("T-Depth: " ++ show (global_tdepth state))
-- | Extract wires from an endpoint, keeping track of whether it was
-- a classical or quantum endpoint.
endpoint_of_endpoint :: B_Endpoint Qubit Bit -> B_Endpoint Wire Wire
endpoint_of_endpoint (Endpoint_Qubit q) = Endpoint_Qubit (wire_of_qubit q)
endpoint_of_endpoint (Endpoint_Bit b) = Endpoint_Bit (wire_of_bit b)
-- | A data structure to hold information about the current depths
data DepthState = DS {
global_depth :: !Integer,
wire_depths :: !(XIntMap Integer),
global_tdepth :: !Integer,
wire_tdepths :: !(XIntMap Integer)
}
-- | The initial state of the depth information depends on the wires
-- that are inputs to a circuit.
initial_depthstate :: [Wire] -> DepthState
initial_depthstate ws = DS { global_depth = 0, wire_depths = foldr (\w -> xintmap_insert w 0) xintmap_empty ws,
global_tdepth = 0, wire_tdepths = foldr (\w -> xintmap_insert w 0) xintmap_empty ws }
-- | We use a State Monad to carry our state
type Depth a = State DepthState a
-- | Given a list of wires, lookup the current depth
-- of each wire, and return the maximum
input_depth :: [Wire] -> Depth (Integer,Integer)
input_depth ws = do
state <- get
let wd = wire_depths state
let ds = map (\w -> fromJust (xintmap_lookup w wd)) ws
let maxds =
case ds of
[] -> 0
ds -> maximum ds
let wtd = wire_tdepths state
let tds = map (\w -> fromJust (xintmap_lookup w wtd)) ws
let maxtds =
case tds of
[] -> 0
tds -> maximum tds
return (maxds, maxtds)
-- | Given a current depth, and a list of wires, update
-- the depth of each wire, to be one more than the current
-- depth. Also, update the global depth, if necessary.
update_depths :: (Integer,Integer) -> [Wire] -> Bool -> Depth ()
update_depths (old_depth,old_tdepth) ws addt = do
let new_depth = old_depth + 1
state <- get
let gd = global_depth state
let gd' = if gd < new_depth then new_depth else gd
let wd = wire_depths state
let wd' = foldr (\w -> xintmap_insert w new_depth) wd ws
let new_tdepth = if addt then old_tdepth + 1 else old_tdepth
let gdt = global_tdepth state
let gdt' = if gdt < new_tdepth then new_tdepth else gdt
let wdt = wire_tdepths state
let wdt' = foldr (\w -> xintmap_insert w new_tdepth) wdt ws
put (state {global_depth = gd', wire_depths = wd', global_tdepth = gdt', wire_tdepths = wdt'})
-- | A helper function to combine a list of wires, and
-- a list of controls, into a single list of wires.
wires_of :: [Wire] -> Ctrls Wire Wire -> [Wire]
wires_of ws c = ws ++ (map wire_of_ctrl c)
where
wire_of_ctrl :: Signed (B_Endpoint Wire Wire) -> Wire
wire_of_ctrl (Signed (Endpoint_Qubit w) _) = w
wire_of_ctrl (Signed (Endpoint_Bit w) _) = w
same_in_out :: Wire -> Ctrls Wire Wire -> Depth (Wire,Ctrls Wire Wire)
same_in_out w c = do
let inputs = wires_of [w] c
depth <- input_depth inputs
let outputs = inputs
update_depths depth outputs False
return (w,c)
same_in_out2 :: Wire -> Wire -> Ctrls Wire Wire -> Depth (Wire,Wire,Ctrls Wire Wire)
same_in_out2 w v c = do
let inputs = wires_of [w,v] c
depth <- input_depth inputs
let outputs = inputs
update_depths depth outputs False
return (w,v,c)
same_in_out_multi_t :: [Wire] -> Ctrls Wire Wire -> Depth ([Wire],Ctrls Wire Wire)
same_in_out_multi_t ws c = do
let inputs = wires_of ws c
depth <- input_depth inputs
let outputs = inputs
update_depths depth outputs True
return (ws,c)
same_in_out_multi :: [Wire] -> Ctrls Wire Wire -> Depth ([Wire],Ctrls Wire Wire)
same_in_out_multi ws c = do
let inputs = wires_of ws c
depth <- input_depth inputs
let outputs = inputs
update_depths depth outputs False
return (ws,c)
init_wire :: [Wire] -> Depth Wire
init_wire ws = do
let inputs = ws
depth <- input_depth inputs
state <- get
let wires = wire_depths state
let b = xintmap_freshkey wires
let outputs = [b]
update_depths depth outputs False
return b
term_wire :: [Wire] -> Wire -> Depth ()
term_wire inputs w = do
depth <- input_depth inputs
let new_depth = fst(depth) + 1
state <- get
let gd = global_depth state
let gd' = if gd < new_depth then new_depth else gd
put (state {global_depth = gd'})
return ()
depth_transformer :: Transformer (State DepthState) Wire Wire
-- Translation of classical gates:
depth_transformer (T_CNot ncf f) = f same_in_out
depth_transformer (T_CInit val ncf f) = f $ init_wire []
depth_transformer (T_CTerm b ncf f) = f $ term_wire []
depth_transformer (T_CDiscard f) = f $ term_wire []
depth_transformer (T_DTerm b f) = f $ term_wire []
depth_transformer (T_CGate name ncf f) = f $
\ws -> do
v <- init_wire ws
return (v,ws)
depth_transformer g@(T_CGateInv name ncf f) = f $
\v ws -> do
term_wire ws v
return ws
-- Translation of quantum gates:
depth_transformer (T_QGate "trace" _ _ inv ncf f) = f $
\ws vs c -> return (ws, vs, c) -- don't count a trace gate
depth_transformer (T_QGate "T" _ _ inv ncf f) = f $
\qs gcs c -> do
same_in_out_multi_t (qs++gcs) c
return (qs,gcs,c)
depth_transformer (T_QGate name _ _ inv ncf f) = f $
\qs gcs c -> do
same_in_out_multi (qs++gcs) c
return (qs,gcs,c)
depth_transformer (T_QRot name _ _ inv theta ncf f) = f $
\qs gcs c -> do
same_in_out_multi (qs++gcs) c
return (qs,gcs,c)
depth_transformer (T_GPhase t ncf f) = f $
\w c -> do
same_in_out_multi [] c
return c
depth_transformer (T_QInit val ncf f) = f $ init_wire []
depth_transformer (T_QMeas f) = f $
\q -> do
same_in_out q []
return q
depth_transformer (T_QDiscard f) = f $ term_wire []
depth_transformer (T_QTerm b ncf f) = f $ term_wire []
depth_transformer (T_Comment name inv f) = f $
\ws -> return () -- don't count a comment
depth_transformer g@(T_CSwap ncf f) = f same_in_out2
depth_transformer g@(T_QPrep ncf f) = f $
\w -> error ("depth_transformer: unimplemented gate: " ++ show g)
depth_transformer g@(T_QUnprep ncf f) = f $
\w -> error ("depth_transformer: unimplemented gate: " ++ show g)
depth_transformer g@(T_Subroutine n inv ncf scf ws_pat a1 vs_pat a2 rflg f) = f $
\ns ws c -> do
case Map.lookup n ns of
Just (TypedSubroutine sub_ocirc _ _ _) -> do
let RepeatFlag reps = rflg
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)
state_in <- get
out_bind <- transform_bcircuit_rec depth_transformer sub_bcirc in_bindings
state_out <- get
put (rep_change reps state_in state_out)
return (unbind_list out_bind out_wires, c)
Nothing -> error $ "depth_transformer: subroutine " ++ show n ++ " not found (in " ++ showNames ns ++ ")"
-- | The following function updates the state by multiplying the differences between
-- the given /state_out/ and the given /state_in/ by the given number of repetitions.
rep_change :: Integer -> DepthState -> DepthState -> DepthState
rep_change reps state_in state_out = DS {
global_depth = new_global_depth,
wire_depths = new_wire_depths,
global_tdepth = new_global_tdepth,
wire_tdepths = new_wire_tdepths
}
where
in_global_depth = global_depth state_in
out_global_depth = global_depth state_out
new_global_depth = in_global_depth + (reps * (out_global_depth - in_global_depth))
in_wire_depths = wire_depths state_in
out_wire_depths = wire_depths state_out
out_wire_depths_list = IntMap.toList (xintmap_to_intmap (out_wire_depths))
new_depths = map (\(w,v_out) -> (w,
case (xintmap_lookup w in_wire_depths) of
Nothing -> v_out * reps
Just v_in ->
case v_in > v_out of
True -> reps * v_out
False -> v_in + (reps * (v_out - v_in)))) out_wire_depths_list
new_wire_depths = xintmap_inserts new_depths out_wire_depths
in_global_tdepth = global_tdepth state_in
out_global_tdepth = global_tdepth state_out
new_global_tdepth = in_global_tdepth + (reps * (out_global_tdepth - in_global_tdepth))
in_wire_tdepths = wire_tdepths state_in
out_wire_tdepths = wire_tdepths state_out
out_wire_tdepths_list = IntMap.toList (xintmap_to_intmap (out_wire_tdepths))
new_tdepths = map (\(w,v_out) -> (w,
case (xintmap_lookup w in_wire_tdepths) of
Nothing -> v_out * reps
Just v_in ->
case v_in > v_out of
True -> reps * v_out
False -> v_in + (reps * (v_out - v_in)))) out_wire_tdepths_list
new_wire_tdepths = xintmap_inserts new_tdepths out_wire_tdepths