-- ----------------------------------------------------------------------
-- | This module uses a state monad to track certain circuit information
-- that is collected during parsing. This information contains
-- the inputs and outputs of a circuit, as well as an entry for each subroutine
-- that is defined within a circuit, and all the gates that make up the circuit.
module Quipper.Libraries.QuipperASCIIParser.CircInfo where
import Quipper.Libraries.QuipperASCIIParser.Parse (GatePlus (..),parse_ascii_line)
import Quipper
import Quipper.Internal.Circuit
import Quipper.Internal.Monad
import Control.Monad.State
import Data.Map (Map)
import qualified Data.Map as Map
-- ----------------------------------------------------------------------
-- * Data-types for the State
-- | Information collected about the current subroutine
data Sub = Sub {
name :: String,
shape :: String,
controllable :: ControllableFlag,
inputs :: [(Wire,Wiretype)],
outputs :: [(Wire,Wiretype)],
circuit :: [Gate]
}
-- | An initial subroutine, with only a name
new_subroutine :: String -> Sub
new_subroutine n = Sub {name = n, shape = "", controllable = NoCtl, inputs = [], outputs = [], circuit = []}
-- | A 'CircInfoState' is a record containing a list of input wires, along with their
-- types, and a list of output wires, along with their types. We also keep track of
-- whether we're in a subroutine definition, and all the subroutines that have been
-- defined.
data CircInfoState = CircInfoState {
used_wires :: Map Wire (Maybe Wiretype),
defined_inputs :: [(Wire,Wiretype)],
undefined_inputs :: [(Wire,Maybe Wiretype)],
defined_outputs :: Maybe [(Wire,Wiretype)],
current_subroutine :: [Sub]
}
-- | An initial, empty CircInfoState
empty_circinfostate :: CircInfoState
empty_circinfostate = CircInfoState { used_wires = Map.empty, defined_inputs = [],
undefined_inputs = [], defined_outputs = Nothing, current_subroutine = [] }
-- | The 'CircInfo' monad is used to track a 'CircInfoState' during
-- parsing.
type CircInfo a = State CircInfoState a
-- | The 'CircInfo' monad tracks wires that are inputs. These can only
-- be given in an \"Input\" line in the parsed ASCII, so we assume
-- that duplicate wires don't occur, and we add input wires to the
-- state without checking.
add_wire_inputs :: [(Wire,Wiretype)] -> CircInfo ()
add_wire_inputs ws = do
state <- get
let ms = current_subroutine state
case ms of
[] -> do
let ins = defined_inputs state
let wires = used_wires state
put (state {defined_inputs = ws ++ ins,
used_wires = Map.fromList (map (\(w,wt) -> (w,Just wt)) ws)})
(sub:rest) -> do
let ins = inputs sub
let sub' = sub {inputs = ins ++ ws}
put (state {current_subroutine = (sub':rest)})
-- | The 'CircInfo' monad tracks wires that are outputs. These can
-- only be given in an \"Output\" line in the parsed ASCII, so we
-- assume that duplicate wires don't occur, and we add output wires to
-- the state without checking.
add_wire_outputs :: [(Wire,Wiretype)] -> CircInfo ()
add_wire_outputs ws = do
state <- get
let ms = current_subroutine state
case ms of
[] -> do
case defined_outputs state of
Nothing -> put (state {defined_outputs = Just ws})
Just outs -> put (state {defined_outputs = Just (outs ++ ws)})
(sub:rest) -> do
let outs = outputs sub
let sub' = sub {outputs = outs ++ ws}
put (state {current_subroutine = (sub':rest)})
-- | Given a a wire, check whether it is already in scope.
check_input :: Map Wire (Maybe Wiretype) -> (Wire,Maybe Wiretype) -> Bool
check_input wires (w,wt) = case Map.lookup w wires of
Just wt -> False
Nothing -> True
-- | Given a list of wires that are inputs to a gate, check whether they
-- are already in scope. The returned wires are not in scope, when used by a gate,
-- and must be declared as undefined inputs.
check_inputs :: [(Wire,Maybe Wiretype)] -> Map Wire (Maybe Wiretype) -> [(Wire,Maybe Wiretype)]
check_inputs ins wires = filter (check_input wires) ins
-- | The CircInfo Monad keeps track of all the gates that have been parsed
-- and adds them to the relevant part of the state.
add_gate :: Gate -> [(Wire,Wiretype)] -> CircInfo CircInfoOut
add_gate gate ctws = do
state <- get
let ms = current_subroutine state
case ms of
[] -> do
let wires = used_wires state
let ui = undefined_inputs state
let (ws_in,ws_out) = gate_arity gate
let ws = wirelist_of_gate gate
let ws_unchecked = filter (\w -> (notElem w (map fst ws_in))
&& (notElem w (map fst ws_out))
&& (notElem w (map fst ctws))) ws
let ws_in' = (map (\(w,wt) -> (w, Just wt)) (ws_in ++ ctws))
++ (zip ws_unchecked (repeat Nothing))
let ui' = ui ++ check_inputs ws_in' wires
let wires' = Map.union wires (Map.fromList (ws_in'
++ (map (\(w,wt) -> (w, Just wt)) ws_out)))
put (state {used_wires = wires',undefined_inputs = ui'})
return (Lazy gate)
(sub:rest) -> do
let gates = circuit sub
let gates' = gate:gates
let sub' = sub {circuit = gates'}
put (state {current_subroutine = (sub':rest)})
return Empty
-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info
-- about that subroutine. The entrance to the subroutine contains its name.
enter_subroutine :: String -> CircInfo ()
enter_subroutine name = do
state <- get
let ms = current_subroutine state
put (state {current_subroutine = ((new_subroutine name):ms)})
-- | We can add the shape to the current subroutine
add_subroutine_shape :: String -> CircInfo ()
add_subroutine_shape s = do
state <- get
let ms = current_subroutine state
case ms of
[] -> error "Shape given outside of Subroutine Definition"
(sub:rest) -> put (state {current_subroutine = ((sub {shape = s}):rest)})
-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info
-- about that subroutine. The subroutine might be controllable.
set_controllable :: ControllableFlag -> CircInfo ()
set_controllable val = do
state <- get
let ms = current_subroutine state
case ms of
[] -> error "Controllable not in Subroutine Definition"
(sub:rest) -> put (state {current_subroutine = ((sub {controllable = val}):rest)})
-- | A datatype to represent the various outputs a CircInfo computation
-- may require.
data CircInfoOut = Empty | Lazy Gate | SubDef (BoxId,Sub)
-- | This function returns True if the given input defines a Gate
isGate :: CircInfoOut -> Bool
isGate (Lazy _) = True
isGate _ = False
-- | This function returns True if the given inputs defines a SubDef
isSub :: CircInfoOut -> Bool
isSub (SubDef _) = True
isSub _ = False
-- | The CircInfo Monad tracks whether we are in a subroutine, and collects info
-- about that subroutine. At the end of the subroutine, it stores the subroutine
-- for later use.
exit_subroutine :: CircInfo CircInfoOut
exit_subroutine = do
state <- get
let ms = current_subroutine state
case ms of
[] -> return Empty
(sub:rest) -> do
let n = name sub
let s = shape sub
put (state {current_subroutine = rest})
return (SubDef ((BoxId n s),sub))
-- | Take a GatePlus and execute it in the 'CircInfo' monad.
-- Again, the executed computation may depend upon whether we're in a subroutine
-- definition.
do_gate :: GatePlus -> CircInfo CircInfoOut
do_gate (G gate wts) = add_gate gate wts
do_gate (I ws) = add_wire_inputs ws >> return Empty
do_gate (O ws) = do
add_wire_outputs ws
exit_subroutine
do_gate EmptyLine = return Empty
do_gate (CommentLine comment) = return Empty
do_gate (SubroutineName name) = enter_subroutine name >> return Empty
do_gate (SubroutineShape shape) = add_subroutine_shape shape >> return Empty
do_gate (Controllable b) = set_controllable b >> return Empty
-- | Monad version of 'parse_ascii_line': parse a string and execute the
-- resulting gate directly in the 'CircInfo' monad.
run_ascii_line :: String -> CircInfo CircInfoOut
run_ascii_line s = case parse_ascii_line s of
Nothing -> error ("unrecognized line: " ++ show s)
Just p -> do_gate p
-- | Parse a stream consisting of many lines of ASCII output and execute
-- the parsed gates in the 'CircInfo' monad, checking to see if the first
-- line defines the inputs to the circuit.
run_ascii_lines :: [String] -> (Maybe [(Wire,Wiretype)],CircInfo [CircInfoOut])
run_ascii_lines [] = (Nothing,return [])
run_ascii_lines [f] = case parse_ascii_line f of
Just (I ws) -> (Just ws, return [])
_ -> (Nothing, run_ascii_line f >>= \x -> return [x])
-- Special case:
-- If the first line contains the input wires, we should
-- be able to parse the circuit lazily (e.g. it is a full circuit), as
-- we don't have to parse all the gates to calculate the inputs.
run_ascii_lines (f:s) =
case parse_ascii_line f of
Just (I ws) -> (Just ws, mapM run_ascii_line s)
_ -> (Nothing, mapM run_ascii_line (f:s))
-- | Run function for the 'CircInfo' monad: evaluate the state and
-- produce a list of inputs, outputs, and used wires.
run :: CircInfo [CircInfoOut] -> ([Gate],Map BoxId Sub,CircInfoState)
run f = (gs, subs, cis)
where
gs = [x | Lazy x <- filter isGate ci_outs]
subs = Map.fromList [x | SubDef x <- filter isSub ci_outs]
(ci_outs,cis) = runState f empty_circinfostate