packages feed

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]