-- | This module provides a simplified representation of classical
-- circuits.
module Quipper.Libraries.ClassicalOptim.Circuit where
import qualified Data.Map as M
import qualified Data.List as L
import qualified Quipper.Utils.Auxiliary as Q
import Control.Applicative (Applicative(..))
import Control.Monad (liftM, ap)
-- ----------------------------------------------------------------------
-- * Simplified circuits
-- | The type of wires. A wire is determined by an integer ID.
type Wire = Int
-- | The type of gates.
data Gate =
NoOp -- ^ No operation.
| Init Bool Wire -- ^ Initialization.
| Cnot Wire [(Wire,Bool)] -- ^ Multi-controlled not.
deriving (Show,Eq)
-- | Get the wire acted upon by a gate, if any.
wireOfGate :: Gate -> Maybe Wire
wireOfGate NoOp = Nothing
wireOfGate (Init _ w) = Just w
wireOfGate (Cnot w _) = Just w
-- | Get the list of controls, if any.
ctlsOfGate :: Gate -> Maybe [(Wire,Bool)]
ctlsOfGate (Cnot _ ctls) = Just ctls
ctlsOfGate _ = Nothing
-- | Evaluate a circuit on a given initial state, and return the final
-- state. A state is represented as a map from wires to booleans.
evalCirc :: M.Map Wire Bool -> [Gate] -> M.Map Wire Bool
evalCirc m [] = m
evalCirc m (NoOp:gs) = evalCirc m gs
evalCirc m ((Init b w):gs) = evalCirc (M.insert w b m) gs
evalCirc m ((Cnot w ctls):gs) = evalCirc (M.adjust (Q.bool_xor (ands m ctls)) w m) gs
where
ands m [] = True
ands m ((w,b):ctls) = ((m M.! w) `Q.bool_xor` (not b)) && (ands m ctls)
-- ----------------------------------------------------------------------
-- * Simplified Circ monad
-- | A data structure to represent a \"circuit under
-- construction\". This holds the data needed for circuit generation.
data CircState = CS {
circuit :: [Gate], -- ^ The circuit so far.
freshWire :: Wire -- ^ The next fresh wire.
} deriving (Show)
-- | The empty state.
emptyState :: CircState
emptyState = CS {circuit = [], freshWire = 0}
-- | A simplified @Circ@ monad.
data Circ a = Circ (CircState -> (CircState, a))
instance Monad Circ where
return x = Circ (\y -> (y,x))
(>>=) (Circ c) f = Circ (\s -> let (s',x) = c s in
let (Circ c') = f x in
c' s')
instance Applicative Circ where
pure = return
(<*>) = ap
instance Functor Circ where
fmap = liftM
-- ----------------------------------------------------------------------
-- * Low-level access functions
-- | Retrieve the next fresh wire.
getFresh :: Circ Wire
getFresh = Circ (\s -> (s, freshWire s))
-- | Increment the value of the fresh wire.
incrementFresh :: Circ ()
incrementFresh = Circ (\s -> (s { freshWire = freshWire s + 1 }, ()))
-- | Add a new gate to the circuit.
addGate :: Gate -> Circ ()
addGate g = Circ (\s -> (s {circuit = g : (circuit s)}, ()))
-- | Get the circuit out of the monad.
extractCircuit :: Circ a -> [Gate]
extractCircuit (Circ c) = circuit $ fst $ c emptyState
-- ----------------------------------------------------------------------
-- * Higher-level access functions
-- | Initialize a new wire.
init :: Bool -> Circ Wire
init b = do
w <- getFresh
addGate (Init b w)
incrementFresh
return w
-- | Add a multi-controlled not gate.
cnot :: Wire -> [(Wire,Bool)] -> Circ ()
cnot w ws = do
addGate (Cnot w ws)
return ()
-- ----------------------------------------------------------------------
-- * Pretty-printing
-- $ These functions are only used for testing.
-- | Pretty-print a circuit as a list of gates.
printCircuit :: Circ a -> IO ()
printCircuit c = do
mapM_ putStrLn $ map show $ reverse $ extractCircuit c
-- | Print a gate as Quipper code.
print_quipperStyle :: Gate -> IO ()
print_quipperStyle (Init b w) = putStrLn (" x" ++ (show w) ++ " <- Q.qinit " ++ (show b))
print_quipperStyle (Cnot w ctls) = putStrLn (" Q.qnot x" ++ (show w) ++ " `Q.controlled` " ++
(L.intercalate " Q..&&. " $ map (\(w,b) -> "x" ++ (show w) ++ " Q..==. " ++ (show b) ++ " ") ctls))
print_quipperStyle g = putStrLn (" Q.comment \"" ++ (show g) ++ "\"")