{-# LANGUAGE FlexibleContexts #-} -- | Alternative implementations for the binary welded tree algorithm. -- The purpose of these is to experiment with and potentially -- illustrate a more functional programming style. module Quipper.Algorithms.BWT.Alternative where import Quipper import Quipper.Libraries.Qureg import Quipper.Libraries.Simulation import Quipper.Algorithms.BWT.Definitions -- import other Quipper stuff import qualified Quipper.Algorithms.BWT.BWT as BWT import Quipper.Utils.Sampling import Quipper.Utils.Auxiliary -- import other stuff import Control.Monad import Text.Printf import Data.Bits (xor) import Text.Printf import Data.Map (Map) import qualified Data.Map as Map -- ====================================================================== -- * Oracle abstraction -- | This is a version of 'BWT.Oracle' that uses 'Qulist' instead of -- 'Qureg'. An oracle provides the following information: the tree -- depth /n/, the label length /m/, the number of edge colors /k/, -- the entrance label /ENTRANCE/, and for each color 0 ≤ /c/ < /k/, -- a reversible circuit /ORACLE[sub c]\(a,b,r)/. data Oracle = Oracle { n :: Int, m :: Int, k :: Int, entrance :: Boollist, oraclefun :: Int -> (Qulist, Qulist, Qubit) -> Circ () } -- | Convert an @Alternative@.'Oracle' into a @BWT@.'BWT.Oracle'. convert_oracle :: Oracle -> BWT.Oracle convert_oracle Oracle { n=n, m=m, k=k, entrance=e, oraclefun=f1 } = BWT.Oracle { BWT.n=n, BWT.m=m, BWT.k=k, BWT.entrance=e, BWT.oraclefun=f2 } where f2 c (a, b, r) = f1 c (qulist_of_qureg_te a, qulist_of_qureg_te b, r) -- ====================================================================== -- * Top-level algorithm -- | Do a quantum random walk on the binary welded tree given by the -- oracle, for /s/ times steps of length /dt/. Return a bit list -- corresponding to the probable exit label. This is a more -- functional implementation of 'BWT.qrwbwt' from the module -- "Quipper.Algorithms.BWT.BWT". -- -- Note: This implementation does not rely on the oracle being -- self-inverse, and therefore only requires that -- -- > ORACLE[sub c](a, 0, 0) = (a, v[sub c](a), f[sub c](a)), -- -- rather than the more general property -- -- > ORACLE[sub c](a, b, r) = (a, b ⊕ v[sub c](a), r ⊕ f[sub c](a)). -- -- This gives us the freedom to build more efficient oracles, where -- appropriate. qrwbwt :: Oracle -> Timestep -> Int -> Circ Bitlist qrwbwt oracle dt s = do comment (printf "ENTER: qrwbwt (dt=%.3e, s=%d)" dt s) -- Initialize a to the entrance label. a <- qinit (entrance oracle) -- Run the Hamiltonian /s/ times for time step /dt/. replicateM s $ hamiltonian dt oracle a -- Measure /a/. exit <- measure a comment_with_label "EXIT: qrwbwt" exit "exit" return exit -- | Apply one round of the Hamiltonian for time step /dt/ to /a/. hamiltonian :: Timestep -> Oracle -> Qulist -> Circ () hamiltonian dt oracle a = do comment_with_label "ENTER: hamiltonian" a "a" for 0 ((k oracle)-1) 1 $ \c -> do -- for c from 0 to k-1 -- create ancillas b and r with_ancilla_list (length a) $ \b -> do with_ancilla $ \r -> do -- perform computations (oraclefun oracle) c (a,b,r) time_step dt (a,b,r) reverse_generic_imp ((oraclefun oracle) c) (a,b,r) -- "reverse" here is not quite GFI comment_with_label "EXIT: hamiltonian" a "a" -- | Apply the diffusion step to /(a,b,r)/. Here /a/ and /b/ must be -- of equal length. time_step :: Timestep -> (Qulist, Qulist, Qubit) -> Circ () time_step dt (a,b,r) = do comment_with_label "ENTER: time_step" (a,b,r) ("a","b","r") with_ancilla $ \h -> do basischange (a,b,h) controlledExpGate (dt,r,h) (reverse_generic_imp basischange) (a,b,h) comment_with_label "EXIT: time_step" (a,b,r) ("a","b","r") -- | Apply the basis change from Figure 3 of \[Childs et al.\] to /a/, -- /b/, and /h/. Here /a/ and /b/ must be of equal length. basischange :: (Qulist, Qulist, Qubit) -> Circ () basischange (a,b,h) = do comment_with_label "ENTER: basischange" (a,b,h) ("a","b","h") -- apply W gates zipWithM_ gate_W a b -- apply doubly-controlled not-gates zipWithM_ (dc_not h) a b comment_with_label "EXIT: basischange" (a,b,h) ("a","b","h") where dc_not h x y = qnot_at h `controlled` (x .==. 1 .&&. y .==. 0) -- | Compute the required number of iterations as a function of ε and -- /dt/. -- -- Inputs: /n/ is the tree depth, ε is the desired precision, /dt/ is -- the simulation time step. Intermediate values: /t/ is the -- simulation time. Output: /s/, the upper bound on the number of -- simulation time steps. -- -- Note: \[Childs et al\] specifies that /t/ should be chosen -- uniformly at random within the interval 0 < /t/ ≤ /n/[sup 4]\/2ε. -- Here, for simplicity, we just use /t/ = ⌊/n/[sup 4]\/2ε⌋. Also note -- that this function is for information only, as it is not actually -- used. Users should specify /s/ directly. compute_steps :: Int -> Double -> Double -> Int compute_steps n epsilon dt = floor (t / dt) where t = fromIntegral (n * n * n * n) / (2.0 * epsilon) -- ====================================================================== -- * Oracle implementations -- ** Blackbox oracle -- | A blackbox oracle for testing. This just produces a labelled box -- in place of the actual oracle circuit. The argument is the tree -- height. oracle_blackbox :: Int -> Oracle oracle_blackbox n = let m = n+2 in Oracle { n = n, m = m, k = 4, entrance = boollist_of_int_bh (n+2) 1, oraclefun = \c (a, b, r) -> do named_gate_at ("O(" ++ show c ++ ")") (a ++ b ++ [r]) return () } -- ---------------------------------------------------------------------- -- ** A simple \"exponential\" oracle. -- | This oracle, which works only for the fixed tree height 3, works -- by explicitly listing all the edges. It serves only to illustrate -- how the edge information is encoded. Listing all edges explicitly -- obviously would not scale well to larger graphs. oracle_simple :: Oracle oracle_simple = let n = 3 m = 5 k = 4 invalid = 0 -- also works correctly for invalid = 31 invalid_vec = boollist_of_int_bh m invalid green = 0 blue = 1 red = 2 black = 3 edges :: Int -> [(Int, Int)] edges 0 = [(1,3), (2,5), (4,8), (6,12), (7,15), (9,17), (10,18), (13,21), (19,25), (22,27), (24,28), (26,29)] edges 1 = [(2,4), (3,6), (5,10), (7,14), (8,16), (11,19), (12,20), (17,24), (21,26), (23,27), (25,28), (29,30)] edges 2 = [(1,2), (3,7), (4,9), (5,11), (6,13), (14,22), (15,23), (16,24), (18,25), (20,26), (27,29), (28,30)] edges 3 = [(8,17), (9,18), (10,19), (11,20), (12,21), (13,22), (14,23), (15,16)] edges n = error ("oracle_simple: illegal color: " ++ show n) -- apply an (a,n1)-controlled not operation to (v,n2) multi_controlled_multi_not :: Int -> (Qulist, Qulist) -> (Int, Int) -> Circ () multi_controlled_multi_not m (a,v) (n1, n2) = do let vec1 = boollist_of_int_bh m n1 let vec2 = boollist_of_int_bh m n2 bool_controlled_not v vec2 `controlled` a .==. vec1 return () -- the O_c circuit oracle_O :: Int -> (Qulist, Qulist) -> Circ () oracle_O c (a,v) = do let e = edges c foreach e $ \(n1, n2) -> do multi_controlled_multi_not m (a,v) (n1, invalid `xor` n2) multi_controlled_multi_not m (a,v) (n2, invalid `xor` n1) -- the B_c circuit oracle_B :: (Qulist, Qulist) -> Circ () oracle_B (v,b) = for 0 (m-1) 1 $ \i -> do qnot_at (b !! i) `controlled` (v !! i) .==. 1 -- the R_c circuit oracle_R :: (Qulist, Qubit) -> Circ () oracle_R (v,r) = do qnot_at r `controlled` v .==. invalid_vec -- the oracle circuit oraclefun :: Int -> (Qulist, Qulist, Qubit) -> Circ () oraclefun c (a,b,r) = do comment_with_label "ENTER: oracle_simple" (a,b,r) ("a","b","r") with_ancilla_init invalid_vec $ \v -> do oracle_O c (a,v) oracle_B (v,b) oracle_R (v,r) oracle_O c (a,v) comment_with_label "EXIT: oracle_simple" (a,b,r) ("a","b","r") in -- return the oracle object Oracle { n = n, m = m, k = k, entrance = boollist_of_int_bh m 1, oraclefun = oraclefun } -- ====================================================================== -- ** Alternate implementations of the \"orthodox\" oracle -- ---------------------------------------------------------------------- -- *** Classical implementation -- $ In this section, we first implement the oracle function -- /v/[sub /c/]\(/a/) as a classical boolean function. This implementation -- is just for reference, and attempts to be neither efficient nor -- quantum. It can, however, be used as a specification to test the -- actual quantum oracles against. -- -- Both the classical circuit implementation (below) and the Template -- Haskell implementation (in the module -- "Quipper.Algorithms.BWT.Template") were derived from this. -- -- We start with several auxiliary functions. -- | The type of nodes: a pair of a tree bit and a node address. type Node = (Bool, [Bool]) -- | Convert integers to nodes, mainly for testing. node_of_int :: Int -> Int -> Node node_of_int m a = node_of_boollist (boollist_of_int_bh m a) -- | Convert nodes to integers, mainly for testing. int_of_node :: Node -> Int int_of_node n = int_of_boollist_unsigned_bh (boollist_of_node n) -- | Convert a bit vector to a node. node_of_boollist :: [Bool] -> Node node_of_boollist (t:a) = (t,a) node_of_boollist [] = error "node_of_boollist: empty boollist" -- | Convert a node to a bit vector. boollist_of_node :: Node -> [Bool] boollist_of_node (t,a) = t:a -- | Input a node /a/ and return the parent of /a/. We assume that /a/ -- is not a root or invalid. parent :: Node -> Node parent (t, aa) = (t, False : init aa) -- | Input a node /a/ and return the left or right child of /a/ -- (depending on whether the /childbit/ is 'False' or 'True', -- respectively). Assumes that /a/ is not a leaf. childintree :: Node -> Bool -> Node childintree (t, h:aa) childbit = (t, aa ++ [childbit]) childintree _ _ = error "childintree: invalid node" -- | @'bit_adder' 'False'@ is a one-bit adder, and @'bit_adder' 'True'@ is -- a one-bit subtracter (i.e., add the 2's complement of /y/). bit_adder :: Bool -> (Bool, Bool, Bool) -> (Bool, Bool) bit_adder sign (carry, x, y) = (carry', z) where y' = y `bool_xor` sign z = carry `bool_xor` x `bool_xor` y' carry' = majority carry x y' majority a b c = if a==b then a else c -- | Input an /n/+1-bit leaf node /a/:/aa/ (without the tree bit; /a/ -- is the highest bit and /aa/ is the remaining /n/ bits) and a sign -- /s/ (where 'True' = negative, 'False' = positive). Return -- /a/:(/aa/ + /s/ * /f/). The first input is the /n/-bit welding -- vector /f/ (a parameter to the oracle). Note that /f/ is a -- parameter and /s/, /aa/ are inputs. doweld1 :: Boollist -> Bool -> [Bool] -> [Bool] doweld1 f s (a:aa) = a : aa' where aa' = snd $ fold_right_zip (bit_adder s) (s, aa, f) doweld1 f s [] = error "doweld1: invalid node" -- | Input an /n/+1-bit leaf node /a/:/aa/ (without the tree bit), and -- return /a/:(/aa/ ⊕ /g/). The first input is the /n/-bit welding -- vector /g/ (a parameter to the oracle). doweld0 :: Boollist -> [Bool] -> [Bool] doweld0 g (a:aa) = a : aa' where aa' = g `boollist_xor` aa doweld0 g [] = error "doweld0: invalid node" -- | Input a leaf node /a/ and return the left or right weld of /a/ in -- the other tree (depending on whether the /weldbit/ is 'False' or -- 'True'). Assumes that /a/ is a leaf. weld :: Boollist -> Boollist -> Node -> Bool -> Node weld f g (t, aa) weldbit = (not t, bb) where bb = if weldbit then doweld1 g t aa else doweld0 f aa -- | Input a node /a/ and return the left or right child of /a/ -- (depending on whether the /childbit/ is 'False' or 'True'. This -- works for leaf and non-leaf nodes. child :: Boollist -> Boollist -> Node -> Bool -> Node child f g (t, aa) childbit = case aa of True : _ -> -- This is a leaf weld f g (t, aa) childbit False : _ -> -- This is not a leaf childintree (t, aa) childbit _ -> error "child: invalid node" -- | Input a node address (without the tree bit) and return the parity -- of the node level expressed as a boolean either 'False' or -- 'True'. Leaves have parity 'False', and other levels have -- alternating parities. In other words: count the number of leading -- zeros modulo 2. level_parity :: [Bool] -> Bool level_parity [] = False level_parity (h:t) = if h then False else not (level_parity t) -- | Input a node address (without the tree bit) and return 'True' iff -- the node address is invalid. In other words, return 'True' iff the -- list consists of all 0's. is_zero :: [Bool] -> Bool is_zero [] = True is_zero (h:t) = if h then False else is_zero t -- | Input a node address (without the tree bit) and return 'True' iff -- the node is a root or invalid. In other words, check whether all -- digits but the last are 0's. is_root :: [Bool] -> Bool is_root [] = True is_root (h:[]) = True is_root (h:t) = if h then False else is_root t -- | @'v_function' f g c a@: returns /v/[sub /c/]\(/a/), the label of the -- node connected to /a/ by an edge of color /c/, or 'Nothing' if -- there is no such node. The parameters /f/ and /g/ encode the -- welding functions, and are lists of length /n/. /c/ is a color in -- the range 0..3, and /a/ is an (/n/+2)-bit node label. v_function :: Boollist -> Boollist -> Int -> Node -> Maybe Node v_function f g c a = let (t,aa) = a bc_hi = level_parity aa z = is_zero aa e = is_root aa a1 = if last aa then True else False [c_hi, c_lo] = boollist_of_int_bh 2 c [cbc_hi, cbc_lo] = [c_hi `bool_xor` bc_hi, c_lo] in if not e && [cbc_hi, cbc_lo] == [True, a1] then Just(parent a) else if not z && cbc_hi == False then Just(child f g a cbc_lo) else Nothing -- ---------------------------------------------------------------------- -- *** Auxiliary functions -- | The type of nodes: a pair of a tree bit and a node address. type CNode = (Bit, Bitlist) -- | Like 'CNode', but uses qubits instead of classical bits. type QNode = (Qubit, [Qubit]) -- | Convert a 'Qulist' to a 'QNode'. qnode_of_qulist :: Qulist -> QNode qnode_of_qulist (t:a) = (t,a) qnode_of_qulist [] = error "qnode_of_qulist: empty list" -- | Convert a 'Bitlist' to a 'CNode'. cnode_of_bitlist :: Bitlist -> CNode cnode_of_bitlist (t:a) = (t,a) cnode_of_bitlist [] = error "cnode_of_bitlist: empty list" -- | Exclusive or operation on bit vectors. cboollist_xor :: Bitlist -> Bitlist -> Circ Bitlist cboollist_xor = zipWithM (\x y -> cgate_xor [x,y]) -- ---------------------------------------------------------------------- -- *** Classical circuit implementation -- $ We now implement the oracle function /v/[sub /c/]\(/a/) as a classical -- circuit, with /c/ as a parameter. We don't try to be clever or -- efficient yet. The implementation follows the \"classical -- implementation\" above, but must be reformulated due to the need to -- work within the 'Circ' monad. -- | Input a node /a/ and return the parent of /a/. We assume that /a/ -- is not a root or invalid. cparent :: CNode -> Circ CNode cparent (t, aa_in) = do comment_with_label "ENTER: cparent" (t, aa_in) ("t", "aa_in") false <- cinit False let aa_out = false : init aa_in comment_with_label "EXIT: cparent" (t, aa_out) ("t", "aa_out") return (t, aa_out) -- | Input a node /a/ and return the left or right child of /a/ -- (depending on whether the /childbit/ is 'False' or 'True', -- respectively). Assumes that /a/ is not a leaf. cchildintree :: CNode -> Bit -> Circ CNode cchildintree (t, node_in@(h:aa)) childbit = do comment_with_label "ENTER: cchildintree" (t, node_in) ("t", "node_in") let node_out = aa ++ [childbit] comment_with_label "EXIT: cchildintree" (t, node_out) ("t", "node_out") return (t, node_out) cchildintree _ _ = error "cchildintree: invalid node" -- | @'bit_adder' 'False'@ is a one-bit adder, and @'bit_adder' 'True'@ is -- a one-bit subtracter (i.e., add the 2's complement of /y/). cbit_adder :: Bit -> (Bit, Bit, Bit) -> Circ (Bit, Bit) cbit_adder sign (carry_in, x, y) = do comment_with_label "ENTER: cbit_adder" (sign, carry_in, x, y) ("sign", "carry_in", "x", "y") y' <- cgate_xor [y, sign] z <- cgate_xor [carry_in, x, y'] carry_out <- cmajority carry_in x y' comment_with_label "EXIT: cbit_adder" (carry_out, z) ("carry_out", "z") return (carry_out, z) where cmajority a b c = do cond <- cgate_eq a b cgate_if cond a c -- | Input an /n/+1-bit leaf node /a/:/aa/ (without the tree bit; /a/ -- is the highest bit and /aa/ is the remaining /n/ bits) and a sign -- /s/ (where 'True' = negative, 'False' = positive). Return -- /a/:(/aa/ + /s/ * /f/). The first input is the /n/-bit welding -- vector /f/ (a parameter to the oracle). Note that /f/ is a -- parameter and /s/, /aa/ are inputs. cdoweld1 :: Boollist -> Bit -> Bitlist -> Circ Bitlist cdoweld1 f s node_in@(a:aa) = do comment_with_label "ENTER: cdoweld1" (s, node_in) ("s", "node_in") f' <- cinit f (_,aa') <- fold_right_zipM (cbit_adder s) (s, aa, f') let node_out = a : aa' comment_with_label "EXIT: cdoweld1" node_out "node_out" return node_out cdoweld1 f s [] = error "cdoweld1: invalid node" -- | Input an /n/+1-bit leaf node /a/:/aa/ (without the tree bit), and -- return /a/:(/aa/ ⊕ /g/). The first input is the /n/-bit welding -- vector /g/ (a parameter to the oracle). cdoweld0 :: Boollist -> Bitlist -> Circ Bitlist cdoweld0 g node_in@(a:aa) = do comment_with_label "ENTER: cdoweld0" node_in "node_in" g' <- cinit g aa' <- g' `cboollist_xor` aa let node_out = a:aa' comment_with_label "EXIT: cdoweld0" node_out "node_out" return node_out cdoweld0 g [] = error "cdoweld0: invalid node" -- | Input a leaf node /a/ and return the left or right weld of /a/ in -- the other tree (depending on whether the /weldbit/ is 'False' or -- 'True'). Assumes that /a/ is a leaf. cweld :: Boollist -> Boollist -> CNode -> Bit -> Circ CNode cweld f g node_in@(t, aa) weldbit = do comment_with_label "ENTER: cweld" (node_in, weldbit) ("node_in", "weldbit") bb <- circ_if weldbit ( cdoweld1 g t aa )( cdoweld0 f aa ) t' <- cgate_not t let node_out = (t', bb) comment_with_label "EXIT: cweld" node_out "node_out" return node_out -- | Input a node /a/ and return the left or right child of /a/ -- (depending on whether the /childbit/ is 'False' or 'True'. This -- works for leaf and non-leaf nodes. cchild :: Boollist -> Boollist -> CNode -> Bit -> Circ CNode cchild f g node_in@(t, a:aa) childbit = do comment_with_label "ENTER: cchild" (node_in, childbit) ("node_in", "childbit") node_out <- circ_if a ( -- This is a leaf cweld f g (t, a:aa) childbit )( -- This is not a leaf cchildintree (t, a:aa) childbit ) comment_with_label "EXIT: cchild" node_out "node_out" return node_out cchild f g (t, _) childbit = error "cchild: invalid node" -- | Input a node address (without the tree bit) and return the parity -- of the node level expressed as a boolean either 'False' or -- 'True'. Leaves have parity 'False', and other levels have -- alternating parities. In other words: count the number of leading -- zeros modulo 2. clevel_parity :: Bitlist -> Circ Bit clevel_parity node = do comment_with_label "ENTER: clevel_parity" node "node" parity <- clevel_parity_rec node comment_with_label "EXIT: clevel_parity" parity "parity" return parity where clevel_parity_rec :: Bitlist -> Circ Bit clevel_parity_rec [] = cinit False clevel_parity_rec (h:t) = do r <- clevel_parity_rec t circ_if h ( cinit False )( cgate_not r ) -- | Input a node address (without the tree bit) and return 'True' iff -- the node address is invalid. In other words, return 'True' iff the -- list consists of all 0's. cis_zero :: Bitlist -> Circ Bit cis_zero node = do comment_with_label "ENTER: cis_zero" node "node" is_zero <- cis_zero_rec node comment_with_label "EXIT: cis_zero" is_zero "is_zero" return is_zero where cis_zero_rec :: Bitlist -> Circ Bit cis_zero_rec [] = cinit True cis_zero_rec (h:t) = do circ_if h ( cinit False )( cis_zero_rec t ) -- | Input a node address (without the tree bit) and return 'True' iff -- the node is a root or invalid. In other words, check whether all -- digits but the last are 0's. cis_root :: Bitlist -> Circ Bit cis_root node = do comment_with_label "ENTER: cis_root" node "node" is_root <- cis_root_rec node comment_with_label "EXIT: cis_root" is_root "is_root" return is_root where cis_root_rec :: Bitlist -> Circ Bit cis_root_rec [] = cinit True cis_root_rec (h:[]) = cinit True cis_root_rec (h:t) = do circ_if h ( cinit False )( cis_root_rec t ) -- | @'cv_function' f g c a@: returns /v/[sub /c/]\(/a/), the label of the -- node connected to /a/ by an edge of color /c/, or 'Nothing' if -- there is no such node. The parameters /f/ and /g/ encode the -- welding functions, and are lists of length /n/. /c/ is a color in -- the range 0..3, and /a/ is an (/n/+2)-bit node label. -- -- We currently implement @'Maybe' 'CNode'@ as an indexed union, and -- specifically as @('CNode','Bit')@. When /Bit/='True', the value of -- 'CNode' is undefined (doesn't matter); in particular, this value -- may contain garbage. cv_function :: Boollist -> Boollist -> Int -> CNode -> Circ (CNode,Bit) cv_function f g color a = do comment_with_label (printf "ENTER: cv_function (color=%d)" color) a "a" let (t,aa) = a bc_hi <- clevel_parity aa z <- cis_zero aa e <- cis_root aa let a1 = last aa let [c_hi', c_lo'] = boollist_of_int_bh 2 color c_hi <- cinit c_hi' c_lo <- cinit c_lo' cbc_hi <- cgate_xor [c_hi, bc_hi] let cbc_lo = c_lo not_e <- cgate_not e cbc_lo_eq_a1 <- cgate_eq cbc_lo a1 cond1 <- cgate_and [not_e, cbc_hi, cbc_lo_eq_a1] (b, invalid) <- circ_if cond1 ( do cparent_a <- cparent a false <- cinit False return (cparent_a, false) )( do cchild_a_cbc_lo <- cchild f g a cbc_lo not_z <- cgate_not z cbc_hi_eq_false <- cgate_not cbc_hi valid <- cgate_and [not_z, cbc_hi_eq_false] not_valid <- cgate_not valid -- a slight optimization here: we return garbage in the -- first register if not_valid == True return (cchild_a_cbc_lo, not_valid) ) comment_with_label (printf "EXIT: cv_function (color=%d)" color) (b, invalid) ("b", "invalid") return (b, invalid) -- ====================================================================== -- *** Oracle abstraction -- | The classical oracle implementation, packaged into the 'Oracle' -- abstraction. This oracle has two parameters, namely the welding -- vectors /f/ and /g/. Note: this oracle has not been optimized -- whatsoever. oracle_classical :: Boollist -> Boollist -> Oracle oracle_classical f g = Oracle { n = n, m = m, k = k, entrance = entrance, oraclefun = oraclefun } where n = length f m = n+2 k = 4 entrance = boollist_of_int_bh m 1 oraclefun :: Int -> (Qulist, Qulist, Qubit) -> Circ () oraclefun color (a,b,r) = do let an = qnode_of_qulist a let bn = qnode_of_qulist b (classical_to_quantum . classical_to_reversible) (cv_function f g color) (an, (bn, r)) return () -- ====================================================================== -- * Testing functions -- | Output the list of colored edges as computed by the classical -- 'v_function', for some arbitrary choice of /f/ and /g/ and /n/=3. main_edges1 :: IO() main_edges1 = mapM_ output (sample_all0 (127,3)) where f = take 5 (True : False : f) g = take 5 (False : True : g) output :: (Int,Int) -> IO() output (a,c) = case v_function f g c (node_of_int 7 a) of Nothing -> printf "%d ---%d---> None\n" a c Just b -> printf "%d ---%d---> %d\n" a c (int_of_node b) -- | For debugging: 'circfun' is similar to 'v_function', except it -- works by calling 'cv_function' to assemble the circuit, then -- simulates it. This is for testing whether the assembled circuit is -- correct. Returns @('Bool', 'Node')@ instead of @'Maybe' 'Node'@, so -- that we can see any garbage that is output in case of an invalid -- node. circfun :: Boollist -> Boollist -> Int -> Node -> (Node, Bool) circfun f g color nd = run_classical_generic (cv_function f g color) nd -- | Output the list of colored edges as computed by simulating the -- circuit 'cv_function', for some arbitrary choice of /f/ and /g/ and -- /n/=3. This is like 'main_edges1', except it actually assembles and -- simulates the classical circuit. main_edges2 :: IO() main_edges2 = mapM_ output (sample_all0 (127,3)) where f = take 5 (True : False : f) g = take 5 (False : True : g) output :: (Int,Int) -> IO() output (a,c) = case circfun f g c (node_of_int 7 a) of (node, False) -> printf "%d ---%d---> %d\n" a c (int_of_node node) (garbage, True) -> printf "%d ---%d---> None (%d)\n" a c (int_of_node garbage) -- | Graphically output the classical oracle circuit for color /c/, -- using /n/ from the oracle data structure, and for some arbitrary -- /f/ and /g/. main_oraclec :: Format -> BWT.Oracle -> Int -> IO() main_oraclec format oracle color = print_generic format circuit cnode_shape where m' = BWT.m oracle n' = BWT.n oracle f = take n' (True : False : f) g = take n' (False : True : g) cnode_shape = cnode_of_bitlist (replicate m' bit) circuit n = cv_function f g color n -- | Like 'main_oraclec', except it rewrites the classical circuit in -- terms of Toffoli gates. main_oracle2 :: Format -> BWT.Oracle -> Int -> IO() main_oracle2 format oracle color = print_generic format circuit cnode_shape where m' = BWT.m oracle n' = BWT.n oracle f = take n' (True : False : f) g = take n' (False : True : g) cnode_shape = cnode_of_bitlist (replicate m' bit) circuit n = classical_to_cnot (cv_function f g color n) -- | Like 'main_oraclec', except it makes the classical circuit -- reversible first. main_oracle3 :: Format -> BWT.Oracle -> Int -> IO() main_oracle3 format oracle color = print_generic format circuit (cnode_shape, (cnode_shape, qubit)) where m' = BWT.m oracle n' = BWT.n oracle f = take n' (True : False : f) g = take n' (False : True : g) cnode_shape = qnode_of_qulist (replicate m' qubit) circuit = (classical_to_quantum . classical_to_reversible) (cv_function f g color) -- | Output the top-level circuit for the binary welded tree algorithm -- with the classical oracle, using some arbitrary welding vectors /f/ -- and /g/, and /s/=1. main_qrwbwt :: IO() main_qrwbwt = print_simple EPS (qrwbwt (oracle_classical f g) dt 1) where f = [False, False, True] g = [True, False, True] dt = pi/180