{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE FlexibleInstances #-} -- | The BWT Oracle, written in a classical, functional manner and -- automatically transformed to a quantum circuit using Quipper's -- \"build_circuit\" mechanism. module Quipper.Algorithms.BWT.Template where import Quipper import Control.Monad (sequence) import Quipper.Algorithms.BWT.Alternative (Oracle(..), Node, QNode) import Quipper.Utils.Auxiliary hiding (boollist_xor) import Quipper.Libraries.ClassicalOptim -- ---------------------------------------------------------------------- -- * Circuit building functions -- $BUILDING -- -- This section contains an implementation of the oracle as a -- collection of ordinary functional programs. Each function in this -- section is decorated with the @build_circuit@ keyword (see -- "Quipper.Internal.CircLifting#build_circuit"). Therefore, circuits are -- automatically generated; for example, the circuit corresponding to -- the function 'v_function' is automatically built and given the name -- 'template_v_function'. ---------------------------------------------------------------------- -- ** General operations on booleans -- | Exclusive /or/ operation on bit vectors. build_circuit boollist_xor :: [Bool] -> [Bool] -> [Bool] boollist_xor x y = zipWith bool_xor x y -- | '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/). build_circuit bit_adder :: Bool -> (Bool,Bool,Bool) -> (Bool,Bool) bit_adder sign (carry, x,y) = let majority a b c = if (a `bool_xor` b) then c else a in let y' = y `bool_xor` sign in let z = carry `bool_xor` x `bool_xor` y' in let carry' = majority carry x y' in (carry', z) ---------------------------------------------------------------------- -- ** Encoding the BWT oracle on booleans and lists of booleans -- | Input a node /a/ and return the parent of /a/. We assume that /a/ -- is not a root or invalid. build_circuit parent :: Node -> Node parent (x,y) = (x, False:(init y)) -- | 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. build_circuit childintree :: Node -> Bool -> Node childintree (t,l) c = case l of [] -> error "childintree" h:aa -> (t, aa ++ [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. build_circuit doweld1 :: Boollist -> Bool -> [Bool] -> [Bool] doweld1 f s l = case l of [] -> error "doweld1" a:aa -> a : (snd (fold_right_zip (bit_adder s) (s, aa, f))) -- | 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). build_circuit doweld0 :: Boollist -> [Bool] -> [Bool] doweld0 g l = case l of [] -> error "doweld0" a:aa -> a : (g `boollist_xor` aa) -- | 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. build_circuit weld :: Boollist -> Boollist -> Node -> Bool -> Node weld f g (t,aa) weldBit = if weldBit then (not t, doweld1 g t aa) else (not t, 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. build_circuit child :: Boollist -> Boollist -> Node -> Bool -> Node child f g (t,aa) childBit = case aa of [] -> error "child" h:tt -> if h then weld f g (t, aa) childBit else childintree (t, aa) childBit -- | 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. build_circuit level_parity :: [Bool] -> Bool level_parity l = foldl (\a b -> not (a || b)) False (reverse l) -- | 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. build_circuit is_zero :: [Bool] -> Bool is_zero l = foldl (\a b -> a && (not b)) True l -- | 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. build_circuit is_root :: [Bool] -> Bool is_root l = case (reverse l) of [] -> True (h:t) -> is_zero 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. build_circuit v_function :: BoolParam -- ^First color bit. -> BoolParam -- ^Second color bit. -> Boollist -- ^Vector /f/ from Equation (26). -> Boollist -- ^Vector /g/ from Equation (27). -> Node -- ^Entry node /a/. -> (Bool,Node) -- ^('True', exit node) or ('False', garbage). v_function c_hi c_lo f g a = let aa = snd a in let cbc_hi = newBool c_hi `bool_xor` level_parity aa in let cbc_lo = newBool c_lo in if (not (is_root aa) && cbc_hi && not (cbc_lo `bool_xor` (last aa))) then (False, parent a) else let res = child f g a cbc_lo in (is_zero aa || cbc_hi, res) -- ---------------------------------------------------------------------- -- * Wrapping it into the Oracle data type -- $PACKAGE The following functions package the circuit generated by -- 'v_function' into a data structure of type 'Oracle'. -- ---------------------------------------------------------------------- -- ** Colors -- | A color is a number between 0 and 3. type Color = Int -- | Convert an integer representation of a color into the two-bit representation. colorToBoolParam :: Color -> (BoolParam,BoolParam) colorToBoolParam 0 = (PFalse,PFalse) colorToBoolParam 1 = (PFalse,PTrue) colorToBoolParam 2 = (PTrue,PFalse) colorToBoolParam 3 = (PTrue,PTrue) colorToBoolParam _ = error "color out of range" -- ---------------------------------------------------------------------- -- ** Functions for using the generated oracle -- | This is the /irreversible/ classical circuit generated from -- 'v_function'. This is basically the same as 'template_v_function', -- except that excessive uses of 'Circ' are removed from the type, and -- the inputs and outputs have been reorganized. classical_BWT_oracle :: Color -- ^ The color. -> ([Qubit],[Qubit], QNode) -- ^ The two welding vectors and a node /a/. -> Circ (Qubit, QNode) -- ^ Output /(r,b)/. classical_BWT_oracle col (f,g,xs) = unpack template_v_function b1 b2 f g xs where (b1,b2) = colorToBoolParam col -- | This is the /reversible/ circuit automatically generated from 'classical_BWT_oracle'. reversible_BWT_oracle :: Color -- ^ Color. -> (([Qubit], [Qubit], QNode), (Qubit, QNode)) -- ^ /(f, g, a, r, b)/. -> Circ (([Qubit], [Qubit], QNode), (Qubit, QNode)) -- ^ Output /(f, g, a, r, b)/. reversible_BWT_oracle color ((f, g, a), (r, b)) = do comment_with_label "ENTER: reversible_BWT_oracle" ((f, g, a), (r, b)) (("f", "g", "a"), ("r", "b")) ((f, g, a), (r, b)) <- classical_to_reversible (classical_BWT_oracle color) ((f, g, a), (r, b)) comment_with_label "EXIT: reversible_BWT_oracle" ((f, g, a), (r, b)) (("f", "g", "a"), ("r", "b")) return ((f, g, a), (r, b)) -- | This is the /reversible/ circuit automatically generated from 'classical_BWT_oracle', and optimized with peep-hole optimization. reversible_BWT_oracle_optim :: Color -- ^ Color. -> (([Qubit], [Qubit], QNode), (Qubit, QNode)) -- ^ /(f, g, a, r, b)/. -> Circ (([Qubit], [Qubit], QNode), (Qubit, QNode)) -- ^ Output /(f, g, a, r, b)/. reversible_BWT_oracle_optim color ((f, g, a), (r, b)) = do comment_with_label "ENTER: reversible_BWT_oracle" ((f, g, a), (r, b)) (("f", "g", "a"), ("r", "b")) ((f, g, a), (r, b)) <- classical_to_reversible_optim (classical_BWT_oracle color) ((f, g, a), (r, b)) comment_with_label "EXIT: reversible_BWT_oracle" ((f, g, a), (r, b)) (("f", "g", "a"), ("r", "b")) return ((f, g, a), (r, b)) -- | The template oracle, packaged into the 'Oracle' abstraction. Note -- that this circuit is automatically generated from the classical -- functions above, but is completely unoptimized. This oracle has two -- parameters, namely the welding vectors /f/ and /g/. oracle_template :: [Bool] -> [Bool] -> Oracle oracle_template f g = Oracle { n = n, m = m, k = 4, entrance = boollist_of_int_bh m 1, oraclefun = \c (as,bs,r) -> do qf <- qinit f qg <- qinit g let (a:aa) = as let (b:bb) = bs reversible_BWT_oracle c ((qf, qg, (a,aa)), (r, (b,bb))) qterm g qg qterm f qf } where n = length f m = n+2 -- | The template oracle, optimized. oracle_template_optim :: [Bool] -> [Bool] -> Oracle oracle_template_optim f g = Oracle { n = n, m = m, k = 4, entrance = boollist_of_int_bh m 1, oraclefun = \c (as,bs,r) -> do qf <- qinit f qg <- qinit g let (a:aa) = as let (b:bb) = bs reversible_BWT_oracle_optim c ((qf, qg, (a,aa)), (r, (b,bb))) qterm g qg qterm f qf } where n = length f m = n+2