{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveDataTypeable #-} -- | This modules implements a library for fixed-points real numbers. module Quipper.Algorithms.QLS.QDouble where import qualified Data.Ratio as Ratio import Data.Typeable import Quipper import Quipper.Internal import Quipper.Libraries.Arith import Quipper.Libraries.Simulation import Quipper.Algorithms.QLS.Utils import Quipper.Algorithms.QLS.QSignedInt import Quipper.Utils.Auxiliary (getId, mmap) -- * Signed fixed point type -- | Data type for fixed point arithmetic. -- -- 'XDouble' /k/ /n/ represents the number /n/⋅2[sup -/k/], where /n/ -- is a signed integer and /k/ is an integer parameter. -- -- We refer to /k/ as the /exponent/ of the fixed point number. When -- we speak of the /length/, we mean the total number of digits -- (excluding the sign), i.e., the length, in binary digits, of the -- underlying /n/. data XDouble x = XDouble Int (SignedInt x) deriving (Show, Typeable) -- | The parameter type of fixed-point numbers. type FDouble = XDouble Bool -- | The quantum type of fixed-point numbers. type QDouble = XDouble Qubit -- | The classical type of fixed-point numbers. type CDouble = XDouble Bit -- ---------------------------------------------------------------------- -- * Auxiliary definitions -- | Compute the power of an integer by a non-negative integer. integer_power :: Int -> Int -> Integer integer_power x y = (fromIntegral x) ^ y -- | Compute the power of an integer by another, possibly negative one. double_power :: Int -> Int -> Double double_power x y | y >= 0 = (fromIntegral x) ^ y | otherwise = 1 / ((fromIntegral x) ^ (-y)) -- | Divide one integer by another, and round the result to the closest -- integer. If the result is half way between two integers, round to -- the even one (this is the same behavior as Haskell's 'round' -- function). This function has unlimited precision. div_round :: Integer -> Integer -> Integer div_round x y = round (x Ratio.% y) -- ---------------------------------------------------------------------- -- * Operation for length and exponent -- ---------------------------------------------------------------------- -- ** Generic functions for XDouble -- | Return the exponent /k/ of an 'XDouble'. xdouble_exponent :: XDouble x -> Int xdouble_exponent (XDouble k _) = k -- | Return the length /m/ of an 'XDouble'. xdouble_length :: XDouble x -> Int xdouble_length (XDouble _ n) = sint_length n -- | Return the \"extent\" of an 'XDouble'. The extent of a fixed-point -- number /x/ is, by definition, the pair (/hi/,/lo/) of integers such -- that the most significant bit of /x/ has positional index /hi/-1 -- (in other words, the value of this bit is 2[sup /hi/-1]), and the -- least significant bit of /x/ has positional index /lo/ (in other -- words, the value of this bit is 2[sup /lo/]. Typically, but not -- necessarily, /hi/ ≥ 0 and /lo/ ≤ 0. In this case, one can also -- think of /hi/ as \"the number of digits before the radix point\" -- and −/lo/ as \"the number of digits after the radix point\". -- -- The exponent /k/, length /m/, and extent (/hi/,/lo/) are related by -- /k/=-/lo/ and /m/=/hi/−/lo/. -- -- Examples: -- -- * a number represented in the form /xxxx.yyy/ has extent (4,-3), -- exponent 3, and length 7. -- -- * a number represented in the form /xxxx000./ has extent (7,3), -- exponent -3, and length 4. -- -- * a number represented in the form /.000xxxx/ has extent (-3,-7), -- exponent 7, and length 4. -- -- If we regard extents as intervals, ordered by inclusion, then it is -- always possible to losslessly cast a fixed-point number from a -- smaller to a larger extent. xdouble_extent :: XDouble x -> (Int, Int) xdouble_extent x = (m-k, -k) where m = xdouble_length x k = xdouble_exponent x -- | Add /n/ zeros to the high bits of the 'XDouble'. This sends -- /xxx.yyy/ to 000/xxx.yyy/. This increases the length without changing -- the exponent or value. xdouble_pad_left :: (Monad m) => m x -> Int -> XDouble x -> m (XDouble x) xdouble_pad_left zero n (XDouble k (SInt digits s)) = do pad <- sequence $ replicate n zero let digits' = pad ++ digits return $ XDouble k (SInt (digits') s) -- | Add /n/ zeros to the low bits of the 'XDouble'. This sends -- /xxx.yyy/ to /xxx.yyy000/. This increases the length and the -- exponent without changing the value. xdouble_pad_right :: (Monad m) => m x -> Int -> XDouble x -> m (XDouble x) xdouble_pad_right zero n (XDouble k (SInt digits s)) = do pad <- sequence $ replicate n zero let digits' = digits ++ pad return $ XDouble (k+n) (SInt (digits') s) -- | Pad an 'XDouble' on both sides to reach the desired extent. This -- increases the length and exponent without changing the value (it is -- a lossless operation). It is an error to call this function if the -- selected extent does not contain the extent of the input 'XDouble'. xdouble_pad_to_extent :: (Monad m) => m x -> (Int, Int) -> XDouble x -> m (XDouble x) xdouble_pad_to_extent zero (hi, lo) x | lo <= lo_x && hi_x <= hi = xdouble_pad_left zero (hi - hi_x) =<< xdouble_pad_right zero (lo_x - lo) x | otherwise = error "qdouble_pad_to_extent: bad extent" where (hi_x, lo_x) = xdouble_extent x -- | Remove the /n/ low bits of an 'XDouble'. This sends /xxx.yyyzzz/ -- to /xxx.yyy/. It is an error to call this function when the -- 'XDouble' has fewer than /n/ digits. xdouble_truncate_right :: Int -> QDouble -> QDouble xdouble_truncate_right n (XDouble k (SInt digits s)) = (XDouble k' (SInt digits' s)) where k' = k - n digits' | length digits >= n = reverse $ drop n $ reverse digits | otherwise = error "xdouble_truncate_right" -- | Convert a 'SignedInt' to an 'XDouble' with exponent 0. xdouble_of_sint :: SignedInt x -> XDouble x xdouble_of_sint n = XDouble 0 n -- ---------------------------------------------------------------------- -- ** Special cases for QDouble -- | Add /n/ zeros to the high bits of the 'QDouble'. This sends -- /xxx.yyy/ to 000/xxx.yyy/. This increases the length without -- changing the exponent or value. This function does not return a -- fresh copy; it reuses part of its input. qdouble_pad_left :: Int -> QDouble -> Circ QDouble qdouble_pad_left = xdouble_pad_left (qinit False) -- | Add /n/ zeros to the low bits of the 'QDouble'. This sends -- /xxx.yyy/ to /xxx.yyy000/. This increases the length and the -- exponent without changing the value. This function does not return -- a fresh copy; it reuses part of its input. qdouble_pad_right :: Int -> QDouble -> Circ QDouble qdouble_pad_right = xdouble_pad_right (qinit False) -- | Pad a 'QDouble' on both sides to reach the desired extent. This -- increases the length and exponent without changing the value (it is -- a lossless operation). It is an error to call this function if the -- selected extent does not contain the extent of the input -- 'QDouble'. This function does not return a fresh copy; it reuses -- part of its input. qdouble_pad_to_extent :: (Int, Int) -> QDouble -> Circ QDouble qdouble_pad_to_extent = xdouble_pad_to_extent (qinit False) -- | Remove the /n/ low bits of a 'QDouble'. This sends /xxx.yyyzzz/ -- to /xxx.yyy/. Note that the /n/ low qubits are not terminated and -- become garbage. It is an error to call this function when the -- 'QDouble' has fewer than /n/ digits. qdouble_truncate_right :: Int -> QDouble -> QDouble qdouble_truncate_right = xdouble_truncate_right -- ---------------------------------------------------------------------- -- ** Special cases for FDouble -- | Add /n/ zeros to the low bits of the 'FDouble'. This sends -- /xxx.yyy/ to /xxx.yyy000/. This increases the length and the -- exponent without changing the value. fdouble_pad_right :: Int -> FDouble -> FDouble fdouble_pad_right k x = getId $ xdouble_pad_right (return False) k x -- | Pad a 'FDouble' on both sides to reach the desired extent. This -- increases the length and exponent without changing the value (it is -- a lossless operation). It is an error to call this function if the -- selected extent does not contain the extent of the input 'FDouble'. fdouble_pad_to_extent :: (Int, Int) -> FDouble -> FDouble fdouble_pad_to_extent extent x = getId $ xdouble_pad_to_extent (return False) extent x -- ---------------------------------------------------------------------- -- * Operations for FDouble -- ---------------------------------------------------------------------- -- ** Casts -- | @'fdouble_of_double' /k/ /m/ /x/@: Convert /x/ to an 'FDouble' of -- exponent /k/ and length /m/ ≥ 0. Note that the exponent does not -- need to be between 0 and /m/; it can even be negative. fdouble_of_double :: Int -> Int -> Double -> FDouble fdouble_of_double k m x | abs n >= integer_power 2 m = error "fdouble_of_double: number too large" | otherwise = XDouble k (fsint_of_integer m n) where d = double_power 2 k n = round (d * x) -- | Convert an 'FDouble' to a 'Double'. double_of_fdouble :: FDouble -> Double double_of_fdouble (XDouble k n) = (fromIntegral x) / (double_power 2 k) where x = integer_of_fsint n -- | Convert an 'FSignedInt' to an 'FDouble' with exponent 0. fdouble_of_fsint :: FSignedInt -> FDouble fdouble_of_fsint = xdouble_of_sint -- | Make an 'FDouble' value of exponent /k/, length /m/, and value /a/2[sup /-k/]. fdouble_of_integer :: Int -> Int -> Integer -> FDouble fdouble_of_integer k m a = XDouble k (fsint_of_integer m a) -- | Construct a 'Double' from an 'FDouble', using some arbitrary -- method to guess the length and exponent. fdouble :: Double -> FDouble fdouble x = fromRational $ toRational x -- | Convert an 'FDouble' to a string in human-readable form. show_fdouble :: FDouble -> String show_fdouble x@(XDouble k (SInt digits s)) = sign ++ binary ++ " (" ++ (show float) ++ ")" where float = double_of_fdouble x sign = if s then "-" else "+" m = length digits binary_full = [ if h then '1' else '0' | h <- digits ] binary | k < 0 = binary_full ++ "e" ++ show (-k) | k > m = "." ++ binary_full ++ "e" ++ show (m-k) | otherwise = binary1 ++ "." ++ binary2 (binary1,binary2) = splitAt ((fromIntegral m) - k) binary_full -- ---------------------------------------------------------------------- -- ** Type class instances -- $ We make 'FDouble' an instance of 'Eq', 'Ord', 'Real', 'Num', -- 'Fractional', and 'RealFrac'. See the source code for details. -- Note: none of the arithmetic operations pass via the native -- 'Double' type, and therefore they are not subject to arbitrary -- precision limits. However, most operations set the precision of the -- result to the maximum precision of the inputs. -- | Express a pair of 'FDouble' values as a pair of 'FSignedInt's with a -- common exponent. fdouble_align :: FDouble -> FDouble -> (Int, FSignedInt, FSignedInt) fdouble_align (XDouble h m) (XDouble k n) | h <= k = (k, fsint_shift (k-h) m, n) | otherwise = (h, m, fsint_shift (h-k) n) instance Eq FDouble where x == y = m == n where (k,m,n) = fdouble_align x y instance Ord FDouble where compare x y = compare m n where (k,m,n) = fdouble_align x y instance Real FDouble where toRational (XDouble h n) | h >= 0 = (Ratio.%) nx (integer_power 2 h) | otherwise = fromInteger (nx * integer_power 2 (-h)) where nx = integer_of_fsint n instance Num FDouble where -- Additive operations set the exponent to the maximum of the two -- exponents, and extend the length if necessary. Signum keeps the -- length but resets the exponent to 0. x + y = XDouble k (m + n) where (k,m,n) = fdouble_align x y x - y = XDouble k (m - n) where (k,m,n) = fdouble_align x y abs x = XDouble k (abs m) where XDouble k m = x signum x = fdouble_of_fsint (signum m) where XDouble k m = x -- fromInteger uses the fixed exponent 'after_radix_length'. fromInteger = fdouble_pad_right after_radix_length . fdouble_of_fsint . fromInteger -- Multiplication sets the extent to the maximum of the two extents. x * y | k >= 0 = fdouble_of_integer k len (a*b `div_round` integer_power 2 k) | otherwise = fdouble_of_integer k len (a*b * integer_power 2 (-k)) where (k,m,n) = fdouble_align x y a = integer_of_fsint m b = integer_of_fsint n len = max (sint_length m) (sint_length n) instance Fractional FDouble where -- Division sets the extent to the maximum of the two extents. x / y | k >= 0 = fdouble_of_integer k len ((a * integer_power 2 k) `div_round` b) | otherwise = fdouble_of_integer k len (a `div_round` (b * integer_power 2 (-k))) where (k,m,n) = fdouble_align x y a = integer_of_fsint m b = integer_of_fsint n len = max (sint_length m) (sint_length n) fromRational x = fdouble_of_double before_radix_length (before_radix_length+after_radix_length) ((fromInteger $ Ratio.numerator x) / (fromInteger $ Ratio.denominator x)) instance RealFrac FDouble where properFraction x | k <= 0 = (0, x) | otherwise = (fromInteger q, fdouble_of_integer k len r) where XDouble k m = x a = integer_of_fsint m len = sint_length m (q, r) = a `quotRem` integer_power 2 k -- ---------------------------------------------------------------------- -- Operations for QDouble -- ---------------------------------------------------------------------- -- QCData instance type instance QCType x y (XDouble z) = XDouble (QCType x y z) type instance QTypeB FDouble = QDouble instance QCLeaf x => QCData (XDouble x) where qcdata_mapM ~(XDouble _ shape) f g (XDouble n xs) = mmap (XDouble n) $ qcdata_mapM shape f g xs qcdata_zip ~(XDouble _ shape) q c q' c' (XDouble n xs) (XDouble m ys) e | n == m = XDouble n $ qcdata_zip shape q c q' c' xs ys (const $ e "XDouble length mismatch") | otherwise = error (e "XDouble exponent mismatch") qcdata_promote (XDouble n b) (XDouble m q) e | n == m = XDouble n $ qcdata_promote b q (const $ e "XDouble length mismatch") | otherwise = error (e "XDouble exponent mismatch") -- Labeling of QDouble is s.sign, s[hi-1], ..., s[lo], where lo = -k. instance QCLeaf x => Labelable (XDouble x) String where label_rec (XDouble k (SInt digits sign)) s = do label_rec sign s `dotted_indexed` "sign" sequence_ [ label_rec d s `indexed` show i | (d,i) <- zip rdigits [-k,-k+1..] ] where rdigits = reverse digits instance CircLiftingUnpack (Circ QDouble) (Circ QDouble) where pack x = x unpack x = x -- ---------------------------------------------------------------------- -- * Casts -- | Convert a 'QSignedInt' to a 'QDouble' with exponent 0. This -- function does not return a fresh copy; instead, it uses the input -- qubits. qdouble_of_qsint :: QSignedInt -> Circ QDouble qdouble_of_qsint x = do (_,y) <- qc_copy_fun x return $ xdouble_of_sint y -- ---------------------------------------------------------------------- -- ** Type class instances -- $ We make 'QDouble' an instance of 'QOrd'. instance QOrd QDouble where q_less x y | kx < ky = do x <- qdouble_pad_right (ky-kx) x compare_right x y | kx > ky = do y <- qdouble_pad_right (kx-ky) y compare_right x y | otherwise = compare_right x y where kx = xdouble_exponent x ky = xdouble_exponent y -- compare_right assumes matching exponent, i.e., both bit strings -- are right-aligned. compare_right :: QDouble -> QDouble -> Circ Qubit compare_right (XDouble _ n) (XDouble _ m) = q_less n m -- | Express a pair of 'QDouble' values as a pair of 'QSignedInt's with a -- common exponent. qdouble_align :: QDouble -> QDouble -> Circ (Int, QSignedInt, QSignedInt) qdouble_align (XDouble h m) (XDouble k n) | h <= k = do m <- qsint_shift (k-h) m return (k, m, n) | otherwise = do n <- qsint_shift (h-k) n return (k, m, n) instance QNum QDouble where -- Additive operations set the exponent to the maximum of the two -- exponents, and extend the length if necessary. Signum keeps the -- length but resets the exponent to 0. q_add x y = do (k,m,n) <- qdouble_align x y (_, _, r) <- q_add m n return (x, y, XDouble k r) q_sub x y = do (k,m,n) <- qdouble_align x y (_, _, r) <- q_sub m n return (x, y, XDouble k r) q_abs x = do let XDouble k m = x (_, r) <- q_abs m return (x, XDouble k r) q_negate x = do let XDouble k m = x (_, r) <- q_negate m return (x, XDouble k r) q_signum x = do let XDouble k m = x (_, r) <- q_signum m y <- qdouble_of_qsint r return (x, y) q_fromQDInt x = do (_,y) <- q_fromQDInt x z <- qdouble_of_qsint y w <- qdouble_pad_right after_radix_length z return (x,w) -- 'q_mult' currently does not work with negative exponents. q_mult x y = let m = max (xdouble_exponent x) (xdouble_exponent y) in let s = (xdouble_exponent x) + (xdouble_exponent y) in do ext_x <- qdouble_pad_left m x ext_y <- qdouble_pad_left m y let XDouble kx nx = ext_x let XDouble ky ny = ext_y (_,_,nz) <- q_mult nx ny let z = XDouble s nz return (x,y,qdouble_truncate_right (s-m) z) -- ---------------------------------------------------------------------- -- * Other functions -- Developer note: the following instances are to be able to use -- named_gate_safe, which is probably not at all safe. Also, it -- requires us to define an Eq instance for every type where this is -- used. The instances are defined as follows, and are considered a -- temporary hack. -- Textual equality for 'QDouble'. instance Eq QDouble where XDouble a b == XDouble a' b' = (a,b) == (a',b') -- Textual equality for 'QSignedInt'. instance Eq QSignedInt where (SInt b c) == (SInt b' c') = (b,c) == (b',c') -- | Coercion from 'QSignedInt' to 'QDouble'. q_fromIntegral :: QSignedInt -> Circ QDouble q_fromIntegral x = do x' <- qsint_shift after_radix_length x return $ XDouble after_radix_length x' -- | QDouble of 'ceiling': coercion from 'QDouble' to 'QSignedInt'. -- Note: This rounds to 0 and not to infinity. q_ceiling :: QDouble -> Circ QSignedInt q_ceiling (XDouble k (SInt x b)) = return $ SInt (reverse . drop k . reverse $ x) b -- | Real division with 'QDouble'. q_div_real :: QDouble -> QDouble -> Circ QDouble q_div_real (XDouble kx (SInt x bx)) (XDouble ky (SInt y by)) = if (kx /= ky) then error "q_div_real" else let k = kx in do pad_x <- qinit (replicate k False) pad_y <- qinit (replicate k False) let ext_x = x ++ pad_x let ext_y = pad_y ++ y (_,_,ext_z) <- q_quot (qdint_of_qulist_bh ext_x) (qdint_of_qulist_bh ext_y) let z = reverse $ drop k $ reverse $ qulist_of_qdint_bh ext_z bz <- qinit False qnot_at bz `controlled` bx qnot_at bz `controlled` by return (XDouble k (SInt z bz)) my_test = let x = fromRational $ toRational (12345.34) in let y = fromRational $ toRational (323.1) in let last (x,y,z) = z in do putStrLn $ show_fdouble x putStrLn $ show_fdouble y putStrLn $ show_fdouble $ snd $ run_classical_generic q_negate x instance Num (FDouble,FDouble) where x + y = undefined x * y = undefined x - y = undefined fromInteger x = undefined abs x = undefined signum x = undefined instance QNum (QDouble,QDouble) where q_add (x1,x2) (y1,y2) = do (_,_,z1) <- q_add x1 y1 (_,_,z2) <- q_add x2 y2 return ((x1,x2),(y1,y2),(z1,z2)) q_mult (x1,x2) (y1,y2) = do (_,_,a) <- q_mult x1 y1 (_,_,b) <- q_mult x2 y2 (_,_,z1) <- q_sub a b (_,_,c) <- q_mult x1 y2 (_,_,d) <- q_mult x2 y1 (_,_,z2) <- q_add c d return ((x1,x2),(y1,y2),(z1,z2)) q_sub (x1,x2) (y1,y2) = do (_,_,z1) <- q_sub x1 y1 (_,_,z2) <- q_sub x2 y2 return ((x1,x2),(y1,y2),(z1,z2)) q_abs x = do z <- qinit $ qc_false x; named_gate "abs" (x,z) q_negate x = do z <- qinit $ qc_false x; named_gate "neg" (x,z) q_signum x = do z <- qinit $ qc_false x; named_gate "sigNum" (x,z) q_fromQDInt x = undefined