brush-strokes-0.1.0.0: src/lib/Math/Interval.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Math.Interval
( π(π), inf, sup
, midpoint , scaleInterval, width
, hull
, addWithErr, subWithErr, prodWithErr, divWithErr
, isCanonical
, (β©), (β), (β), (ββ), (β)
, extendedRecip
, singleton, point
, nonDecreasing
, aabb, bisect
, πβ(..)
)
where
-- base
import Prelude hiding ( Num(..), Fractional(..) )
import qualified Data.List.NonEmpty as NE
-- acts
import Data.Act
( Act((β’)), Torsor((-->)) )
-- groups
import Data.Group
( Group(..) )
-- groups-generic
import Data.Group.Generics
( )
-- brush-strokes
import Math.Algebra.Dual
import Math.Float.Utils
( prevFP )
import Math.Interval.Internal
( π(π), inf, sup
, scaleInterval, width, singleton
, hull
, addWithErr, subWithErr, prodWithErr
, πβ(..), divWithErr
)
import Math.Linear
( β(..), T(..)
, RepresentableQ(..), Representable(..), coordinates
)
import Math.Module
import Math.Monomial
import Math.Ring
--------------------------------------------------------------------------------
-- Interval arithmetic.
type instance Dim ( πβ n ) = n
-- | Turn a non-decreasing function into a function on intervals.
nonDecreasing :: forall n m
. ( Representable Double ( β n )
, Representable π ( πβ n )
, Representable Double ( β m )
, Representable π ( πβ m ) )
=> ( β n -> β m ) -> πβ n -> πβ m
nonDecreasing f u =
let
lo, hi :: β n
lo = tabulate \ i -> inf ( u `index` i )
hi = tabulate \ i -> sup ( u `index` i )
lo', hi' :: β m
lo' = f lo
hi' = f hi
in tabulate \ j -> π ( lo' `index` j ) ( hi' `index` j )
{-# INLINEABLE nonDecreasing #-}
-- | Does the given value lie inside the specified interval?
(β) :: Double -> π -> Bool
x β π lo hi = x >= lo && x <= hi
infix 4 β
-- | Does the given point lie inside the specified box?
(ββ) :: ( Representable Double ( β n ), Representable π ( πβ n ) ) => β n -> πβ n -> Bool
p ββ i = and $ (β) <$> coordinates p <*> coordinates i
infix 4 ββ
{-# INLINEABLE (ββ) #-}
{-# SPECIALISE INLINE (ββ) @1 #-}
{-# SPECIALISE INLINE (ββ) @2 #-}
{-# SPECIALISE INLINE (ββ) @3 #-}
{-# SPECIALISE INLINE (ββ) @4 #-}
-- | Is this interval canonical, i.e. it consists of either 1 or 2 floating point
-- values only?
isCanonical :: π -> Bool
isCanonical ( π x_inf x_sup ) = x_inf >= prevFP x_sup
-- | The midpoint of an interval.
--
-- NB: assumes the endpoints are neither @NaN@ nor infinity.
midpoint :: π -> Double
midpoint ( π x_inf x_sup ) = 0.5 * ( x_inf + x_sup )
point :: ( Representable Double ( β n ), Representable π ( πβ n ) )
=> β n -> πβ n
point x = tabulate \ i -> singleton ( x `index` i )
{-# INLINEABLE point #-}
-- | Compute the intersection of two intervals.
--
-- Returns whether the first interval is a strict subset of the second interval
-- (or the intersection is a single point).
(β©) :: π -> π -> [ ( π, Bool ) ]
π lo1 hi1 β© π lo2 hi2
| lo > hi
= [ ]
| otherwise
= [ ( π lo hi, ( lo1 > lo2 && hi1 < hi2 ) || ( lo == hi ) ) ]
where
lo = max lo1 lo2
hi = min hi1 hi2
infix 3 β©
infixl 6 β
(β) :: π -> π -> π
(β) a@( π lo1 hi1 ) b@( π lo2 hi2 )
| width a >= width b
= π ( lo1 - lo2 ) ( hi1 - hi2 )
| otherwise
= π ( hi1 - hi2 ) ( lo1 - lo2 )
-- | Bisect an interval.
--
-- Generally returns two sub-intervals, but returns the input interval if
-- it is canonical (and thus bisection would be pointless).
bisect :: π -> NE.NonEmpty π
bisect x@( π x_inf x_sup )
| isCanonical x
= NE.singleton x
| otherwise
= π x_inf x_mid NE.:| [ π x_mid x_sup ]
where x_mid = midpoint x
deriving via ViaAbelianGroup ( T π )
instance Semigroup ( T π )
deriving via ViaAbelianGroup ( T π )
instance Monoid ( T π )
deriving via ViaAbelianGroup ( T π )
instance Group ( T π )
instance Act ( T π ) π where
T g β’ a = g + a
instance Torsor ( T π ) π where
a --> b = T $ b - a
--------------------------------------------------------------------------------
-- Extended division
-- | Extended division, returning either 1 or 2 intervals.
--
-- NB: this function can return overlapping intervals if both arguments contain 0.
-- Otherwise, the returned intervals are guaranteed to be disjoint.
(β) :: π -> π -> [ π ]
x β y
#ifdef ASSERTS
| 0 β x && 0 β y
= error $ unlines
[ "x β y: both arguments contain zero"
, "x: " ++ show x, "y: " ++ show y ]
| otherwise
#endif
= map ( x * ) ( extendedRecip y )
infixl 7 β
-- | Extended reciprocal, returning either 1 or 2 intervals.
extendedRecip :: π -> [ π ]
extendedRecip x@( π lo hi )
| lo == 0 && hi == 0
= [ π negInf posInf ]
| lo >= 0 || hi <= 0
= [ recip x ]
| otherwise
= [ π negInf ( recip lo ), π ( recip hi ) posInf ]
where
negInf, posInf :: Double
negInf = -1 / 0
posInf = 1 / 0
-------------------------------------------------------------------------------
-- Lattices.
aabb :: ( Representable π ( πβ n ), Functor f )
=> f ( πβ n ) -> ( f π -> π ) -> πβ n
aabb fv extrema = tabulate \ i -> extrema ( fmap ( `index` i ) fv )
{-# INLINEABLE aabb #-}
--------------------------------------------------------------------------------
instance Module π ( T ( πβ 0 ) ) where
origin = T πβ0
_ ^+^ _ = T πβ0
_ ^-^ _ = T πβ0
_ *^ _ = T πβ0
instance Module π ( T ( πβ 1 ) ) where
origin = T $ πβ1 ( unT origin )
T ( πβ1 x1 ) ^+^ T ( πβ1 x2 ) = T $ πβ1 ( x1 + x2 )
T ( πβ1 x1 ) ^-^ T ( πβ1 x2 ) = T $ πβ1 ( x1 - x2 )
k *^ T ( πβ1 x1 ) = T $ πβ1 ( k * x1 )
instance Module π ( T ( πβ 2 ) ) where
origin = T $ πβ2 ( unT origin ) ( unT origin )
T ( πβ2 x1 y1 ) ^+^ T ( πβ2 x2 y2 ) = T $ πβ2 ( x1 + x2 ) ( y1 + y2 )
T ( πβ2 x1 y1 ) ^-^ T ( πβ2 x2 y2 ) = T $ πβ2 ( x1 - x2 ) ( y1 - y2 )
k *^ T ( πβ2 x1 y1 ) = T $ πβ2 ( k * x1 ) ( k * y1 )
instance Module π ( T ( πβ 3 ) ) where
origin = T $ πβ3 ( unT origin ) ( unT origin ) ( unT origin )
T ( πβ3 x1 y1 z1 ) ^+^ T ( πβ3 x2 y2 z2 ) = T $ πβ3 ( x1 + x2 ) ( y1 + y2 ) ( z1 + z2 )
T ( πβ3 x1 y1 z1 ) ^-^ T ( πβ3 x2 y2 z2 ) = T $ πβ3 ( x1 - x2 ) ( y1 - y2 ) ( z1 - z2 )
k *^ T ( πβ3 x1 y1 z1 ) = T $ πβ3 ( k * x1 ) ( k * y1 ) ( k * z1 )
instance Module π ( T ( πβ 4 ) ) where
origin = T $ πβ4 ( unT origin ) ( unT origin ) ( unT origin ) ( unT origin )
T ( πβ4 x1 y1 z1 w1 ) ^+^ T ( πβ4 x2 y2 z2 w2 ) = T $ πβ4 ( x1 + x2 ) ( y1 + y2 ) ( z1 + z2 ) ( w1 + w2 )
T ( πβ4 x1 y1 z1 w1 ) ^-^ T ( πβ4 x2 y2 z2 w2 ) = T $ πβ4 ( x1 - x2 ) ( y1 - y2 ) ( z1 - z2 ) ( w1 - w2 )
k *^ T ( πβ4 x1 y1 z1 w1 ) = T $ πβ4 ( k * x1 ) ( k * y1 ) ( k * z1 ) ( k * w1 )
instance Inner π ( T ( πβ 2 ) ) where
T ( πβ2 x1 y1 ) ^.^ T ( πβ2 x2 y2 ) = x1 * x2 + y1 * y2
instance Cross π ( T ( πβ 2 ) ) where
T ( πβ2 x1 y1 ) Γ T ( πβ2 x2 y2 ) = x1 * y2 - x2 * y1
deriving via ViaModule π ( T ( πβ n ) )
instance Module π ( T ( πβ n ) ) => Semigroup ( T ( πβ n ) )
deriving via ViaModule π ( T ( πβ n ) )
instance Module π ( T ( πβ n ) ) => Monoid ( T ( πβ n ) )
deriving via ViaModule π ( T ( πβ n ) )
instance Module π ( T ( πβ n ) ) => Group ( T ( πβ n ) )
deriving via ViaModule π ( πβ n )
instance Module π ( T ( πβ n ) ) => Act ( T ( πβ n ) ) ( πβ n )
deriving via ( ViaModule π ( πβ n ) )
instance Module π ( T ( πβ n ) ) => Torsor ( T ( πβ n ) ) ( πβ n )
--------------------------------------------------------------------------------
-- HasChainRule instances.
instance HasChainRule π 2 ( πβ 0 ) where
linearD f v = D0 ( f v )
chain _ ( D0 gfx ) = D21 gfx origin origin
instance HasChainRule π 3 ( πβ 0 ) where
linearD f v = D0 ( f v )
chain _ ( D0 gfx ) = D31 gfx origin origin origin
instance HasChainRule π 2 ( πβ 1 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 1 -> w ) -> πβ 1 -> D2πΈ1 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D2πΈ1 ( πβ 1 ) -> D2πΈ1 w -> D2πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 3 ( πβ 1 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 1 -> w ) -> πβ 1 -> D3πΈ1 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D3πΈ1 ( πβ 1 ) -> D3πΈ1 w -> D3πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 2 ( πβ 2 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 2 -> w ) -> πβ 2 -> D2πΈ2 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D2πΈ1 ( πβ 2 ) -> D2πΈ2 w -> D2πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 3 ( πβ 2 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 2 -> w ) -> πβ 2 -> D3πΈ2 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D3πΈ1 ( πβ 2 ) -> D3πΈ2 w -> D3πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 2 ( πβ 3 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 3 -> w ) -> πβ 3 -> D2πΈ3 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D2πΈ1 ( πβ 3 ) -> D2πΈ3 w -> D2πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 3 ( πβ 3 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 3 -> w ) -> πβ 3 -> D3πΈ3 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D3πΈ1 ( πβ 3 ) -> D3πΈ3 w -> D3πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 2 ( πβ 4 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 4 -> w ) -> πβ 4 -> D2πΈ4 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D2πΈ1 ( πβ 4 ) -> D2πΈ4 w -> D2πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )
instance HasChainRule π 3 ( πβ 4 ) where
linearD :: forall w. Module π ( T w ) => ( πβ 4 -> w ) -> πβ 4 -> D3πΈ4 w
linearD f v =
let !o = origin @π @( T w )
in $$( monTabulateQ \ mon ->
if | isZeroMonomial mon
-> [|| f v ||]
| Just i <- isLinear mon
-> [|| f $$( tabulateQ \ j ->
if | j == i
-> [|| π 1 1 ||]
| otherwise
-> [|| π 0 0 ||]
) ||]
| otherwise
-> [|| unT o ||]
)
chain :: forall w. Module π ( T w ) => D3πΈ1 ( πβ 4 ) -> D3πΈ4 w -> D3πΈ1 w
chain !df !dg =
let !o = origin @π @( T w )
!p = (^+^) @π @( T w )
!s = (^*) @π @( T w )
in $$( chainRule1NQ
[|| o ||] [|| p ||] [|| s ||]
[|| df ||] [|| dg ||] )