{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LINE 1 "Quipper/Libraries/FPReal.hs" #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE DeriveDataTypeable #-}
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
data FPRealX x = FPRealX Int (XInt x) | FPReal_indet Double (Identity IntM (XInt x))
deriving (Show, Typeable)
type FPReal = FPRealX Bool
instance Show FPReal where
show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"
show (FPReal_indet r id) = show r
type FPRealQ = FPRealX Qubit
instance Show FPRealQ where
show (FPRealX n x) = "fprealx " ++ show n ++ " (" ++ show x ++ ")"
show (FPReal_indet _ _) = error "FPRealX: internal error"
type FPRealC = FPRealX Bit
fprealx :: Int -> XInt x -> FPRealX x
fprealx n x = FPRealX n x
fpreal_of_double :: Double -> FPReal
fpreal_of_double r = FPReal_indet r reflexivity
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)
fprealx_expt :: FPRealX x -> Int
fprealx_expt x =
case fprealx_case x of
Left (n, _) -> n
Right _ -> error "fprealx_expt: indeterminate exponent"
fprealx_num :: FPRealX x -> XInt x
fprealx_num x =
case fprealx_case x of
Left (_, xi) -> xi
Right _ -> error "fprealx_num: indeterminate exponent"
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"
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)))
fprealx_maybe_expt :: FPRealX x -> Maybe (Int)
fprealx_maybe_expt xr = case fprealx_case xr of
Left (n, _) -> Just n
Right _ -> Nothing
fpreal_case :: FPReal -> Either (Int, IntM) (Double)
fpreal_case (FPRealX n x) = Left (n, x)
fpreal_case (FPReal_indet r _) = Right r
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'
fprealq_shape :: Int -> Int -> FPRealQ
fprealq_shape n l = fprealx n $ qdint_shape l
fprealc_shape :: Int -> Int -> FPRealC
fprealc_shape n l = fprealx n $ cint_shape l
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
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..] ]
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
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
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)
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)")
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
fromInteger = fpreal_of_double . fromInteger
instance Ord FPReal where
compare x y = compare (double_of_fpreal x) (double_of_fpreal y)
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
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
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"
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)
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)
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"
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
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)
fprealq_shift :: Int -> FPRealQ -> FPRealQ
fprealq_shift n x =
let num = fprealx_num x
expt = fprealx_expt x
in fprealx (expt + n) num
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 =
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."
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."
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."
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."
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"
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"
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
qprod_accum <- qinit $ fprealx (qy_expt + i_min) $ intm (1 + qy_length + i_max - i_min) 0
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
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)
qc_copy
return (qy, qprod)
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 :: 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 :: (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
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
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
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]]
(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)
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)
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
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 :: 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_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