simple-expr-0.2.0.0: src/Debug/SimpleExpr/Utils/Algebra.hs
-- | Module : Debug.SimpleExpr.Utils.Algebra
-- Copyright : (C) 2025 Alexey Tochin
-- License : BSD3 (see the file LICENSE)
-- Maintainer : Alexey Tochin <Alexey.Tochin@gmail.com>
--
-- Inegral power type class and instances.
module Debug.SimpleExpr.Utils.Algebra
( MultiplicativeAction,
(*|),
Convolution,
(|*|),
AlgebraicPower,
IntPower,
IntegerPower,
NaturalPower,
FloatPower,
DoublePower,
(^^),
(^),
square,
qube,
splitIntoN,
splitInto4,
)
where
import Combinatorics (binomialSeq)
import Data.Functor (Functor (fmap))
import Data.Functor.Identity (Identity (Identity))
import Data.Int (Int, Int16, Int32, Int64, Int8)
import qualified Data.List as DL
import qualified Data.Stream as DS
import qualified Data.Vector as DV
import qualified Data.Vector.Generic as DVG
import qualified Data.Vector.Generic.Sized as DVGS
import qualified Data.Vector.Unboxed as DVU
import Data.Word (Word, Word16, Word32, Word64, Word8)
import Debug.SimpleExpr (SimpleExpr)
import GHC.Base (Double, Eq ((==)), Float, Maybe, otherwise, ($), (.), (>=))
import GHC.Integer (Integer)
import GHC.Natural (naturalFromInteger)
import qualified GHC.Num as GHCN
import GHC.Real (Integral, Real, fromIntegral, mod, realToFrac, toInteger)
import qualified GHC.Real
import GHC.TypeLits (Natural)
import NumHask
( Complex (Complex),
Field,
Multiplicative,
Ring,
Subtractive,
negate,
recip,
(*),
(-),
)
import qualified NumHask as NH
-- | Type class for multiplicative actions.
-- This class defines a method for multiplying a value of type @b@
-- by a value of type @a@ producing a value of type @b@.
--
-- ==== __Examples__
--
-- >>> (2 :: Int) *| (3 :: Float) :: Float
-- 6.0
--
-- >>> (2 :: Int) *| (3 :: Float, 4 :: Double) :: (Float, Double)
-- (6.0,8.0)
--
-- >>> (2 :: Natural) *| [3, 4, 5] :: [Int]
-- [6,8,10]
--
-- >>> (2 :: Natural) *| Data.Vector.fromList [3, 4, 5] :: Data.Vector.Vector Int
-- [6,8,10]
--
-- >>> (2 :: Natural) *| [(3, 4), (5, 6), (7, 8)] :: [(Int, Int)]
-- [(6,8),(10,12),(14,16)]
--
-- >>> (2 :: Natural) *| Data.Vector.fromList [[3, 4], [], [5]] :: Data.Vector.Vector [Int]
-- [[6,8],[],[10]]
class MultiplicativeAction a b where
-- | Left multiplicative action operator that preserve
-- the type on the right hand side.
(*|) :: a -> b -> b
-- | `Int` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Int
where
(*|) c = (fromIntegral c *)
-- | `Int8` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Int8
where
(*|) c = (fromIntegral c *)
-- | `Int16` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Int16
where
(*|) c = (fromIntegral c *)
-- | `Int32` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Int32
where
(*|) c = (fromIntegral c *)
-- | `Int64` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Int64
where
(*|) c = (fromIntegral c *)
-- | `Word` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Word
where
(*|) c = (fromIntegral c *)
-- | `Word8` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Word8
where
(*|) c = (fromIntegral c *)
-- | `Word16` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Word16
where
(*|) c = (fromIntegral c *)
-- | `Word32` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Word32
where
(*|) c = (fromIntegral c *)
-- | `Word64` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Word64
where
(*|) c = (fromIntegral c *)
-- | `Integer` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Integer
where
(*|) c = (fromIntegral c *)
-- | `Natural` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a Natural
where
(*|) c = (fromIntegral c *)
-- | `Float` instance of `MultiplicativeAction`@ a@.
instance
(Real a) =>
MultiplicativeAction a Float
where
(*|) c = (realToFrac c *)
-- | `Double` instance of `MultiplicativeAction`@ a@.
instance
(Real a) =>
MultiplicativeAction a Double
where
(*|) c = (realToFrac c *)
-- | `SimpleExpr` instance of `MultiplicativeAction`@ a@.
instance
(Integral a) =>
MultiplicativeAction a SimpleExpr
where
(*|) c = (*) (NH.fromInteger $ toInteger c)
-- | Tuple instance of `MultiplicativeAction`@ a@.
instance
(MultiplicativeAction a b0, MultiplicativeAction a b1) =>
MultiplicativeAction a (b0, b1)
where
(*|) c (x0, x1) = (c *| x0, c *| x1)
-- | Triple instance of `MultiplicativeAction`@ a@.
instance
( MultiplicativeAction a b0,
MultiplicativeAction a b1,
MultiplicativeAction a b2
) =>
MultiplicativeAction a (b0, b1, b2)
where
(*|) c (x0, x1, x2) = (c *| x0, c *| x1, c *| x2)
-- | `Data.Functor.Identity` instance of `MultiplicativeAction`@ a@.
instance
(MultiplicativeAction a b) =>
MultiplicativeAction a (Identity b)
where
(*|) = fmap . (*|)
-- | List `[]` instance of `MultiplicativeAction`@ a@.
instance
(MultiplicativeAction a b) =>
MultiplicativeAction a [b]
where
(*|) = fmap . (*|)
-- | Boxed vector `Data.Vector.Vector` instance of `MultiplicativeAction`@ a@.
instance
(MultiplicativeAction a b) =>
MultiplicativeAction a (DV.Vector b)
where
(*|) = DV.map . (*|)
-- | Unboxed vector `Data.Vector.Unboxed.Vector` instance
-- of `MultiplicativeAction`@ a@.
instance
(MultiplicativeAction a b) =>
MultiplicativeAction a (DVGS.Vector DV.Vector n b)
where
(*|) = DVGS.map . (*|)
-- | `Data.Stream.Stream` instance of `MultiplicativeAction`@ a@.
instance
(MultiplicativeAction a b) =>
MultiplicativeAction a (DS.Stream b)
where
(*|) = DS.map . (*|)
-- | Type class for convolution operations that support nested structures.
--
-- ==== __Examples__
--
-- >>> [1,1,1] |*| [1,2,0] :: Int
-- 3
--
-- >>> ([1,1,1], Data.Vector.fromList [1, 2]) |*| ([1,2,0], Data.Vector.fromList [0, 2]) :: Int
-- 7
class Convolution a b c where
-- | The convolution operator that combines values of type @a@ and @b@.
(|*|) :: a -> b -> c
-- | `Int` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Int Int a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Int8` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Int8 Int8 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Int16` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Int16 Int16 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Int32` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Int32 Int32 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Int64` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Int64 Int64 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Word` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Word Word a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Word8` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Word8 Word8 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Word16` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Word16 Word16 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Word32` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Word32 Word32 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Word64` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Word64 Word64 a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Integer` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Integer Integer a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Natural` instance of `Convolution`.
instance
(GHCN.Num a) =>
Convolution Natural Natural a
where
x |*| y = fromIntegral x GHCN.* fromIntegral y
-- | `Float` instance of `Convolution`.
instance
(GHC.Real.Fractional a) =>
Convolution Float Float a
where
x |*| y = realToFrac x GHCN.* realToFrac y
-- | `Double` instance of `Convolution`.
instance
(GHC.Real.Fractional a) =>
Convolution Double Double a
where
x |*| y = realToFrac x GHCN.* realToFrac y
-- | Tuple instance of `Convolution`.
instance
(Convolution a0 b0 c, Convolution a1 b1 c, NH.Additive c) =>
Convolution (a0, a1) (b0, b1) c
where
(x0, x1) |*| (y0, y1) = x0 |*| y0 NH.+ x1 |*| y1
-- | Triple instance of `Convolution`.
instance
(Convolution a0 b0 c, Convolution a1 b1 c, Convolution a2 b2 c, NH.Additive c) =>
Convolution (a0, a1, a2) (b0, b1, b2) c
where
(x0, x1, x2) |*| (y0, y1, y2) = x0 |*| y0 NH.+ x1 |*| y1 NH.+ x2 |*| y2
-- | `Data.Functor.Identity` instance of `Convolution`.
instance
(Convolution a b c) =>
Convolution (Identity a) (Identity b) c
where
(Identity x) |*| (Identity y) = x |*| y
-- | List `[]` instance of `Convolution`.
-- Shorter list is efficently treated as padded with zeros.
instance
(Convolution a b c, NH.Additive c) =>
Convolution [a] [b] c
where
lx |*| ly = DL.foldl' (NH.+) NH.zero $ DL.zipWith (|*|) lx ly
-- | Boxed vector `Data.Vector.Vector` instance of `Convolution`.
-- Smaller vector is efficently treated as padded with zeros.
instance
(Convolution a b c, NH.Additive c) =>
Convolution (DV.Vector a) (DV.Vector b) c
where
vx |*| vy = DV.foldl' (NH.+) NH.zero $ DV.zipWith (|*|) vx vy
-- | Unboxed vector `Data.Vector.Unboxed.Vector` instance of `Convolution`.
-- Smaller vector is efficently treated as padded with zeros.
instance
(Convolution a b c, NH.Additive c) =>
Convolution (DVGS.Vector DV.Vector n a) (DVGS.Vector DV.Vector n b) c
where
vx |*| vy = DVGS.foldl' (NH.+) NH.zero $ DVGS.zipWith (|*|) vx vy
-- instance
-- MultiplicativeAction a b c =>
-- MultiplicativeAction (FiniteSupportStream a) (DS.Stream b) c
-- where
-- vx |*| vy = undefined -- foldl' zero (+) $ DVGS.zipwith (|*|) lx ly
-- -- (*|) = DS.map . (*|)
-- instance
-- MultiplicativeAction a b c =>
-- MultiplicativeAction (DS.Stream a) (FiniteSupportStream b) c
-- where
-- vx |*| vy = undefined -- foldl' zero (+) $ DVGS.zipwith (|*|) lx ly
-- instance
-- MultiplicativeAction a b c =>
-- MultiplicativeAction (FiniteSupportStream a) (FiniteSupportStream b) c
-- where
-- vx |*| vy = undefined -- foldl' zero (+) $ DVGS.zipwith (|*|) lx ly
-- | Type class for power operations.
-- This class defines a method for raising a value of type @a@ to a power
-- of type @b@.
-- It is usefull to deistinguish, for example,
-- the integral power defined as a repetative multiplication
-- `(^^)` or `(^)` from the general power operation.
--
-- ==== __Examples__
--
-- >>> import Debug.SimpleExpr (variable, SE, simplify)
-- >>> import GHC.Base (($))
-- >>> import GHC.Real (Rational)
-- >>> import qualified NumHask
--
-- >>> x = variable "x"
--
-- >>> (^^ 2) x
-- x^2
--
-- >>> (NumHask.^^ 2) (x)
-- x*x
--
-- >>> (^^ 2) (3 :: Float)
-- 9.0
--
-- >>> (2.0 :: Double) ^^ (3.5 :: Float)
-- 11.313708498984761
--
-- For lists, vectors and other containers, the power operation is applied element-wise:
-- >>> (^^ 2) [0, 1, 2, 3] :: [Int]
-- [0,1,4,9]
class AlgebraicPower a b where
(^^) :: b -> a -> b
-- | `Int` type alias for `AlgebraicPower`.
type IntPower a = AlgebraicPower Int a
-- | `Integer` type alias for `AlgebraicPower`.
type IntegerPower a = AlgebraicPower Integer a
-- | `Natural` type alias for `AlgebraicPower`.
type NaturalPower a = AlgebraicPower Natural a
-- | `Float` type alias for `AlgebraicPower`.
type FloatPower a = AlgebraicPower Float a
-- | `Double` type alias for `AlgebraicPower`.
type DoublePower a = AlgebraicPower Double a
-- | Infix synonym for `(^^)`.
(^) :: (AlgebraicPower Integer a) => a -> Integer -> a
(^) = (^^)
-- | Square a value.
square :: (AlgebraicPower Integer a) => a -> a
square x = x ^ 2
-- | Qube a value.
qube :: (AlgebraicPower Integer a) => a -> a
qube x = x ^ 2
-- | `Int` instance of `AlgebraicPower` typeclass.
instance
(Integral a) =>
AlgebraicPower a Int
where
x ^^ n = x GHC.Real.^ n
-- | `Int8` instance of `AlgebraicPower` typeclass.
instance
(Integral a) =>
AlgebraicPower a Int8
where
x ^^ n = x GHC.Real.^ n
-- | `Integer` instance of `AlgebraicPower` typeclass.
instance
(Integral a) =>
AlgebraicPower a Integer
where
x ^^ n = x GHC.Real.^ n
-- | `Float` instace of `AlgebraicPower` typeclass.
instance
(Real a) =>
AlgebraicPower a Float
where
x ^^ n = x NH.** realToFrac n
-- | `Double` instance of `AlgebraicPower` typeclass
-- for raising `Double` values to `Real` exponents.
instance
(Real a) =>
AlgebraicPower a Double
where
x ^^ n = x NH.** realToFrac n
-- | `AlgebraicPower` instance for raising `SimpleExpr` values to `Integer` exponents.
instance AlgebraicPower Integer SimpleExpr where
x ^^ n = x NH.** NH.fromInteger n
-- | `Data.Functor.Identity` instance of `AlgebraicPower` typeclass.
instance
(AlgebraicPower b a) =>
AlgebraicPower b (Identity a)
where
(Identity x0) ^^ n = Identity $ x0 ^^ n
-- | `Maybe` instance of `AlgebraicPower` typeclass.
instance
(AlgebraicPower b a) =>
AlgebraicPower b (Maybe a)
where
x ^^ n = fmap (^^ n) x
-- | Generate the list of terms of the binomial expansion of
-- \( (x + y)^n \).
--
-- ==== __Examples__
--
-- >>> import Debug.SimpleExpr (variable, simplify)
--
-- >>> simplify $ biniminalList 3 (variable "x") (variable "y")
-- [y^3,3*(x*(y^2)),3*((x^2)*y),x^3]
biniminalList ::
( Multiplicative a,
AlgebraicPower b a,
MultiplicativeAction b a,
Subtractive b,
Integral b
) =>
b ->
a ->
a ->
[a]
biniminalList n x y =
DL.zipWith
(*|)
(binomialSeq n)
([x ^^ i * y ^^ (n - i) | i <- [0 ..]])
-- | Interleave two lists.
--
-- ==== __Examples__
--
-- >>> interleave [1,3,5] [2,4,6]
-- [1,2,3,4,5,6]
interleave :: [a] -> [a] -> [a]
interleave [] ys = ys
interleave xs [] = xs
interleave (x : xs) (y : ys) = x : y : interleave xs ys
-- | Take every n-th element from a list starting from a given index.
--
-- ==== __Examples__
--
-- >>> takeEveryNth 3 0 [0..11]
-- [0,3,6,9]
takeEveryNth :: Int -> Int -> [a1] -> [a1]
takeEveryNth n start ys = [y | (y, j) <- DL.zip ys [0 ..], j `mod` n == start]
-- | Split a list into n lists by taking every n-th element.
--
-- ==== __Examples__
--
-- >>> splitIntoN 3 [0..11]
-- [[0,3,6,9],[1,4,7,10],[2,5,8,11]]
splitIntoN :: Int -> [a] -> [[a]]
splitIntoN n xs = [takeEveryNth n i xs | i <- [0 .. n - 1]]
-- | Split a list into n lists by taking every n-th element.
--
-- ==== __Examples__
--
-- >>> splitInto4 [0..11]
-- ([0,4,8],[1,5,9],[2,6,10],[3,7,11])
splitInto4 :: [a] -> ([a], [a], [a], [a])
splitInto4 xs = (takeEveryNth 4 0 xs, takeEveryNth 4 1 xs, takeEveryNth 4 2 xs, takeEveryNth 4 3 xs)
-- | Complex number `NumHask.Complex` instance of `AlgebraicPower`
-- for raising to `Natural` exponents.
instance
(Ring a, AlgebraicPower Natural a, MultiplicativeAction Natural a) =>
AlgebraicPower Natural (Complex a)
where
(Complex (x, y)) ^^ n = Complex (r, i)
where
r = NH.sum $ interleave split0 (fmap negate split2)
i = NH.sum $ interleave split1 (fmap negate split3)
(split0, split1, split2, split3) = splitInto4 (biniminalList n x y)
-- | Complex number `NumHask.Complex` instance of `AlgebraicPower`
-- for raising to `Integer` exponents.
instance
(Field a, AlgebraicPower Natural a, MultiplicativeAction Natural a) =>
AlgebraicPower Integer (Complex a)
where
x ^^ n
| n >= 0 = x ^^ naturalFromInteger n
| otherwise = recip $ x ^^ naturalFromInteger (-n)
-- | `AlgebraicPower` instance for raising tuples to powers.
instance
(AlgebraicPower b a0, AlgebraicPower b a1) =>
AlgebraicPower b (a0, a1)
where
(x0, x1) ^^ n = (x0 ^^ n, x1 ^^ n)
-- | `AlgebraicPower` instance for raising triples to powers.
instance
(AlgebraicPower b a0, AlgebraicPower b a1, AlgebraicPower b a2) =>
AlgebraicPower b (a0, a1, a2)
where
(x0, x1, x2) ^^ n = (x0 ^^ n, x1 ^^ n, x2 ^^ n)
-- | `AlgebraicPower` instance for raising lists to powers.
instance
(AlgebraicPower b a) =>
AlgebraicPower b [a]
where
x ^^ n = fmap (^^ n) x
-- | `AlgebraicPower` instance for raising boxed vectors to powers.
instance
(AlgebraicPower b a) =>
AlgebraicPower b (DV.Vector a)
where
x ^^ n = DV.map (^^ n) x
-- | `AlgebraicPower` instance for raising unboxed vectors to powers.
instance
(AlgebraicPower b a, DVU.Unbox a) =>
AlgebraicPower b (DVU.Vector a)
where
x ^^ n = DVU.map (^^ n) x
-- | `AlgebraicPower` instance for raising sized vectors to powers.
instance
(AlgebraicPower b a, DVG.Vector v a) =>
AlgebraicPower b (DVGS.Vector v n a)
where
x ^^ n = DVGS.map (^^ n) x
-- | `AlgebraicPower` instance for raising streams to powers.
instance
(AlgebraicPower b a) =>
AlgebraicPower b (DS.Stream a)
where
x ^^ n = DS.map (^^ n) x