entangle-0.1.0: src/lib/EntangleMonad.hs
{-# LANGUAGE FlexibleContexts #-}
module EntangleMonad where
import Control.Monad
import Data.List
import Data.Map.Lazy (Map)
import qualified Data.Map.Lazy as DM
import Data.Maybe
import Data.Monoid
--import Debug.Trace
import Quipper
import Quipper.Circuit
import Quipper.Monad
import Quipper.Transformer
import BitQubitId
type BitState = Map BitId Bool
newtype EntangleMonad a = EntangleMonad {
untangle :: BitState -- ^ state of bits before the operation
-> [QubitId] -- ^ measured qubits before the operation
-> CircTree (BitState, [QubitId], a) -- ^ tree after the operation
}
instance Functor EntangleMonad where
fmap = liftM
instance Applicative EntangleMonad where
pure = return
(<*>) = ap
instance Monad EntangleMonad where
return x = EntangleMonad (\bs ms -> LeafNode (bs, ms, x))
x >>= f = EntangleMonad $ \bs ms -> do
(bs', ms', y) <- untangle x bs ms
untangle (f y) bs' ms'
data CircTree a
= GateNode String [QubitId] [QubitId] (CircTree a) -- ^ name, affected qubits, controls, child
| ParameterizedGateNode String Double [QubitId] [QubitId] (CircTree a) -- ^ name, parameter, affected qubits, controls, child
| MeasureNode QubitId BitId (CircTree a) (CircTree a)
| LeafNode a
instance Functor CircTree where
fmap = liftM
instance Applicative CircTree where
pure = return
(<*>) = ap
instance Monad CircTree where
return = LeafNode
(GateNode n qs cs t) >>= f = GateNode n qs cs (t >>= f)
(ParameterizedGateNode n p qs cs t) >>= f = ParameterizedGateNode n p qs cs (t >>= f)
(MeasureNode q b l r) >>= f = MeasureNode q b (l >>= f) (r >>= f)
(LeafNode x) >>= f = f x
instance Foldable CircTree where
foldMap f (GateNode _ _ _ t) = foldMap f t
foldMap f (ParameterizedGateNode _ _ _ _ t) = foldMap f t
foldMap f (MeasureNode _ _ l r) = foldMap f l <> foldMap f r
foldMap f (LeafNode x) = f x
instance Show a => Show (CircTree a) where
show = showTree 0 show' child where
child (GateNode _ _ _ c) = [c]
child (ParameterizedGateNode _ _ _ _ c) = [c]
child (MeasureNode _ _ l r) = [l, r]
child (LeafNode _) = []
show' (GateNode n qs cs _) = "GateNode \"" ++ n ++ "\" on " ++ qubits ++ (if null controls then "" else " and controls " ++ controls) where
qubits = intercalate ", " $ map show qs
controls = intercalate ", " $ map show cs
show' (ParameterizedGateNode n t qs cs _) = "ParameterizedGateNode \"" ++ n ++ "\" with parameter " ++ show t ++ " on " ++ qubits ++ (if null controls then "" else " and controls " ++ controls) where
qubits = intercalate ", " $ map show qs
controls = intercalate ", " $ map show cs
show' (MeasureNode q b _ _) = "MeasureNode on " ++ show q ++ " producing " ++ show b
show' (LeafNode x) = "LeafNode of " ++ show x
showTree :: Int -> (a -> String) -> (a -> [a]) -> a -> String
showTree i sf cf t = indent i ++ sf t ++ concatMap showChild children where
children = cf t
fork = length children > 1
showChild c = "\n" ++ showTree (if fork then i+1 else i) sf cf c
indent :: Int -> String
indent i = replicate (4*i) ' '
transformGate :: String -> [QubitId] -> [QubitId] -> EntangleMonad ()
transformGate name qs cs = EntangleMonad res where
res bs ms = GateNode name qs cs $ LeafNode (bs, ms, ())
transformParameterizedGate :: String -> Double -> [QubitId] -> [QubitId] -> EntangleMonad ()
transformParameterizedGate name t qs cs = EntangleMonad res where
res bs ms = ParameterizedGateNode name t qs cs $ LeafNode (bs, ms, ())
transformMeasure :: QubitId -> EntangleMonad BitId
--transformMeasure i | trace ("Measuring " ++ show (unQubitID i)) False = undefined
transformMeasure i = EntangleMonad res where
res bs ms = MeasureNode i new l r where
ms' = i : ms
lastBit = foldr max minBound $ DM.keys bs
new = succ lastBit
bsF = DM.insert new False bs
l = LeafNode (bsF, ms', new)
bsT = DM.insert new True bs
r = LeafNode (bsT, ms', new)
transformDynamicLifting :: BitId -> EntangleMonad Bool
transformDynamicLifting i = EntangleMonad res where
res bs ms = LeafNode (bs, ms, b) where
b = fromMaybe False $ DM.lookup i bs
-- |mytransformer is the main transformer.
-- it used to extract the needed information from a Quipper circuit.
mytransformer :: Transformer EntangleMonad QubitId BitId
--mytransformer g | trace (show g) False = undefined
mytransformer (T_QGate name _ _ _ _ f) = f g where
open (Signed _ False) = error "Negative controls are not supported yet"
open (Signed x True) = x
g wires g_controls controls = do
transformGate name wires (map (assumeQubit . open) controls)
return (wires, g_controls, controls)
mytransformer (T_QRot name _ _ _ t _ f) = f g where
open (Signed _ False) = error "Negative controls are not supported yet"
open (Signed x True) = x
g wires g_controls controls = do
transformParameterizedGate name t wires (map (assumeQubit . open) controls)
return (wires, g_controls, controls)
mytransformer (T_QMeas f) = f transformMeasure
mytransformer (T_DTerm _ f) = f (const $ return ())
mytransformer g = error $ "Gate \"" ++ show g ++ "\" is not supported yet"
assumeQubit :: B_Endpoint QubitId BitId -> QubitId
assumeQubit (Endpoint_Qubit qi) = qi
assumeQubit (Endpoint_Bit _) = error "Using bits as controls is not supported yet"
mydtransformer :: DynamicTransformer EntangleMonad QubitId BitId
mydtransformer = DT mytransformer (error "Boxed circuits are not supported yet") transformDynamicLifting
showIndented :: Show a => Int -> a -> String
showIndented i x = indent i ++ replace (show x) where
replace [] = []
replace ('\n':ss) = '\n' : indent i ++ replace ss
replace (s:ss) = s : replace ss
-- |buildTree takes a 'Circuit', its arity and returns a tree representing it.
buildTree :: DBCircuit x -> Int -> CircTree x
buildTree circuit n = fmap (fst . (\(_, _, x) -> x)) res where
res = untangle monad DM.empty []
monad = transform_dbcircuit mydtransformer circuit bindings
bindings = foldr (\i -> bind_qubit (qubit_of_wire i) (qubitId i)) bindings_empty [1..n]