{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveDataTypeable #-} -- | This module provides a data type of quantum registers, as well as -- associated types of classical and boolean registers. module Quipper.Libraries.Qureg ( -- * Quantum registers Qureg, qureg_of_qulist_te, qulist_of_qureg_te, qureg_length, qinit_register, qterm_register, qmeasure_register, with_ancilla_reg, with_ancilla_reg_init, qureg_shape, -- * Bit registers Bitreg, bitreg_of_bitlist_te, bitlist_of_bitreg_te, bitreg_length, -- * Boolean registers Boolreg, boolreg_of_boollist_te, boollist_of_boolreg_te, boolreg_length, boolreg_of_int_le, int_of_boolreg_unsigned_le, -- * General registers Register, (.!) ) where import Quipper import Quipper.Internal import Quipper.Utils.Auxiliary import qualified Data.IntMap as IntMap import Data.IntMap (IntMap) import Data.Typeable -- ---------------------------------------------------------------------- -- * Quantum registers -- ** General registers -- | A register is an array of elements of some type /x/, indexed by -- natural numbers in the range from 0 to /n/-1, where /n/ is the -- length of the register. newtype Register x = Register { unRegister :: IntMap x } deriving (Typeable, Show) -- | @/r/ !.(/i/)@: Return the /i/th element of a register /r/. (.!) :: Register x -> Int -> x a .!(i) = (unRegister a) IntMap.! i infixl 9 .! -- same precedence as !! -- | Convert a list to a register. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. register_of_list_te :: [x] -> Register x register_of_list_te l = Register $ IntMap.fromList (zip [n-1, n-2 .. 0] l) where n = length l -- | Convert a register to a list. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. list_of_register_te :: Register x -> [x] list_of_register_te r = map (r .!) [n-1, n-2 .. 0] where n = register_length r -- | Return the length of a register. register_length :: Register x -> Int register_length = IntMap.size . unRegister -- ---------------------------------------------------------------------- -- ** Qubit registers -- | The type of quantum registers. A quantum register is an array of -- qubits, indexed by natural numbers in the range from 0 to /n/-1, -- where /n/ is the length of the register. The syntax /a/ .!(/i/) is -- used to access the /i/th element of the register /a/. -- -- The main advantage of a register over a list is constant-time -- access. The main disadvantage is that registers don't allow easy -- appending, pattern matching, or recursion. type Qureg = Register Qubit -- | Convert a 'Qulist' to a 'Qureg'. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. qureg_of_qulist_te :: Qulist -> Qureg qureg_of_qulist_te = register_of_list_te -- | Convert a 'Qureg' to a 'Qulist'. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. qulist_of_qureg_te :: Qureg -> Qulist qulist_of_qureg_te = list_of_register_te -- | Return the length of a 'Qureg'. qureg_length :: Qureg -> Int qureg_length = register_length -- | Return a piece of shape data to represent an /m/-qubit quantum -- register. Please note that the data can only be used as shape; it -- will be undefined at the leaves. qureg_shape :: Int -> Qureg qureg_shape m = qureg_of_qulist_te (replicate m qubit) -- ---------------------------------------------------------------------- -- ** Bit registers -- | The type of 'Bit' registers. The syntax /a/ .!(/i/) is used to -- access the /i/th element of the register /a/. type Bitreg = Register Bit -- | Turn a bit vector into a bit register. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. bitreg_of_bitlist_te :: Bitlist -> Bitreg bitreg_of_bitlist_te = register_of_list_te -- | Turn a bit register into a bit vector. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. bitlist_of_bitreg_te :: Bitreg -> Bitlist bitlist_of_bitreg_te = list_of_register_te -- | Return the length of a 'Bitreg'. bitreg_length :: Bitreg -> Int bitreg_length = register_length -- ---------------------------------------------------------------------- -- ** Boolean registers -- | The type of boolean registers. type Boolreg = Register Bool -- | Turn a bool vector into a bool register. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. boolreg_of_boollist_te :: Boollist -> Boolreg boolreg_of_boollist_te = register_of_list_te -- | Turn a bool register into a bool vector. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. boollist_of_boolreg_te :: Boolreg -> Boollist boollist_of_boolreg_te = list_of_register_te -- | Return the length of a 'Boolreg'. boolreg_length :: Boolreg -> Int boolreg_length = register_length -- ---------------------------------------------------------------------- -- * Special functions for quantum registers -- | Creates a new quantum register, initialized from a list of -- booleans. The conversion is tail-endian, i.e., /r/.!(0) holds the -- tail of the list. qinit_register :: [Bool] -> Circ Qureg qinit_register bs = do qs <- qinit bs return (qureg_of_qulist_te qs) -- | Terminates a quantum register, and assert that its state is as -- specified by the list of booleans. The conversion is tail-endian, -- i.e., /r/.!(0) holds the tail of the list. qterm_register :: [Bool] -> Qureg -> Circ () qterm_register bs r = do let qs = qulist_of_qureg_te r qterm bs qs -- | Measure a quantum register, yielding a list of 'Bit's. qmeasure_register :: Qureg -> Circ [Bit] qmeasure_register r = measure (qulist_of_qureg_te r) -- | Temporarily create a quantum register of size /n/ for use as an -- ancilla. This can be used to introduce an ancilla with a local scope, like this: -- -- > with_ancilla_reg n $ \r -> do { -- > <<<code block using ancilla register r>>> -- > } with_ancilla_reg :: Int -> (Qureg -> Circ a) -> Circ a with_ancilla_reg n f = do let falselist = (take n $ repeat False) q <- qinit_register falselist a <- f q qterm_register falselist q return a -- | Like 'with_ancilla_reg', except also initialize the register as -- specified by a bit vector. In this case, the argument /n/ is not -- required, because it equals the length of the bit vector. When the -- ancilla is terminated at the end of its scope, it is asserted to be -- in the same state it was prepared in. with_ancilla_reg_init :: Boollist -> (Qureg -> Circ a) -> Circ a with_ancilla_reg_init v f = do q <- qinit_register v a <- f q qterm_register v q return a -- ---------------------------------------------------------------------- -- * Special functions for boolean registers -- | @boolreg_of_int m x@: Initialize a bool register directly from an -- integer /x/, regarded as a binary string of length /m/. The -- conversion is little-endian, i.e., the register holds the least -- significant digit at index 0. boolreg_of_int_le :: Integral a => Int -> a -> Boolreg boolreg_of_int_le m x = boolreg_of_boollist_te (boollist_of_int_bh m x) -- | @int_of_boolreg_unsigned_le m r@: Turn a bool register into an -- integer, regarded as a binary string. The conversion is -- little-endian, i.e., the register holds the least significant digit -- at index 0. The integer is unsigned. int_of_boolreg_unsigned_le :: Integral a => Boolreg -> a int_of_boolreg_unsigned_le r = int_of_boollist_unsigned_bh (boollist_of_boolreg_te r) -- ---------------------------------------------------------------------- -- Make 'Qureg' an instance of 'QData' and 'QCData'. type instance QCType x y Qureg = Register x type instance QCType x y Bitreg = Register y type instance QTypeB Boolreg = Qureg instance QCData Qureg where qcdata_mapM shape f g (Register xs) = do ys <- intmap_mapM f xs return (Register ys) qcdata_zip shape q c q' c' (Register xs) (Register ys) e = (Register zs) where zs = intmap_zip_errmsg xs ys (e "register length mismatch") qcdata_promote as xs e | register_length as == register_length xs = as | otherwise = error (e "register length mismatch") instance QCData Bitreg where qcdata_mapM shape f g (Register xs) = do ys <- intmap_mapM g xs return (Register ys) qcdata_zip shape q c q' c' (Register xs) (Register ys) e = (Register zs) where zs = intmap_zip_errmsg xs ys (e "register length mismatch") qcdata_promote as xs e | register_length as == register_length xs = as | otherwise = error (e "register length mismatch") instance Labelable Qureg String where label_rec (Register xs) s = do sequence_ [ label_rec x s `indexed` show i | (i,x) <- IntMap.toList xs ] instance Labelable Bitreg String where label_rec (Register xs) s = do sequence_ [ label_rec x s `indexed` show i | (i,x) <- IntMap.toList xs ]