diff --git a/src/HashedExpression/Operation.hs b/src/HashedExpression/Operation.hs index e977d3d..2fb5d54 100644 --- a/src/HashedExpression/Operation.hs +++ b/src/HashedExpression/Operation.hs @@ -16,55 +16,93 @@ import HashedExpression.Internal.Node import HashedExpression.Internal.Utils import Prelude hiding ((^)) --- | This is a class for ... --- --- @ --- x ^ 0 --- @ +{- + This module overloads and redefines operations to use with expressions, + defining additional operations as necessary as well as providing the means + to create multidimensional constants and variables +-} + + -- @ + -- x ^ 0 + -- @ instance (DimensionType d, NumType et) => PowerOp (Expression d et) Int where -- | This is the power method (^) :: Expression d et -> Int -> Expression d et (^) e1 x = applyUnary (unary (Power x) `hasShape` expressionShape e1) e1 ------------------------------------------------------------------------------- +-- | Converts a double-precision floating-point number to a real-number expression with dimension constraint `d` fromDouble :: forall d. ToShape d => Double -> (Expression d R) + -- @ + -- (fromDouble 15) :: Expression Scalar R + -- @ fromDouble value = Expression h (fromList [(h, node)]) where node = (toShape (Proxy @d), Const value) h = hash node -------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-- | Basic operations on Num class expressions with dimension constraint `d` instance ToShape d => Num (Expression d R) where - e1 + e2 = + -- TODO Tensor discussion, how do we comment this below with mathematical rigour? + -- @ + -- let e1 = (fromInteger 11) :: Expression Scalar R + -- let e2 = (fromInteger 12) :: Expression Scalar R + -- e1 `binary operator` e2 + -- `unary operator` e1 + -- @ + + -- | Overload operators and define common transformations + e1 + e2 = -- | Sum two expressions iff they have the same dimension let op = naryET Sum ElementDefault `hasShape` expressionShape e1 in ensureSameShape e1 e2 $ applyBinary op e1 e2 - e1 * e2 = + e1 * e2 = -- | Multiply two expressions iff they have the same dimension let op = naryET Mul ElementDefault `hasShape` expressionShape e1 in ensureSameShape e1 e2 $ applyBinary op e1 e2 - negate = + negate = -- | Unary minus applied to an expression let op = unaryET Neg ElementDefault in applyUnary $ unaryET Neg ElementDefault - fromInteger val = fromDouble $ fromIntegral val - abs = error "TODO: abs" + fromInteger val = fromDouble $ fromIntegral val -- | Integer to real-number expression + abs = error "TODO: abs" -- | Absolute value of expression signum = error "Not applicable to tensor" ------------------------------------------------------------------------------- +-- | Define division operation and representation for real-number fractional expressions with dimension constraint `d` instance ToShape d => Fractional (Expression d R) where - e1 / e2 = ensureSameShape e1 e2 $ e1 * e2 ^ (-1) - fromRational r = fromDouble $ fromRational r + -- TODO e2 can be a 0-equivalent valued expression + -- @ + -- let e1 = (fromRational 11) :: Expression Scalar R + -- let e2 = (fromRational 12) :: Expression Scalar R + -- e1 / e2 + -- @ + + -- | Overload operators and define common transformations + e1 / e2 = ensureSameShape e1 e2 $ e1 * e2 ^ (-1) -- | Divide two compatible expressions of dimension `d` + fromRational r = fromDouble $ fromRational r -- | Rational number to Fractional expression ------------------------------------------------------------------------------- +-- | Represent common functions for real-number floating-point expressions with dimension constraint `d` instance ToShape d => Floating (Expression d R) where - pi = fromDouble $ pi - sqrt = applyUnary (unary Sqrt) - exp = applyUnary (unary Exp) - log = applyUnary (unary Log) + -- TODO: Going outside domain not undefined or complex (sqrt, log, etc.) + -- but rather Expression Scalar R... intentional? + -- @ + -- let val = (fromDouble 1.2345) :: Expression Scalar R + -- `function` val + -- @ + + -- | Overload exponential, log functions and pi + pi = fromDouble $ pi -- | Constant pi overloaded into Expression type + sqrt = applyUnary (unary Sqrt) -- | Square root + exp = applyUnary (unary Exp) -- | Exponential function, e^x + log = applyUnary (unary Log) -- | Natural logarithm + -- | Trignometric functions sin = applyUnary (unary Sin) cos = applyUnary (unary Cos) tan = applyUnary (unary Tan) asin = applyUnary (unary Asin) acos = applyUnary (unary Acos) atan = applyUnary (unary Atan) + -- | Hyperbolic trig functions sinh = applyUnary (unary Sinh) cosh = applyUnary (unary Cosh) tanh = applyUnary (unary Tanh) @@ -73,39 +111,67 @@ instance ToShape d => Floating (Expression d R) where atanh = applyUnary (unary Atanh) ------------------------------------------------------------------------------- +-- | Basic operations on complex-number expressions with dimension constraint `d` instance ToShape d => Num (Expression d C) where - e1 + e2 = + -- TODO: Tensor discussion, how do we comment this below with mathematical rigour? + -- @ + -- let e1 = ((fromDouble 10) +: fromIntegral 1) :: Expression Scalar C + -- let e2 = ((fromDouble 15) +: fromIntegral 3) :: Expression Scalar C + -- e1 `binary operation` e2 + -- `unary operation` e1 + -- @ + + -- | Overload operators and define common transformations + e1 + e2 = -- | Sum two complex expressions iff they have the same dimension let op = naryET Sum ElementDefault `hasShape` expressionShape e1 in ensureSameShape e1 e2 $ applyBinary op e1 e2 - e1 * e2 = + e1 * e2 = -- | Multiply two complex expressions iff they have the same dimension let op = naryET Mul ElementDefault `hasShape` expressionShape e1 in ensureSameShape e1 e2 $ applyBinary op e1 e2 - negate = + negate = -- | Unary minus application to a complex expression let op = unaryET Neg ElementDefault in applyUnary $ unaryET Neg ElementDefault - fromInteger val = fromIntegral val +: fromIntegral 0 - abs = error "TODO: abs" + -- | Transformation to complex expression with val as real part, + -- and 0i imaginary part + fromInteger val = fromIntegral val +: fromIntegral 0 + + abs = error "TODO: abs" -- | Absolute value of expression signum = error "Not applicable to tensor" ------------------------------------------------------------------------------- +-- | Define division operation and transformation to complex fractional expression from rational real number with dimension constraint `d` instance ToShape d => Fractional (Expression d C) where - e1 / e2 = ensureSameShape e1 e2 $ e1 * e2 ^ (-1) - fromRational r = (fromDouble $ fromRational r) +: fromIntegral 0 + -- TODO Complex division by 0? + -- @ + -- let e1 = ((fromRational 10) +: fromIntegral 1) :: Expression Scalar C + -- let e2 = ((fromRational 15) +: fromIntegral 3) :: Expression Scalar C + -- e1 / e2 + -- @ + + -- | Complex division overloading for expressions e1 and e2 + e1 / e2 = ensureSameShape e1 e2 $ e1 * e2 ^ (-1) + -- | Rational real number to complex expression with fractional real part + -- and 0i imaginary part + fromRational r = (fromDouble $ fromRational r) +: fromIntegral 0 ------------------------------------------------------------------------------- +-- | Basic operations on covector expressions with dimension constraint `d` instance ToShape d => Num (Expression d Covector) where - e1 + e2 = + -- TODO Example + + -- | Overload operators and define common transformations + e1 + e2 = -- | Sum two covector expressions iff they have the same dimension let op = naryET Sum ElementDefault `hasShape` expressionShape e1 in ensureSameShape e1 e2 $ applyBinary op e1 e2 (*) = error "Not applicable to 1-form" - negate = + negate = -- | Negation of covector expression let op = unaryET Neg ElementDefault in applyUnary $ unaryET Neg ElementDefault fromInteger = error "Not applicable to 1-form" - abs = error "TODO: abs" + abs = error "TODO: abs" -- | Absolute value of covector signum = error "Not applicable to 1-form" --- | Scale in vector space +-- TODO Scale in vector space comments instance (VectorSpace d et s) => VectorSpaceOp (Expression Scalar s) (Expression d et) where scale :: Expression Scalar s -> Expression d et -> Expression d et scale e1 e2 = @@ -114,23 +180,28 @@ instance (VectorSpace d et s) => VectorSpaceOp (Expression Scalar s) (Expression `hasShape` expressionShape e2 in applyBinary op e1 e2 ----- | From R to C two part ----- -instance (DimensionType d) => ComplexRealOp (Expression d R) (Expression d C) where +-- | Create a complex expression from two real number expression +instance (DimensionType d) => ComplexRealOp (Expression d R) (Expression d C) where + -- @ + -- let exp = ((fromDouble 1.2345) :: Expression Scalar R) +: ((fromDouble 2) :: Expression Scalar R) + -- xRe exp + -- xIm exp + -- @ + (+:) :: Expression d R -> Expression d R -> Expression d C - (+:) e1 e2 = + (+:) e1 e2 = -- Make complex expression with e1 as real part, and e2 and imaginary part let op = binary RealImag in ensureSameShape e1 e2 $ applyBinary op e1 e2 xRe :: Expression d C -> Expression d R - xRe = + xRe = -- Get real part of complex expression, returned as its own expression let op = unary RealPart in applyUnary op xIm :: Expression d C -> Expression d R - xIm = + xIm = -- Get imaginary part of complex expression, returned as its own expression let op = unary ImagPart in applyUnary op --- | inner product +-- TODO inner product comments instance (InnerProductSpace d s) => InnerProductSpaceOp (Expression d s) (Expression d s) (Expression Scalar s) @@ -144,43 +215,50 @@ instance in ensureSameShape e1 e2 $ applyBinary op e1 e2 -- | Huber loss: https://en.wikipedia.org/wiki/Huber_loss +-- | Piecewise loss function where the loss algorithm chosen depends on delta huber :: forall d. (DimensionType d) => Double -> Expression d R -> Expression d R huber delta e = piecewise [- delta, delta] e [outerLeft, inner, outerRight] + -- @ + -- let z = variable1D @5 "var" + -- huber 3 z + -- @ where one = constWithShape @d (expressionShape e) 1 - inner = constant 0.5 *. (e * e) + inner = constant 0.5 *. (e * e) -- 1/2*e^2, for |e| <= δ + -- δ(|e| - 1/2*δ) outerLeft = constant (- delta) *. e - constant (delta * delta / 2) *. one outerRight = constant delta *. e - constant (delta * delta / 2) *. one --- | Norm 2 +-- | Norm 2 uses inner product space norm2 :: (DimensionType d) => Expression d R -> Expression Scalar R norm2 expr = sqrt (expr <.> expr) --- ----- | Norm 1 ----- +-- TODO Isn't the Euclidian norm in R^n: sqrt (sumElements(expr * expr))? +-- | Euclidian norm norm1 :: (DimensionType d) => Expression d R -> Expression Scalar R norm1 expr = sumElements (sqrt (expr * expr)) --- | Norm 2 square +-- | Norm 2 square interface class Norm2SquareOp a b | a -> b where norm2square :: a -> b +-- | Norm 2 square of real expression instance (DimensionType d) => Norm2SquareOp (Expression d R) (Expression Scalar R) where norm2square :: Expression d R -> Expression Scalar R norm2square exp = exp <.> exp +-- | Norm 2 square of complex expression instance (DimensionType d) => Norm2SquareOp (Expression d C) (Expression Scalar R) where norm2square :: Expression d C -> Expression Scalar R norm2square exp = (xRe exp <.> xRe exp) + (xIm exp <.> xIm exp) --- | +-- | Outlier-sensitive error measure using huber loss huberNorm :: (DimensionType d) => Double -> Expression d R -> Expression Scalar R huberNorm alpha = sumElements . huber alpha -- | Discrete fourier transform -- --- | Sum across +-- | Sum elements of a `d`-dimensional vector sumElements :: forall d. (DimensionType d) => Expression d R -> Expression Scalar R sumElements expr = expr <.> one where @@ -189,6 +267,7 @@ sumElements expr = expr <.> one -- | Piecewise, with a condition expression and branch expressions -- This is element corresponding, so condition and all branches should have the same dimension and shape instance (DimensionType d, ElementType et) => PiecewiseOp (Expression d R) (Expression d et) where + -- TODO I don't know how this works yet or how to comment it piecewise :: HasCallStack => [Double] -> Expression d R -> [Expression d et] -> Expression d et piecewise marks conditionExp branchExps | not (null marks), @@ -202,8 +281,13 @@ instance (DimensionType d, ElementType et) => PiecewiseOp (Expression d R) (Expr where guard = ensureSameShapeList branchExps . ensureSameShape conditionExp (head branchExps) -instance (DimensionType d) => FTOp (Expression d C) (Expression d C) where +-- Fourier transform on complex expression +instance (DimensionType d) => FTOp (Expression d C) (Expression d C) where ft :: Expression d C -> Expression d C + -- @ + -- let exp = ((fromDouble 1.2345) :: Expression Scalar R) +: ((fromDouble 2) :: Expression Scalar R) + -- ft exp + -- @ ft e | isScalarShape $ expressionShape e = e | otherwise = @@ -211,19 +295,26 @@ instance (DimensionType d) => FTOp (Expression d C) (Expression d C) where imFT = applyUnary (unary ImFT) e in reFT +: imFT -instance (DimensionType d) => FTOp (Expression d R) (Expression d C) where +-- Fourier transform on real expression which returns complex expression +instance (DimensionType d) => FTOp (Expression d R) (Expression d C) where ft :: Expression d R -> Expression d C + -- @ + -- let exp = (fromDouble 15) :: Expression Scalar R + -- ft exp + -- @ ft e = ft (e +: constWithShape (expressionShape e) 0) --- | +-- TODO Comments instance (ElementType et, KnownNat n) => RotateOp Int (Expression n et) where rotate :: Int -> Expression n et -> Expression n et rotate x = applyUnary . unary $ Rotate [x] +-- TODO Comments instance (ElementType et, KnownNat m, KnownNat n) => RotateOp (Int, Int) (Expression '(m, n) et) where rotate :: (Int, Int) -> Expression '(m, n) et -> Expression '(m, n) et rotate (x, y) = applyUnary . unary $ Rotate [x, y] +-- TODO Comments instance (ElementType et, KnownNat m, KnownNat n, KnownNat p) => RotateOp (Int, Int, Int) (Expression '(m, n, p) et) @@ -232,7 +323,7 @@ instance rotate (x, y, z) = applyUnary . unary $ Rotate [x, y, z] --- | +-- | Returns an int from a type-level natural valueFromNat :: forall n. (KnownNat n) => Int valueFromNat = fromIntegral $ natVal (Proxy :: Proxy n) @@ -244,35 +335,55 @@ variable name = Expression h (fromList [(h, node)]) h = hash node -- | Create primitive expressions using Nat kind +-- | Supply m, n, p using @ + +-- | Create a variable for one-dimensional nat values variable1D :: forall n. (KnownNat n) => String -> - Expression n R -variable1D name = Expression h (fromList [(h, node)]) + Expression n R +variable1D name = Expression h (fromList [(h, node)]) + -- | Restrict size with @n + -- @ + -- let exp = variable1D "var" + -- let exp = variableD @10 "var" + -- @ where size = valueFromNat @n node = ([size], Var name) h = hash node +-- | Create a variable for two-dimensional nat values variable2D :: forall m n. (KnownNat m, KnownNat n) => String -> Expression '(m, n) R variable2D name = Expression h (fromList [(h, node)]) + -- | Restrict size with @m @n + -- @ + -- let exp = variable2D "var" + -- let exp = variable2D @10 @20 "var" + -- @ where size1 = valueFromNat @m size2 = valueFromNat @n node = ([size1, size2], Var name) h = hash node +-- | Create a variable for three-dimensional nat values variable3D :: forall m n p. (KnownNat m, KnownNat n, KnownNat p) => String -> Expression '(m, n, p) R variable3D name = Expression h (fromList [(h, node)]) + -- | Restrict size with @m @n @p + -- @ + -- let exp = variable3D "var" + -- let exp = variable3D @10 @20 @30 "var" + -- @ where size1 = valueFromNat @m size2 = valueFromNat @n @@ -280,43 +391,59 @@ variable3D name = Expression h (fromList [(h, node)]) node = ([size1, size3], Var name) h = hash node --- | +-- | Constant to expression, without KnownNat constraint constant :: Double -> Expression Scalar R constant val = Expression h (fromList [(h, node)]) + -- @ + -- constant 3 + -- @ where node = ([], Const val) - h = hash node + h = hash node + +-- TODO Explanation as to reason for satifying KnownNat constraint --- | +-- One-dimensional constant constant1D :: forall n. (KnownNat n) => Double -> Expression n R constant1D val = Expression h (fromList [(h, node)]) + -- @ + -- constant2D @1 40 + -- @ where size = valueFromNat @n node = ([size], Const val) h = hash node +-- Two-dimensional constant constant2D :: forall m n. (KnownNat m, KnownNat n) => Double -> Expression '(m, n) R constant2D val = Expression h (fromList [(h, node)]) + -- @ + -- constant2D @1 @2 40 + -- @ where size1 = valueFromNat @m size2 = valueFromNat @n node = ([size1, size2], Const val) h = hash node +-- Three-dimensional constant constant3D :: forall m n p. (KnownNat m, KnownNat n, KnownNat p) => Double -> Expression '(m, n, p) R constant3D val = Expression h (fromList [(h, node)]) + -- @ + -- constant2D @1 @2 @3 40 + -- @ where size1 = valueFromNat @m size2 = valueFromNat @n