{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE DeriveDataTypeable #-} -- | This library provides a quantum implementation of fixed-precision real numbers (i.e., with precision determined at circuit-generation time), and classical counterpart types. -- -- Currently still significantly experimental. TODO: -- -- * decide on how much access to provide to underlying representation of 'FPReal'. Full 'fpreal_case', or more like just what’s available through Haskell’s 'RealFloat'? -- -- * decide how to use 'ErrMsg': in internal functions only, or exported ones too? -- -- * many specific TODO’s in code throughout this file. -- -- * write export list. module Quipper.Libraries.FPReal where import Quipper import Quipper.Internal import Quipper.Libraries.Arith import Quipper.Utils.Auxiliary import Control.Monad (foldM) import Data.Maybe (fromJust) import Data.Ratio (numerator, denominator) import Data.Typeable -- =========================================== -- * Fixed-precision real arithmetic: the FPReal family -- | 'FPRealX': a family of datatypes for fixed-precision real numbers. -- (That is, the precision is a parameter, fixed at circuit-generation time.) -- -- 'FPRealX' is based on the family 'XInt' of fixed-length integer types: -- @'FPRealX' /n/ /a/@ represents 2[sup /n/] /a/, where /a/ is some fixed-length integer. -- -- Alternately, in the specific case /x/ = 'Bool', a Haskell 'Double' may be -- used as an 'FPReal', considered as having indeterminate length and exponent. -- This is exactly analogous to the case of indeterminate length in 'XInt' / 'IntM'; for a more -- detailed explanation, see the documentation there. data FPRealX x = FPRealX Int (XInt x) | FPReal_indet Double (Identity IntM (XInt x)) deriving (Show, Typeable) -- | Fixed-precision real parameters. As with 'IntM', the length and/or exponent -- of an 'FPReal' may be indeterminate; such an 'FPReal' may only be used in -- certain contexts, typically either when the length/exponent can be inferred from context -- (e.g., terminating a 'FPRealQ'), or where the result can again be indeterminate. -- -- As with indeterminate 'IntM's, indeterminate 'FPReal's may be used for efficient, -- high-precision classical calculation, and then explicitly or implicitly coerced -- to determinate 'FPReal's when required for interfacing with quantum computation. type FPReal = FPRealX Bool instance Show FPReal where show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")" show (FPReal_indet r id) = show r -- | Fixed-precision reals for quantum circuits. type FPRealQ = FPRealX Qubit instance Show FPRealQ where show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")" show (FPReal_indet _ _) = error "FPRealX: internal error" -- | Fixed-precision reals for classical circuits. type FPRealC = FPRealX Bit {- -- for when Qubit /= Bit: instance Show FPRealC where show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")" show (FPReal_indet _ _) = error "FPRealX: internal error" -} -- ---------------------------------------------------------------------- -- ** Primitive combinators on FPReal -- $ Like 'XInt', the type 'FPReal' is intended to be an abstract data type, -- and all access to it should pass through the functions of this -- section. -- *** Constructors -- | Create an 'FPRealX' from an 'XInt' together with an exponent. fprealx :: Int -> XInt x -> FPRealX x fprealx n x = FPRealX n x -- | Create an indeterminate 'FPReal' from a 'Double'. fpreal_of_double :: Double -> FPReal fpreal_of_double r = FPReal_indet r reflexivity -- *** Destructor -- | If the 'FPRealX' is of determinate exponent, return its exponent and mantissa. -- -- If the 'FPRealX' is indeterminate, return a pair (/r/, /id/), where /r/ is the underlying 'Double', and /id/ is a witness of the fact that /x/ = 'Bool'. -- -- This is a lowest-level access function not intended to be used by -- user-level code, and is not exported. fprealx_case :: FPRealX x -> Either (Int, XInt x) (Double, Identity IntM (XInt x)) fprealx_case (FPRealX n x) = Left (n,x) fprealx_case (FPReal_indet r id) = Right (r, id) -- ---------------------------------------------------------------------- -- ** Other low-level operations -- $ The operations in this section are the only ones intended to use -- 'fprealx_case' directly. -- TODO: fprealx_expt, fprealx_num should probably take ErrMsg arguments, at least for internal use --- I guess only the versions specialized to FPRealQ, FPRealC should be written as though unconditional? Perhaps only these, plus the 'Maybe' version specialized to 'FPReal', should be exported? -- | Extract the exponent of an 'FPRealX', assumed to be determinate. -- -- When /x/ is not 'Bool', this and 'fprealx_num' should be considered the destructors of 'FPRealX x'. fprealx_expt :: FPRealX x -> Int fprealx_expt x = case fprealx_case x of Left (n, _) -> n Right _ -> error "fprealx_expt: indeterminate exponent" -- | Extract the mantissa of an 'FPRealX', assumed to be of determinate exponent. -- -- When /x/ is not 'Bool', this and 'fprealx_num' should be considered the destructors of 'FPRealX x'. fprealx_num :: FPRealX x -> XInt x fprealx_num x = case fprealx_case x of Left (_, xi) -> xi Right _ -> error "fprealx_num: indeterminate exponent" -- | Extract the length (in bits) of an 'FPRealX', assumed to be of determinate exponent and length. fprealx_length :: FPRealX x -> Int fprealx_length x = case fprealx_case x of Left (_, xi) -> fromJust $ xint_maybe_length xi Right _ -> error "fprealx_length: indeterminate exponent" -- | Set the exponent of an 'FPReal' to /n/. This operation is only -- legal if the input (a) has indeterminate exponent or (b) has -- determinate exponent already equal to /m/. In particular, it cannot -- be used to change the exponent from anything other than from -- indeterminate to determinate. -- -- If both arguments already have determinate exponents, and they do not -- coincide, throw an error. The 'String' argument is used as an error -- message in that case. -- Implementation note: the “intm_of_integer” may seem unnecessary, -- but needed so that when /r/ is “undefined”, the result is -- “intm_of_integer undefined” not just “undefined”. fprealx_set_expt :: Int -> FPRealX x -> String -> FPRealX x fprealx_set_expt n xr errstr = case fprealx_case xr of Left (n', xi) -> if n' == n then fprealx n xi else error errstr Right (r, id) -> fprealx n (identity id $ intm_of_integer $ round $ if n >= 0 then (r / 2^n) else (r * 2^(-n))) -- | Return the (possibly indeterminate) exponent of an 'FPRealX'. fprealx_maybe_expt :: FPRealX x -> Maybe (Int) fprealx_maybe_expt xr = case fprealx_case xr of Left (n, _) -> Just n Right _ -> Nothing -- | Given an 'FPReal', return either the exponent and numerator, or else the double it wraps. -- -- A specialized and implementation-hiding wrapper for 'fprealx_case'. -- TODO: should this be exported? fpreal_case :: FPReal -> Either (Int, IntM) (Double) fpreal_case (FPRealX n x) = Left (n, x) fpreal_case (FPReal_indet r _) = Right r -- | Equality test. If both have indeterminate exponent, check equality of underlying 'Double's. -- Otherwise, if exponents are compatible (i.e. both determinate and equal, or one indeterminate), -- check equality of numerators. If exponents are incompatible, throw an error (the test -- should in this case be considered ill-typed). fprealx_equals :: (Eq x) => FPRealX x -> FPRealX x -> Bool fprealx_equals x y = case (fprealx_case x, fprealx_case y) of (Left (n,xi), Left (n',yi)) | n == n' -> xi == yi | otherwise -> error "Equality test on FPRealx: operands must be of equal exponent" (_, Left (m,yi)) -> fprealx_equals (fprealx_set_expt m x "fprealx_equals") y (Left (n,xi), _) -> fprealx_equals x (fprealx_set_expt n y "fprealx_equals") (Right (r, _), Right (r', _)) -> r == r' -- ---------------------------------------------------------------------- -- ** Shape parameters -- | Return a piece of shape data to represent an /l/-qubit quantum -- real with exponent /n/. -- Please note that the data can only be used as shape; it -- will be undefined at the leaves. fprealq_shape :: Int -> Int -> FPRealQ fprealq_shape n l = fprealx n $ qdint_shape l -- | Return a piece of shape data to represent an /l/-bit 'FPRealC', -- with exponent /n/. -- Please note that the data can only be used as shape; it will be -- undefined at the leaves. fprealc_shape :: Int -> Int -> FPRealC fprealc_shape n l = fprealx n $ cint_shape l -- ====================================================================== -- ** Circuit type class instances -- Note: instance declarations do not show up in the documentation -- | Try to set the exponent/length of an 'FPReal' to that of another 'FPRealX' -- value (e.g. an 'FPRealQ', an 'FPRealC', or another 'FPReal'). -- This will fail with an error if both numbers already have determinate -- lengths that don't coincide. In this case, the string argument is -- used as an error message. -- -- The possible “shapes” of 'FPReal's may be seen as a partial order, where -- /s1/ ≤ /s2/ means that values of shape /s1/ are coercible to values of shape -- /s2/. 'fpreal_promote' may be seen as taking the binary /sup/ in this poset. fpreal_promote :: FPReal -> FPRealX x -> ErrMsg -> FPReal fpreal_promote br xr errmsg = case fprealx_maybe_expt xr of Nothing -> br Just n -> let br' = fprealx_set_expt n br (errmsg "FPReal: exponent mismatch") in fprealx n $ intm_promote (fprealx_num br') (fprealx_num xr) (errmsg "FPReal: length mismatch") type instance QCType x y (FPRealX z) = FPRealX (QCType x y z) type instance QTypeB FPReal = FPRealQ instance QCLeaf x => QCData (FPRealX x) where qcdata_mapM shape f g xr = mmap (fprealx (fprealx_expt xr)) $ qcdata_mapM (fprealx_num shape) f g (fprealx_num xr) qcdata_zip shape q c q' c' xr yr e | fprealx_expt xr == fprealx_expt yr = fprealx (fprealx_expt xr) $ qcdata_zip (fprealx_num shape) q c q' c' (fprealx_num xr) (fprealx_num yr) (const $ e "FPRealX: length mismatch") | otherwise = error (e "FPRealX: exponent mismatch") qcdata_promote = fpreal_promote -- Labeling of FPRealX is s[hi-1], ..., s[lo], where lo is the exponent. instance QCLeaf x => Labelable (FPRealX x) String where label_rec xr s = do let n = fprealx_expt xr xi = fprealx_num xr qx = list_of_xint_lh xi sequence_ [ label_rec q s `indexed` show i | (q,i) <- zip qx [n..] ] -- ====================================================================== -- * Classical arithmetic on FPReal -- | Convert an 'FPReal' to a 'Double'. double_of_fpreal :: FPReal -> Double double_of_fpreal xr = case fpreal_case xr of Left (n, xi) -> if n >= 0 then (fromIntegral xi) * (2^n) else (fromIntegral xi) / (2^(abs n)) Right r -> r -- | From a list of 'FPReal's, extract a common shape, provided they have compatible shape -- (i.e. if any have determinate exponent, they must agree; similarly for length), -- and throw an error otherwise. -- -- Can be seen as producing finitary suprema in the partial order of shapes. -- -- The 'FPReal' produced should be considered just a shape; its value is a dummy that -- should never be used (and will throw an error if it is). fpreal_common_shape :: [FPReal] -> ErrMsg -> FPReal fpreal_common_shape xs e = foldl (\x y -> fpreal_promote x y e) (fpreal_of_double (error $ e "fpreal_common_shape: dummy value produced here was later accessed")) xs -- | Auxiliary function for lifting a binary operator from 'Double' -- to 'IntM'. The string argument is the name of the operator, for -- error messages. fpreal_binop :: (Double -> Double -> Double) -> String -> (FPReal -> FPReal -> FPReal) fpreal_binop op opname x y = fpreal_promote (fpreal_of_double $ op (double_of_fpreal x) (double_of_fpreal y)) (fpreal_common_shape [x,y] errmsg) (const "FPReal: internal error (fpreal_binop)") where errmsg = (\s -> "Binary operation " ++ opname ++ " on FPReal: " ++ s) -- | Auxiliary function for lifting a unary operator from 'Double' to -- 'FPReal'. fpreal_unop :: (Double -> Double) -> FPReal -> FPReal fpreal_unop op x = fpreal_promote (fpreal_of_double $ op $ double_of_fpreal x) x (const "FPReal: internal error (fpreal_unop)") ------ -- Classical typeclass instances ------ instance Eq x => Eq (FPRealX x) where (==) = fprealx_equals instance Num FPReal where (+) = fpreal_binop (+) "+" (*) = fpreal_binop (*) "*" (-) = fpreal_binop (-) "-" abs = fpreal_unop abs signum = fpreal_unop signum -- Note: signum on determinate exponent/length FPReals has slightly -- surprising behavior if /n/ ≤ –/l/: this will give -- 0, since then 1 = –1 = 0 mod 2^{l + n}. In other words, the -- output 1.0 or -1.0 is not representable in this fixed-precision format. fromInteger = fpreal_of_double . fromInteger instance Ord FPReal where compare x y = compare (double_of_fpreal x) (double_of_fpreal y) -- TODO: is this the right thing to do? Perhaps we should first check they have -- compatible shapes, and throw an error otherwise. instance Enum FPReal where succ = fpreal_unop succ pred = fpreal_unop pred toEnum = fromIntegral fromEnum = fromEnum . double_of_fpreal instance Real FPReal where toRational = toRational . double_of_fpreal instance Fractional FPReal where fromRational = fpreal_of_double . fromRational recip = fpreal_unop recip {- TODO: something along these lines would probably still be good to give. Work on thinking of good interface. Or maybe not really necessary?? -- | Convert from any 'RealFrac' type (eg 'Rational', 'Float') to 'FPReal', with specified precision and (possibly indeterminate) length. fpreal_from_realfrac_with_precision :: (RealFrac a) => Int -> Maybe Int -> a -> FPReal fpreal_from_realfrac_with_precision n l r = case l of Just l -> FPRealX n $ intm l (round $ r * (2^n)) Nothing -> FPRealX n $ fromIntegral (round $ r * (2^n)) -} instance Floating FPReal where pi = fpreal_of_double pi log = fpreal_unop log exp = fpreal_unop exp sin = fpreal_unop sin cos = fpreal_unop cos sinh = fpreal_unop sinh cosh = fpreal_unop cosh asin = fpreal_unop asin acos = fpreal_unop acos atan = fpreal_unop atan asinh = fpreal_unop asinh acosh = fpreal_unop acosh atanh = fpreal_unop atanh instance RealFrac FPReal where properFraction x = let (x_int, x_frac_double) = properFraction $ double_of_fpreal x in (x_int, fpreal_promote (fpreal_of_double x_frac_double) x (const "FPReal: internal error (properFraction)")) ceiling = ceiling . double_of_fpreal {- TODO: would definitely be good to give. instance RealFloat FPReal -} -- ** Shape/precision control -- | Extend the length of a determinate length and precision 'FPReal' by /m/ high and /n/ low bits, without changing its value. fpreal_pad :: Int -> Int -> FPReal -> FPReal fpreal_pad m n x = case fprealx_maybe_expt x of Nothing -> error e_expt Just x_expt -> let x_num = fprealx_num x in case intm_length x_num of Nothing -> error e_length Just x_length -> fprealx (x_expt - n) (2^n * intm_extend_signed (x_length + m + n) x_num) where e_expt = "fpreal_pad: input of indeterminate exponent" e_length = "fpreal_pad: input of indeterminate length" -- | Discard the top /m/ and bottom /n/ bits of a determinate length and precision 'FPReal'. fpreal_unpad :: Int -> Int -> FPReal -> FPReal fpreal_unpad m n x = let x_bits = boollist_of_intm_bh $ fprealx_num x x_expt = fprealx_expt x x_bits_new = reverse $ drop n $ reverse $ drop m x_bits in fprealx (x_expt + n) (intm_of_boollist_bh x_bits_new) -- | Fix the length of an 'IntM' (to automatically-generated values), if not already determinate. -- -- TODO: belongs in 'Quipper.Libraries.Arith' intm_fix_length_auto :: IntM -> IntM intm_fix_length_auto x = case intm_length x of Just _ -> x Nothing -> let l = (1 +) $ ceiling $ logBase 2 $ fromIntegral $ abs x + (if x >= 0 then 1 else 0) in intm l (fromIntegral x) -- | Fix the precision and length of an 'FPReal' (to automatically-generated values), -- leaving unchanged any parts of the shape that are already set. -- -- TODO: discuss \/ reconsider \/ improve implementation of this. -- Implementation note: aim to use as few bits as possible, while retaining accuracy. -- -- Not clear what should be done in case denominator not a power of 2. However, -- Haskell’s 'Double' has radix 2, so ordinarily denominator always should be a power of 2. -- -- However, this does give pretty high-precision by default! Is that good? Seems expensive in qubits. fpreal_fix_shape_auto :: FPReal -> FPReal fpreal_fix_shape_auto x = case fpreal_case x of Left (e, n) -> fprealx e (intm_fix_length_auto n) Right x -> let r = toRational x d = denominator r in if d == 2^(round $ logBase 2 $ fromIntegral d) then fprealx (negate $ round $ logBase 2 $ fromIntegral d) (intm_fix_length_auto $ fromIntegral $ numerator r) else error "fpreal_fix_shape_auto: not yet fully implemented" -- ====================================================================== -- * Quantum operations: FPRealQ -- ** Shape/precision control -- | Extend the length of an 'FPRealQ' by /m/ high bits and /n/ low bits, without changing its value. fprealq_pad :: Int -> Int -> FPRealQ -> Circ FPRealQ fprealq_pad m n x = do let x_bits = qulist_of_qdint_bh $ fprealx_num x x_expt = fprealx_expt x new_high_bits <- qinit $ replicate m False new_high_bits <- case x_bits of [] -> return new_high_bits (x_high:_) -> mapUnary qnot new_high_bits `controlled` x_high new_low_bits <- qinit $ replicate n False return $ fprealx (x_expt - n) $ qdint_of_qulist_bh $ new_high_bits ++ x_bits ++ new_low_bits -- | Cut off the top /m/ and bottom /n/ bits of an 'FPRealQ', retaining them as explicit garbage. fprealq_unpad :: Int -> Int -> FPRealQ -> Circ (FPRealQ, [Qubit]) fprealq_unpad m n x = let x_bits = qulist_of_qdint_bh $ fprealx_num x x_expt = fprealx_expt x x_bits_new = reverse $ drop n $ reverse $ drop m x_bits garbage = (take m x_bits) ++ (take n $ reverse x_bits) in return (fprealx (x_expt + n) (qdint_of_qulist_bh x_bits_new), garbage) -- | Formally shift an 'FPRealQ' up /n/ bits, i.e. add /n/ to its exponent. fprealq_shift :: Int -> FPRealQ -> FPRealQ fprealq_shift n x = let num = fprealx_num x expt = fprealx_expt x in fprealx (expt + n) num -- ** Quantum arithmetic -- $ Besides the functions appearing here in the documentation, basic operations ('q_add', etc) are also provided as methods of the 'QNum' type class instance; see 'QNum' for documentation of these functions. ------ -- Quantum typeclass instances ------ instance QNum FPRealQ where q_add x y = let ex = fprealx_expt x ey = fprealx_expt y in if ex == ey then do let nx = fprealx_num x ny = fprealx_num y (nx,ny,ns) <- q_add nx ny return ((fprealx ex nx), (fprealx ey ny), (fprealx ex ns)) else error "q_add // FPReal: exponent mismatch in arguments." q_mult x y = -- TODO: to implement let ex = fprealx_expt x ey = fprealx_expt y in if ex == ey then do let nx = fprealx_num x ny = fprealx_num y np <- qinit $ qc_false $ nx (nx,ny,np) <- named_gate "q_mult // FPReal" (nx,ny,np) return ((fprealx ex nx), (fprealx ey ny), (fprealx ex np)) else error "q_mult // FPReal: length mismatch in arguments." q_sub x y = let ex = fprealx_expt x ey = fprealx_expt y in if ex == ey then do let nx = fprealx_num x ny = fprealx_num y (nx,ny,nd) <- q_sub nx ny return ((fprealx ex nx), (fprealx ey ny), (fprealx ex nd)) else error "q_sub // FPReal: length mismatch in arguments." q_abs x = let ex = fprealx_expt x nx = fprealx_num x in do (nx,nx') <- q_abs nx return ((fprealx ex nx), (fprealx ex nx')) q_negate x = let ex = fprealx_expt x nx = fprealx_num x in do (nx,nx') <- q_negate nx return ((fprealx ex nx), (fprealx ex nx')) q_signum = error "FPReal // q_signum: not yet implemented." -- TODO! q_fromQDInt x = do (x,x') <- qc_copy_fun x return (x,(fprealx 0 x')) instance QOrd FPRealQ where q_less x y = let ex = fprealx_expt x ey = fprealx_expt y nx = fprealx_num x ny = fprealx_num y in if ex == ey then do x_less_y <- q_less nx ny return x_less_y else error "q_less // FPReal: length mismatch in arguments." q_leq x y = let ex = fprealx_expt x ey = fprealx_expt y nx = fprealx_num x ny = fprealx_num y in if ex == ey then do x_leq_y <- q_leq nx ny return x_leq_y else error "q_leq // FPReal: length mismatch in arguments." -- | Analogue of 'q_add_in_place', for 'FPRealQ'. fprealq_add_in_place :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ) fprealq_add_in_place x y = let ex = fprealx_expt x ey = fprealx_expt y nx = fprealx_num x ny = fprealx_num y in if ex == ey then do (x,y) <- q_add_in_place nx ny return ((fprealx ex nx), (fprealx ey ny)) else error "q_add_in_place_fprealq // FPReal: length mismatch in arguments." -- | Analogue of 'q_sub_in_place', for 'FPRealQ'. fprealq_sub_in_place :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ) fprealq_sub_in_place x y = let ex = fprealx_expt x ey = fprealx_expt y nx = fprealx_num x ny = fprealx_num y in if ex == ey then do (x,y) <- q_sub_in_place nx ny return ((fprealx ex nx), (fprealx ey ny)) else error "q_sub_in_place_fprealq // FPReal: length mismatch in arguments." -- | Subtract a parameter 'FPReal' from an 'FPRealQ', in place. Assume compatible precision. -- -- Note: highly sub-optimal. TODO: optimize! fprealq_sub_param_in_place :: FPReal -> FPRealQ -> Circ FPRealQ fprealq_sub_param_in_place x qy = with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do (qx, qy) <- fprealq_sub_in_place qx qy return qy) where e_prec = "fprealq_sub_param_in_place: incompatible precision" -- | Add a parameter 'FPReal' to an 'FPRealQ', in place. Assume compatible precision. -- -- Note: highly sub-optimal. TODO: optimize! fprealq_add_param_in_place :: FPReal -> FPRealQ -> Circ FPRealQ fprealq_add_param_in_place x qy = with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do (qx, qy) <- fprealq_add_in_place qx qy return qy) where e_prec = "fprealq_sub_param_in_place: incompatible precision" -- | Multiply an 'FPRealQ' by a parameter 'FPReal'. The parameter may have any shape. fprealq_mult_param_het :: FPReal -> FPRealQ -> Circ (FPRealQ, FPRealQ) fprealq_mult_param_het x_in qy = do let x = fpreal_fix_shape_auto x_in e = fprealx_expt x l = fromJust $ intm_length $ fprealx_num x x_bits = boollist_of_intm_bh $ fprealx_num x qy_expt = fprealx_expt qy qy_num = fprealx_num qy qy_length = qdint_length qy_num prod_bits = [ (is_neg, i) | (x_i, i) <- zip x_bits [e+l-1,e+l-2..e] , x_i == True , let is_neg = i == e+l-1 ] qprod <- if null prod_bits then qinit $ fpreal_promote 0 qy ("internal error: fprealq_mult_param_het promotion: " ++) else with_computed (do let i_max = snd $ head prod_bits i_min = snd $ last prod_bits -- Initialise an accumulating product at 0, with as many bits as may be needed in intermediate calculation: qprod_accum <- qinit $ fprealx (qy_expt + i_min) $ intm (1 + qy_length + i_max - i_min) 0 -- Add the appropriate shifts of qy to it: qprod_accum <- foldM (\qprod_accum (is_neg, i) -> do qy_i <- qc_copy $ fprealq_shift i qy qy_i <- fprealq_pad (1 + i_max - i) (i - i_min) qy_i (qy_i, qprod_accum) <- if is_neg then fprealq_sub_in_place qy_i qprod_accum else fprealq_add_in_place qy_i qprod_accum return qprod_accum) qprod_accum prod_bits -- Now shift/truncate the product to match the input length/precision: qprod_large <- fprealq_pad (max (-i_max) 0) (max i_min 0) qprod_accum (qprod, garbage) <- fprealq_unpad (1 + max i_max 0) (max (-i_min) 0) qprod_large return qprod) -- Copy it for output, before erasing garbage: qc_copy return (qy, qprod) -- | Compare an 'FPRealQ' to a parameter 'FPReal'. Assume compatible precision. -- -- Note: highly sub-optimal. TODO: optimize! fprealq_ge_param :: FPReal -> FPRealQ -> Circ (FPRealQ, Qubit) fprealq_ge_param x qy = with_ancilla_init (fpreal_promote x qy $ const e_prec) (\qx -> do (qx, qy, test) <- q_ge qx qy return (qy, test)) where e_prec = "fprealq_ge_param: incompatible precision" -- | 'fprealq_add_het' /p/ /l/ /qx/ /qy/: add two 'FPRealQ's, of potentially different precisions and lengths, into a fresh one with precision /p/ and length /l/. -- -- TODO: not yet implemented; currently just black box. fprealq_add_het :: Int -> Int -> FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ,FPRealQ) fprealq_add_het p l qx qy = do qz <- qinit (fprealx p (intm l 0)) (qx,qy,qz) <- named_gate "fprealq_add_het" (qx,qy,qz) return (qx,qy,qz) -- | @'fprealq_logBase_internal' /errmsg/ /b/ /h/ /p/ /qx/'@: compute log[sub /b/](/qx/), returning /l/ binary digits before the point and /p/ after. The underlying implementation of the rest of the 'fprealq_log' family. -- -- Behavior on non-positive /qx/: currently unspecified. TODO: decide and fix this. Make assertion/post-selection on positivity? Or treat as unsigned? -- -- Time-complexity (estimated): /O/((log /l/[sup /qx/] + log log /b/)(/l/[sup /qx/] + (/h/+/p/)[sup 2])). fprealq_logBase_internal :: (Floating a, RealFrac a) => ErrMsg -> a -> Int -> Int -> FPRealQ -> Circ (FPRealQ, FPRealQ) fprealq_logBase_internal e b h_out p_out x = if h_out + p_out < 0 then error $ e "negative length specified." else if b <= 0 then error $ e "base of logarithm must be > 0" else do y <- with_computed (do -- Shift x into the interval [0,1], and pad it for intermediate calculation: let l_in = qdint_length $ fprealx_num x e_in = fprealx_expt x l_in_pad = 1 + l_in `div` 2 p_in_pad = p_out - (floor $ logBase 2 $ log b) + 1 x_0 <- fprealq_pad l_in_pad p_in_pad $ fprealx (1 - l_in) $ fprealx_num x -- Bound the length and precision needed for computing log_2 x' to precision p_out: let y_size_bound = 1 + (ceiling $ logBase 2 $ fromIntegral $ l_in - 1) y_h_pad = max 0 (y_size_bound - h_out) y_p_pad = 2 + (ceiling $ logBase 2 $ fromIntegral $ l_in_pad + p_in_pad - 1) y_0 <- qinit $ fprealx (- p_out - y_p_pad) $ intm (y_h_pad + h_out + p_out + y_p_pad) 0 -- Iteratively compute log x_0, starting with (x0,y0): let ks_big = 2^(l_in `div` 2) : (reverse $ [ 2^(2^i) | i <- [0..(ceiling $ logBase 2 $ (fromIntegral l_in) / 2) - 1] ]) ks_small = [ (2^i + 1)/(2^i) | i <- [1..p_out - (floor $ logBase 2 $ log b) + 1]] -- bound in ks_small chosen to ensure logBase b k_fin < 2^(-p_out) (x_fin,y_fin) <- foldM (\(xi,yi) ki -> do (xi, xi_ki) <- fprealq_mult_param_het ki xi (xi_ki, test1) <- fprealq_ge_param 1 xi_ki (xi_ki, xi, test2) <- q_gt xi_ki xi (xi1, yi1) <- qc_copy (xi,yi) (xi1,xi) <- controlled_not xi1 xi `controlled` test1 .&&. test2 (xi1,xi_ki) <- controlled_not xi1 xi_ki `controlled` test1 .&&. test2 yi1 <- fprealq_sub_param_in_place (logBase (realToFrac b) ki) yi1 `controlled` test1 .&&. test2 return (xi1, yi1)) (x_0,y_0) (ks_big ++ ks_small) -- Correct for the shift between x and x_, then unpad y to the output precision: y_fin <- fprealq_add_param_in_place ((log 2 / log (realToFrac b)) * (fromIntegral $ l_in + e_in - 1)) y_fin (y_fin, garbage) <- fprealq_unpad y_h_pad y_p_pad y_fin return y_fin) qc_copy return (x,y) -- | Compute the natural log of an 'FPRealQ', with length and precision as in the input. -- -- Note: the behavior on negative inputs is unspecified. fprealq_log :: FPRealQ -> Circ (FPRealQ,FPRealQ) fprealq_log x = let h = fprealx_length x + fprealx_expt x l = - fprealx_expt x in fprealq_logBase_internal ("fprealq_log: " ++ ) (exp 1) h l x -- | Compute the log (to arbitrary base) of an 'FPRealQ', with length and precision as in the input. -- -- Note: the behavior on negative inputs is unspecified. fprealq_logBase :: (Floating a, RealFrac a) => a -> FPRealQ -> Circ (FPRealQ,FPRealQ) fprealq_logBase b x = let h = fprealx_length x + fprealx_expt x l = - fprealx_expt x in fprealq_logBase_internal ("fprealq_logBase: " ++ ) b h l x -- | @'fprealq_log_with_shape' /x/ /y/@: compute the natural log /y/, with length and precision of /x/. -- -- Note: the behavior on negative inputs is unspecified. fprealq_log_with_shape :: FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ) fprealq_log_with_shape x = let h = fprealx_length x + fprealx_expt x l = - fprealx_expt x in fprealq_logBase_internal ("fprealq_log_with_shape: " ++ ) (exp 1) h l -- | @'fprealq_log_with_shape' /b/ /x/ /y/@: compute log[sup /b/] /y/, with length and precision of /x/. -- -- Note: the behavior on negative inputs is unspecified. fprealq_logBase_with_shape :: (Floating a, RealFrac a) => a -> FPRealQ -> FPRealQ -> Circ (FPRealQ,FPRealQ) fprealq_logBase_with_shape b x = let h = fprealx_length x + fprealx_expt x l = - fprealx_expt x in fprealq_logBase_internal ("fprealq_logBase_with_shape: " ++ ) b h l