easytensor 2.0.1.0 → 2.1.0.0
raw patch · 61 files changed
+8508/−3074 lines, 61 filesdep ~constraints-derivingbuild-type:Customsetup-changedPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: constraints-deriving
API changes (from Hackage documentation)
- Numeric.DataFrame.Contraction: instance (Data.Type.List.ConcatList as bs asbs, Numeric.Dimensions.Dim.Dimensions as, Numeric.Dimensions.Dim.Dimensions bs, GHC.Num.Num t) => Numeric.DataFrame.Contraction.Contraction t as bs asbs
- Numeric.DataFrame.IO: data family IODataFrame (t :: Type) (ns :: [k])
- Numeric.DataFrame.Internal.Backend: type DFBackend (t :: Type) (ds :: [Nat]) = Backend I t ds (BackendFamily t ds)
- Numeric.DataFrame.Internal.Backend: type KnownBackend (t :: Type) (ds :: [Nat]) = KnownBackend t ds (BackendFamily t ds)
- Numeric.DataFrame.Internal.PrimArray: arrayContent# :: PrimArray t a => a -> (# t | (# CumulDims, Int#, ByteArray# #) #)
- Numeric.DataFrame.ST: data family STDataFrame s (t :: Type) (ns :: [k])
- Numeric.DataFrame.SubSpace: (!) :: SubSpace t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) => DataFrame t asbs -> Idxs as -> DataFrame t bs
- Numeric.DataFrame.SubSpace: indexOffset# :: SubSpace t as bs asbs => Int# -> DataFrame t asbs -> DataFrame t bs
- Numeric.DataFrame.SubSpace: instance (Data.Type.List.ConcatList as bs asbs, Numeric.Dimensions.Dim.Dimensions as, Numeric.Dimensions.Dim.Dimensions bs, Numeric.Dimensions.Dim.Dimensions asbs, Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t bs), Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t asbs), Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Type.DataFrame t bs), Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Type.DataFrame t asbs)) => Numeric.DataFrame.SubSpace.SubSpace t as bs asbs
- Numeric.DataFrame.SubSpace: updateOffset# :: SubSpace t as bs asbs => Int# -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs
- Numeric.DataFrame.Type: instance (Data.Type.List.All GHC.Read.Read ts, Data.Type.List.All Numeric.PrimBytes.PrimBytes ts, Numeric.TypedList.RepresentableList ts, Numeric.Dimensions.Dim.BoundedDims ds, Data.Type.List.All Numeric.Dimensions.Dim.KnownXNatType ds) => GHC.Read.Read (Numeric.DataFrame.Type.DataFrame ts ds)
- Numeric.DataFrame.Type: instance (GHC.Classes.Ord (Numeric.ProductOrd.Partial.ProductOrd (Numeric.DataFrame.Internal.Backend.DFBackend t ds)), GHC.Classes.Eq (Numeric.DataFrame.Internal.Backend.DFBackend t ds)) => GHC.Classes.Ord (Numeric.ProductOrd.Partial.ProductOrd (Numeric.DataFrame.Type.DataFrame t ds))
- Numeric.DataFrame.Type: instance (GHC.Read.Read t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.BoundedDims ds, Data.Type.List.All Numeric.Dimensions.Dim.KnownXNatType ds) => GHC.Read.Read (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance (Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Internal.Backend.DFBackend t ds), Numeric.PrimBytes.PrimBytes t) => Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Classes.Eq (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Classes.Eq (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Classes.Ord (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Classes.Ord (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Classes.Ord (Numeric.ProductOrd.NonTransitive.ProductOrd (Numeric.DataFrame.Internal.Backend.DFBackend t ds)) => GHC.Classes.Ord (Numeric.ProductOrd.NonTransitive.ProductOrd (Numeric.DataFrame.Type.DataFrame t ds))
- Numeric.DataFrame.Type: instance GHC.Enum.Bounded (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Enum.Bounded (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Enum.Enum (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Enum.Enum (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Float.Floating (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Float.Floating (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Float.RealFloat (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Float.RealFloat (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Num.Num (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Num.Num (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Real.Fractional (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Real.Fractional (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Real.Integral (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Real.Integral (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Real.Real (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Real.Real (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance GHC.Real.RealFrac (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => GHC.Real.RealFrac (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: instance Numeric.ProductOrd.ProductOrder (Numeric.DataFrame.Internal.Backend.DFBackend t ds) => Numeric.ProductOrd.ProductOrder (Numeric.DataFrame.Type.DataFrame t ds)
- Numeric.DataFrame.Type: type KnownBackend t (ds :: [Nat]) = KnownBackend t ds BackendFamily t ds
- Numeric.Matrix: LUFact :: Matrix t n n -> Matrix t n n -> Matrix t n n -> Scalar t -> LUFact t n
- Numeric.Matrix: [luLower] :: LUFact t n -> Matrix t n n
- Numeric.Matrix: [luPermSign] :: LUFact t n -> Scalar t
- Numeric.Matrix: [luPerm] :: LUFact t n -> Matrix t n n
- Numeric.Matrix: [luUpper] :: LUFact t n -> Matrix t n n
- Numeric.Matrix: class MatrixLU t (n :: Nat)
- Numeric.Matrix: data LUFact t n
- Numeric.Matrix: lu :: MatrixLU t n => Matrix t n n -> LUFact t n
- Numeric.Matrix: luSolve :: forall (t :: Type) (n :: Nat). (KnownDim n, Fractional t, PrimArray t (Matrix t n n), PrimArray t (Vector t n)) => LUFact t n -> Vector t n -> Vector t n
- Numeric.Matrix: pivotMat :: forall (t :: Type) (n :: Nat). (KnownDim n, PrimArray t (Matrix t n n), Ord t, Num t) => Matrix t n n -> (Matrix t n n, Matrix t n n, Scalar t)
+ Numeric.Basics: class (Eq a, Floating a) => Epsilon a
+ Numeric.Basics: class (Real a, PrimBytes a) => RealExtras a
+ Numeric.Basics: class (Epsilon a, RealExtras a, RealFloat a) => RealFloatExtras a
+ Numeric.Basics: copysign :: RealExtras a => a -> a -> a
+ Numeric.Basics: data TooManyIterations
+ Numeric.Basics: epsilon :: Epsilon a => a
+ Numeric.Basics: hypot :: RealFloatExtras a => a -> a -> a
+ Numeric.Basics: instance GHC.Classes.Eq Numeric.Basics.TooManyIterations
+ Numeric.Basics: instance GHC.Classes.Ord Numeric.Basics.TooManyIterations
+ Numeric.Basics: instance GHC.Exception.Type.Exception Numeric.Basics.TooManyIterations
+ Numeric.Basics: instance GHC.Show.Show Numeric.Basics.TooManyIterations
+ Numeric.Basics: instance Numeric.Basics.Epsilon GHC.Types.Double
+ Numeric.Basics: instance Numeric.Basics.Epsilon GHC.Types.Float
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Int.Int16
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Int.Int32
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Int.Int64
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Int.Int8
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Types.Double
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Types.Float
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Types.Int
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Types.Word
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Word.Word16
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Word.Word32
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Word.Word64
+ Numeric.Basics: instance Numeric.Basics.RealExtras GHC.Word.Word8
+ Numeric.Basics: instance Numeric.Basics.RealFloatExtras GHC.Types.Double
+ Numeric.Basics: instance Numeric.Basics.RealFloatExtras GHC.Types.Float
+ Numeric.Basics: maxFinite :: RealFloatExtras a => a
+ Numeric.Basics: negateUnless :: Num t => Bool -> t -> t
+ Numeric.Basics: pattern M_EPS :: Epsilon a => a
+ Numeric.Basics: tooManyIterations :: IterativeMethod => String -> a
+ Numeric.Basics: type IterativeMethod = HasCallStack
+ Numeric.DataFrame: sort :: forall (t :: Type) n ns. (SortableDataFrame t (n : ns), Ord t, SortBy n) => DataFrame t (n : ns) -> DataFrame t (n : ns)
+ Numeric.DataFrame: sortBy :: forall (t :: Type) n ns. (SortableDataFrame t (n : ns), SortBy n) => (DataFrame t ns -> DataFrame t ns -> Ordering) -> DataFrame t (n : ns) -> DataFrame t (n : ns)
+ Numeric.DataFrame.Contraction: instance (Data.Type.List.Classes.ConcatList as bs asbs, Numeric.Dimensions.Dim.Dimensions as, Numeric.Dimensions.Dim.Dimensions bs, GHC.Num.Num t) => Numeric.DataFrame.Contraction.Contraction t as bs asbs
+ Numeric.DataFrame.IO: castDataFrame :: forall (t :: Type) (xns :: [XNat]) (ns :: [Nat]). FixedDims xns ns => IODataFrame t ns -> IODataFrame t xns
+ Numeric.DataFrame.IO: copyDataFrameOff :: forall (t :: Type) as bs asbs. (Dimensions bs, PrimArray t (DataFrame t bs), ConcatList as bs asbs) => Int -> DataFrame t bs -> IODataFrame t asbs -> IO ()
+ Numeric.DataFrame.IO: copyMutableDataFrameOff :: forall (t :: Type) as bs asbs. (ExactDims bs, PrimBytes t, ConcatList as bs asbs) => Int -> IODataFrame t bs -> IODataFrame t asbs -> IO ()
+ Numeric.DataFrame.IO: data IODataFrame (t :: Type) (ns :: [k])
+ Numeric.DataFrame.IO: getDataFrameSteps :: forall (t :: Type) ns. IODataFrame t ns -> CumulDims
+ Numeric.DataFrame.IO: oneMoreDataFrame :: forall (t :: Type) ns. IODataFrame t ns -> IO (IODataFrame t ns)
+ Numeric.DataFrame.IO: pattern XIOFrame :: forall (t :: Type) (xns :: [XNat]). () => forall (ns :: [Nat]). (FixedDims xns ns, Dimensions ns) => IODataFrame t ns -> IODataFrame t xns
+ Numeric.DataFrame.IO: subDataFrameView :: forall (t :: Type) b bi bd as bs asbs. (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> IODataFrame t asbs -> IODataFrame t (bd :+ bs)
+ Numeric.DataFrame.IO: subDataFrameView' :: forall (t :: Type) as bs asbs. ConcatList as bs asbs => Idxs as -> IODataFrame t asbs -> IODataFrame t bs
+ Numeric.DataFrame.IO: withThawDataFrame :: forall (t :: Type) ns r. PrimArray t (DataFrame t ns) => (t -> IO r) -> (IODataFrame t ns -> IO r) -> DataFrame t ns -> IO r
+ Numeric.DataFrame.Internal.Backend: class KnownBackend t ds (BackendFamily t ds) => KnownBackend (t :: Type) (ds :: [Nat])
+ Numeric.DataFrame.Internal.Backend: instance Numeric.DataFrame.Internal.Backend.Family.KnownBackend t ds (Numeric.DataFrame.Internal.Backend.Family.BackendFamily t ds) => Numeric.DataFrame.Internal.Backend.KnownBackend t ds
+ Numeric.DataFrame.Internal.Mutable: castDataFrame# :: forall (t :: Type) (xns :: [XNat]) (ns :: [Nat]) s. FixedDims xns ns => MDataFrame s t ns -> MDataFrame s t xns
+ Numeric.DataFrame.Internal.Mutable: copyDataFrameOff# :: forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (Dimensions bs, PrimArray t (DataFrame t bs), ConcatList as bs asbs) => Int -> DataFrame t bs -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: copyMDataFrameOff# :: forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (ExactDims bs, PrimBytes t, ConcatList as bs asbs) => Int -> MDataFrame s t bs -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: getDataFrameSteps# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. MDataFrame s t ns -> CumulDims
+ Numeric.DataFrame.Internal.Mutable: oneMoreDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. MDataFrame s t ns -> State# s -> (# State# s, MDataFrame s t ns #)
+ Numeric.DataFrame.Internal.Mutable: subDataFrameView# :: forall (t :: Type) (k :: Type) (b :: k) (bi :: k) (bd :: k) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> MDataFrame s t asbs -> MDataFrame s t (bd :+ bs)
+ Numeric.DataFrame.Internal.Mutable: subDataFrameView'# :: forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. ConcatList as bs asbs => Idxs as -> MDataFrame s t asbs -> MDataFrame s t bs
+ Numeric.DataFrame.Internal.Mutable: withThawDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) (r :: Type) s. PrimArray t (DataFrame t ns) => (t -> State# s -> (# State# s, r #)) -> (MDataFrame s t ns -> State# s -> (# State# s, r #)) -> DataFrame t ns -> State# s -> (# State# s, r #)
+ Numeric.DataFrame.Internal.PrimArray: broadcast# :: PrimArray t a => t -> a
+ Numeric.DataFrame.Internal.PrimArray: cdIxM :: CumulDims -> Idxs ns -> Maybe Int
+ Numeric.DataFrame.Internal.PrimArray: cdIxSub :: CumulDims -> Idxs (ns +: idxN) -> Dim subN -> Int
+ Numeric.DataFrame.Internal.PrimArray: fromElems# :: PrimArray t a => CumulDims -> Int# -> ByteArray# -> a
+ Numeric.DataFrame.Internal.PrimArray: getOffAndSteps :: Int -> CumulDims -> Idxs ns -> (Int, CumulDims)
+ Numeric.DataFrame.Internal.PrimArray: getOffAndStepsM :: Int -> CumulDims -> Idxs ns -> Maybe (Int, CumulDims)
+ Numeric.DataFrame.Internal.PrimArray: getOffAndStepsSub :: Int -> CumulDims -> Idxs (ns +: idxN) -> Dim subN -> (Int, CumulDims)
+ Numeric.DataFrame.Internal.PrimArray: getOffAndStepsSubM :: Int -> CumulDims -> Idxs (ns +: idxN) -> Dim subN -> Maybe (Int, CumulDims)
+ Numeric.DataFrame.Internal.PrimArray: instance GHC.Show.Show Numeric.DataFrame.Internal.PrimArray.CumulDims
+ Numeric.DataFrame.Internal.PrimArray: withArrayContent :: forall (t :: Type) (a :: Type) (rep :: RuntimeRep) (r :: TYPE rep). PrimArray t a => (t -> r) -> (CumulDims -> Int# -> ByteArray# -> r) -> a -> r
+ Numeric.DataFrame.Internal.PrimArray: withArrayContent# :: forall (rep :: RuntimeRep) (r :: TYPE rep). PrimArray t a => (t -> r) -> (CumulDims -> Int# -> ByteArray# -> r) -> a -> r
+ Numeric.DataFrame.ST: castDataFrame :: forall (t :: Type) (xns :: [XNat]) (ns :: [Nat]) s. FixedDims xns ns => STDataFrame s t ns -> STDataFrame s t xns
+ Numeric.DataFrame.ST: copyDataFrameOff :: forall (t :: Type) as bs asbs s. (Dimensions bs, PrimArray t (DataFrame t bs), ConcatList as bs asbs) => Int -> DataFrame t bs -> STDataFrame s t asbs -> ST s ()
+ Numeric.DataFrame.ST: copyMutableDataFrameOff :: forall (t :: Type) as bs asbs s. (ExactDims bs, PrimBytes t, ConcatList as bs asbs) => Int -> STDataFrame s t bs -> STDataFrame s t asbs -> ST s ()
+ Numeric.DataFrame.ST: data STDataFrame s (t :: Type) (ns :: [k])
+ Numeric.DataFrame.ST: getDataFrameSteps :: forall (t :: Type) ns s. STDataFrame s t ns -> CumulDims
+ Numeric.DataFrame.ST: oneMoreDataFrame :: forall (t :: Type) ns s. STDataFrame s t ns -> ST s (STDataFrame s t ns)
+ Numeric.DataFrame.ST: pattern XSTFrame :: forall s (t :: Type) (xns :: [XNat]). () => forall (ns :: [Nat]). (FixedDims xns ns, Dimensions ns) => STDataFrame s t ns -> STDataFrame s t xns
+ Numeric.DataFrame.ST: subDataFrameView :: forall (t :: Type) b bi bd as bs asbs s. (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> STDataFrame s t asbs -> STDataFrame s t (bd :+ bs)
+ Numeric.DataFrame.ST: subDataFrameView' :: forall (t :: Type) as bs asbs s. ConcatList as bs asbs => Idxs as -> STDataFrame s t asbs -> STDataFrame s t bs
+ Numeric.DataFrame.ST: withThawDataFrame :: forall (t :: Type) ns r s. PrimArray t (DataFrame t ns) => (t -> ST s r) -> (STDataFrame s t ns -> ST s r) -> DataFrame t ns -> ST s r
+ Numeric.DataFrame.SubSpace: (.!) :: forall t a bs. SubSpace t '[a] bs (a :+ bs) => DataFrame t (a :+ bs) -> Idx a -> DataFrame t bs
+ Numeric.DataFrame.SubSpace: ewfoldl' :: forall t as bs asbs b. SubSpace t as bs asbs => (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: ewfoldr' :: forall t as bs asbs b. SubSpace t as bs asbs => (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: indexOffset :: forall t as bs asbs. SubSpace t as bs asbs => Int -> DataFrame t asbs -> DataFrame t bs
+ Numeric.DataFrame.SubSpace: instance (Data.Type.List.Classes.ConcatList as bs asbs, Numeric.DataFrame.SubSpace.SubSpaceCtx t as bs asbs) => Numeric.DataFrame.SubSpace.SubSpace t as bs asbs
+ Numeric.DataFrame.SubSpace: iwfoldl' :: forall t as bs asbs b. SubSpace t as bs asbs => (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: iwfoldr' :: forall t as bs asbs b. SubSpace t as bs asbs => (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: joinDataFrame :: forall t as bs asbs. (SubSpace t as bs asbs, PrimBytes (DataFrame t bs)) => DataFrame (DataFrame t bs) as -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: lookup :: forall t (as :: [XNat]) (bs :: [XNat]) (asbs :: [XNat]). (ConcatList as bs asbs, All KnownDimType bs, PrimBytes t) => Idxs as -> DataFrame t asbs -> Maybe (DataFrame t bs)
+ Numeric.DataFrame.SubSpace: selement :: forall t a bs f. (SubSpace t '[a] bs (a :+ bs), Applicative f) => Idx a -> (DataFrame t bs -> f (DataFrame t bs)) -> DataFrame t (a :+ bs) -> f (DataFrame t (a :+ bs))
+ Numeric.DataFrame.SubSpace: selementWise :: forall t a bs s bs' f. (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs'), Applicative f) => (DataFrame s bs' -> f (DataFrame t bs)) -> DataFrame s (a :+ bs') -> f (DataFrame t (a :+ bs))
+ Numeric.DataFrame.SubSpace: selementWise_ :: forall t a bs f b. (SubSpace t '[a] bs (a :+ bs), Applicative f) => (DataFrame t bs -> f b) -> DataFrame t (a :+ bs) -> f ()
+ Numeric.DataFrame.SubSpace: sewfoldMap :: forall t a bs m. (SubSpace t '[a] bs (a :+ bs), Monoid m) => (DataFrame t bs -> m) -> DataFrame t (a :+ bs) -> m
+ Numeric.DataFrame.SubSpace: sewfoldl :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: sewfoldl' :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: sewfoldr :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: sewfoldr' :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: sewgen :: forall t a bs. (SubSpace t '[a] bs (a :+ bs), Dimensions '[a]) => DataFrame t bs -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: sewmap :: forall t a bs s bs'. (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs')) => (DataFrame s bs' -> DataFrame t bs) -> DataFrame s (a :+ bs') -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: sewzip :: forall t a bs l bsL r bsR. (SubSpace t '[a] bs (a :+ bs), SubSpace l '[a] bsL (a :+ bsL), SubSpace r '[a] bsR (a :+ bsR)) => (DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs) -> DataFrame l (a :+ bsL) -> DataFrame r (a :+ bsR) -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: sindexOffset :: forall t a bs. SubSpace t '[a] bs (a :+ bs) => Int -> DataFrame t (a :+ bs) -> DataFrame t bs
+ Numeric.DataFrame.SubSpace: sindexWise :: forall t a bs s bs' f. (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs'), Applicative f) => (Idx a -> DataFrame s bs' -> f (DataFrame t bs)) -> DataFrame s (a :+ bs') -> f (DataFrame t (a :+ bs))
+ Numeric.DataFrame.SubSpace: sindexWise_ :: forall t a bs f b. (SubSpace t '[a] bs (a :+ bs), Applicative f) => (Idx a -> DataFrame t bs -> f b) -> DataFrame t (a :+ bs) -> f ()
+ Numeric.DataFrame.SubSpace: siwfoldMap :: forall t a bs m. (SubSpace t '[a] bs (a :+ bs), Monoid m) => (Idx a -> DataFrame t bs -> m) -> DataFrame t (a :+ bs) -> m
+ Numeric.DataFrame.SubSpace: siwfoldl :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (Idx a -> b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: siwfoldl' :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (Idx a -> b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: siwfoldr :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (Idx a -> DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: siwfoldr' :: forall t a bs b. SubSpace t '[a] bs (a :+ bs) => (Idx a -> DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b
+ Numeric.DataFrame.SubSpace: siwgen :: forall t a bs. (SubSpace t '[a] bs (a :+ bs), Dimensions '[a]) => (Idx a -> DataFrame t bs) -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: siwmap :: forall t a bs s bs'. (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs')) => (Idx a -> DataFrame s bs' -> DataFrame t bs) -> DataFrame s (a :+ bs') -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: siwzip :: forall t a bs l bsL r bsR. (SubSpace t '[a] bs (a :+ bs), SubSpace l '[a] bsL (a :+ bsL), SubSpace r '[a] bsR (a :+ bsR)) => (Idx a -> DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs) -> DataFrame l (a :+ bsL) -> DataFrame r (a :+ bsR) -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: sliceMaybe :: forall (t :: Type) (b :: XNat) bi bd as bs asbs. (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs, All KnownDimType bs, PrimBytes t) => Idxs (as +: bi) -> DataFrame t asbs -> Maybe (DataFrame t (bd :+ bs))
+ Numeric.DataFrame.SubSpace: slookup :: forall t (a :: XNat) (bs :: [XNat]). (All KnownDimType bs, PrimBytes t) => Idx a -> DataFrame t (a :+ bs) -> Maybe (DataFrame t bs)
+ Numeric.DataFrame.SubSpace: sslice :: forall t b bi bd bs. (KnownDimKind (KindOfEl bs), CanSlice t (b :+ bs), SubFrameIndexCtx b bi bd, KnownDim bd, PrimArray t (DataFrame t (bd :+ bs))) => Idx bi -> DataFrame t (b :+ bs) -> DataFrame t (bd :+ bs)
+ Numeric.DataFrame.SubSpace: ssliceMaybe :: forall (t :: Type) (b :: XNat) (bi :: XNat) (bd :: XNat) (bs :: [XNat]). (SubFrameIndexCtx b bi bd, KnownDim bd, All KnownDimType bs, PrimBytes t) => Idx bi -> DataFrame t (b :+ bs) -> Maybe (DataFrame t (bd :+ bs))
+ Numeric.DataFrame.SubSpace: supdate :: forall t a bs. SubSpace t '[a] bs (a :+ bs) => Idx a -> DataFrame t bs -> DataFrame t (a :+ bs) -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: supdateOffset :: forall t a bs. SubSpace t '[a] bs (a :+ bs) => Int -> DataFrame t bs -> DataFrame t (a :+ bs) -> DataFrame t (a :+ bs)
+ Numeric.DataFrame.SubSpace: supdateSlice :: forall t b bi bd bs. (KnownDimKind (KindOfEl bs), CanSlice t (b :+ bs), SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs, PrimArray t (DataFrame t (bd :+ bs))) => Idx bi -> DataFrame t (bd :+ bs) -> DataFrame t (b :+ bs) -> DataFrame t (b :+ bs)
+ Numeric.DataFrame.SubSpace: type family CanSlice (t :: Type) (asbs :: [k]) :: Constraint
+ Numeric.DataFrame.SubSpace: updateOffset :: forall t as bs asbs. SubSpace t as bs asbs => Int -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: }
+ Numeric.DataFrame.Type: (!) :: IndexFrame t d ds => DataFrame t (d : ds) -> Word -> DataFrame t ds
+ Numeric.DataFrame.Type: asDiag :: forall (n :: Nat) (m :: Nat) (ds :: [Nat]) (t :: Type). (Dimensions ds, KnownDim n, KnownDim m, KnownBackend t (Min n m :+ ds), KnownBackend t (n :+ (m :+ ds)), PrimBytes t) => DataFrame t (Min n m :+ ds) -> DataFrame t (n :+ (m :+ ds))
+ Numeric.DataFrame.Type: class IndexFrame t d ds
+ Numeric.DataFrame.Type: class KnownBackend t ds BackendFamily t ds => KnownBackend t (ds :: [Nat])
+ Numeric.DataFrame.Type: data Nat
+ Numeric.DataFrame.Type: instance (Data.Type.List.All GHC.Read.Read ts, Data.Type.List.All Numeric.PrimBytes.PrimBytes ts, Numeric.TypedList.RepresentableList ts, Numeric.Dimensions.Dim.BoundedDims ds, Data.Type.List.All Numeric.Dimensions.Dim.KnownDimType ds) => GHC.Read.Read (Numeric.DataFrame.Type.DataFrame ts ds)
+ Numeric.DataFrame.Type: instance (GHC.Classes.Ord (Numeric.ProductOrd.Partial.ProductOrd (Numeric.DataFrame.Internal.BackendI.DFBackend t ds)), GHC.Classes.Eq (Numeric.DataFrame.Internal.BackendI.DFBackend t ds)) => GHC.Classes.Ord (Numeric.ProductOrd.Partial.ProductOrd (Numeric.DataFrame.Type.DataFrame t ds))
+ Numeric.DataFrame.Type: instance (GHC.Float.RealFloat (Numeric.DataFrame.Type.DataFrame t '[]), Numeric.DataFrame.Internal.Backend.KnownBackend t '[], GHC.Classes.Eq t) => GHC.Float.RealFloat (Numeric.DataFrame.Type.DataFrame t '[])
+ Numeric.DataFrame.Type: instance (GHC.Read.Read t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.BoundedDims ds, Data.Type.List.All Numeric.Dimensions.Dim.KnownDimType ds) => GHC.Read.Read (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance (GHC.Real.RealFrac (Numeric.DataFrame.Type.DataFrame t '[]), Numeric.DataFrame.Internal.Backend.KnownBackend t '[], GHC.Classes.Eq t) => GHC.Real.RealFrac (Numeric.DataFrame.Type.DataFrame t '[])
+ Numeric.DataFrame.Type: instance (Numeric.Basics.RealExtras (Numeric.DataFrame.Type.DataFrame t '[]), Numeric.DataFrame.Internal.Backend.KnownBackend t '[], GHC.Classes.Eq t) => Numeric.Basics.RealExtras (Numeric.DataFrame.Type.DataFrame t '[])
+ Numeric.DataFrame.Type: instance (Numeric.Basics.RealFloatExtras (Numeric.DataFrame.Type.DataFrame t '[]), Numeric.DataFrame.Internal.Backend.KnownBackend t '[], GHC.Classes.Eq t) => Numeric.Basics.RealFloatExtras (Numeric.DataFrame.Type.DataFrame t '[])
+ Numeric.DataFrame.Type: instance (Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Internal.BackendI.DFBackend t ds), Numeric.PrimBytes.PrimBytes t) => Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance (Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t '[d]), Numeric.Dimensions.Dim.KnownDim d) => Numeric.DataFrame.Type.IndexFrame t d '[]
+ Numeric.DataFrame.Type: instance (Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t (d : ds)), Numeric.Dimensions.Dim.KnownDim d, Numeric.DataFrame.Internal.Backend.KnownBackend t ds) => Numeric.DataFrame.Type.IndexFrame t d ds
+ Numeric.DataFrame.Type: instance (Numeric.DataFrame.Type.AllFrames GHC.Classes.Eq ts ds, Numeric.DataFrame.Type.AllFrames GHC.Classes.Ord ts ds) => GHC.Classes.Ord (Numeric.DataFrame.Type.DataFrame ts ds)
+ Numeric.DataFrame.Type: instance (Numeric.DataFrame.Type.IndexFrame t d ds, Numeric.DataFrame.Type.IndexFrame ts d ds) => Numeric.DataFrame.Type.IndexFrame (t : ts) d ds
+ Numeric.DataFrame.Type: instance (Numeric.Dimensions.Dim.Dimensions xns, Numeric.DataFrame.Internal.Backend.KnownBackend t (Numeric.Dimensions.Dim.DimsBound xns), Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t (Numeric.Dimensions.Dim.DimsBound xns)), Numeric.PrimBytes.PrimBytes t) => Numeric.DataFrame.Internal.PrimArray.PrimArray t (Numeric.DataFrame.Type.DataFrame t xns)
+ Numeric.DataFrame.Type: instance (Numeric.TypedList.RepresentableList ts, Data.Type.List.All Numeric.PrimBytes.PrimBytes ts) => Numeric.DataFrame.Type.IndexFrame ts xd xds
+ Numeric.DataFrame.Type: instance GHC.Classes.Eq (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Classes.Eq (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Classes.Ord (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Classes.Ord (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Classes.Ord (Numeric.ProductOrd.NonTransitive.ProductOrd (Numeric.DataFrame.Internal.BackendI.DFBackend t ds)) => GHC.Classes.Ord (Numeric.ProductOrd.NonTransitive.ProductOrd (Numeric.DataFrame.Type.DataFrame t ds))
+ Numeric.DataFrame.Type: instance GHC.Enum.Bounded (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Enum.Bounded (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Enum.Enum (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Enum.Enum (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Float.Floating (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Float.Floating (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Float.RealFloat (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Float.RealFloat (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Num.Num (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Num.Num (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Real.Fractional (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Real.Fractional (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Real.Integral (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Real.Integral (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Real.Real (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Real.Real (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance GHC.Real.RealFrac (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => GHC.Real.RealFrac (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance Numeric.Basics.Epsilon (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => Numeric.Basics.Epsilon (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance Numeric.Basics.RealExtras (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => Numeric.Basics.RealExtras (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance Numeric.Basics.RealFloatExtras (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => Numeric.Basics.RealFloatExtras (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance Numeric.DataFrame.Type.IndexFrame '[] d ds
+ Numeric.DataFrame.Type: instance Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance Numeric.PrimBytes.PrimBytes t => Numeric.DataFrame.Type.IndexFrame t xd xds
+ Numeric.DataFrame.Type: instance Numeric.ProductOrd.ProductOrder (Numeric.DataFrame.Internal.BackendI.DFBackend t ds) => Numeric.ProductOrd.ProductOrder (Numeric.DataFrame.Type.DataFrame t ds)
+ Numeric.DataFrame.Type: instance forall l (ts :: l). (GHC.Enum.Enum (Numeric.DataFrame.Type.DataFrame ts '[]), Numeric.DataFrame.Type.KnownBackends ts '[]) => GHC.Enum.Enum (Numeric.DataFrame.Type.DataFrame ts '[])
+ Numeric.DataFrame.Type: instance forall l (ts :: l). (GHC.Real.Integral (Numeric.DataFrame.Type.DataFrame ts '[]), Numeric.DataFrame.Type.KnownBackends ts '[], GHC.Classes.Eq (Numeric.DataFrame.Type.DataFrame ts '[])) => GHC.Real.Integral (Numeric.DataFrame.Type.DataFrame ts '[])
+ Numeric.DataFrame.Type: instance forall l (ts :: l). (GHC.Real.Real (Numeric.DataFrame.Type.DataFrame ts '[]), Numeric.DataFrame.Type.KnownBackends ts '[], GHC.Classes.Eq (Numeric.DataFrame.Type.DataFrame ts '[])) => GHC.Real.Real (Numeric.DataFrame.Type.DataFrame ts '[])
+ Numeric.DataFrame.Type: instance forall l (ts :: l). (Numeric.Basics.Epsilon (Numeric.DataFrame.Type.DataFrame ts '[]), Numeric.DataFrame.Type.KnownBackends ts '[], GHC.Classes.Eq (Numeric.DataFrame.Type.DataFrame ts '[])) => Numeric.Basics.Epsilon (Numeric.DataFrame.Type.DataFrame ts '[])
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (Numeric.Dimensions.Dim.Dimensions xns, Numeric.DataFrame.Type.KnownBackends ts (Numeric.Dimensions.Dim.DimsBound xns), GHC.Enum.Bounded (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => GHC.Enum.Bounded (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (Numeric.Dimensions.Dim.Dimensions xns, Numeric.DataFrame.Type.KnownBackends ts (Numeric.Dimensions.Dim.DimsBound xns), GHC.Float.Floating (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => GHC.Float.Floating (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (Numeric.Dimensions.Dim.Dimensions xns, Numeric.DataFrame.Type.KnownBackends ts (Numeric.Dimensions.Dim.DimsBound xns), GHC.Num.Num (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => GHC.Num.Num (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (Numeric.Dimensions.Dim.Dimensions xns, Numeric.DataFrame.Type.KnownBackends ts (Numeric.Dimensions.Dim.DimsBound xns), GHC.Real.Fractional (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => GHC.Real.Fractional (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (Numeric.Dimensions.Dim.Dimensions xns, Numeric.DataFrame.Type.KnownBackends ts (Numeric.Dimensions.Dim.DimsBound xns), Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => Numeric.PrimBytes.PrimBytes (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (xns Data.Type.Equality.~ Data.Type.List.Map 'Numeric.Dimensions.Dim.N (Numeric.Dimensions.Dim.DimsBound xns), GHC.Classes.Eq (Numeric.DataFrame.Type.DataFrame ts xns), GHC.Classes.Ord (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => GHC.Classes.Ord (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: instance forall l (xns :: [Numeric.Dimensions.Dim.XNat]) (ts :: l). (xns Data.Type.Equality.~ Data.Type.List.Map 'Numeric.Dimensions.Dim.N (Numeric.Dimensions.Dim.DimsBound xns), Numeric.ProductOrd.ProductOrder (Numeric.DataFrame.Type.DataFrame ts (Numeric.Dimensions.Dim.DimsBound xns))) => Numeric.ProductOrd.ProductOrder (Numeric.DataFrame.Type.DataFrame ts xns)
+ Numeric.DataFrame.Type: pattern XIdxs :: forall (ds :: [XNat]) (ns :: [Nat]). (FixedDims ds ns, Dimensions ns) => () => Idxs ns -> Idxs ds
+ Numeric.DataFrame.Type: scalar :: t -> DataFrame t ('[] :: [Nat])
+ Numeric.DataFrame.Type: unScalar :: DataFrame t ('[] :: [Nat]) -> t
+ Numeric.Matrix.Bidiagonal: BiDiag :: Matrix t n n -> Scalar t -> Vector t (Min n m) -> Vector t (Min n m) -> Matrix t m m -> Scalar t -> BiDiag
+ Numeric.Matrix.Bidiagonal: [bdAlpha] :: BiDiag -> Vector t (Min n m)
+ Numeric.Matrix.Bidiagonal: [bdBeta] :: BiDiag -> Vector t (Min n m)
+ Numeric.Matrix.Bidiagonal: [bdUDet] :: BiDiag -> Scalar t
+ Numeric.Matrix.Bidiagonal: [bdU] :: BiDiag -> Matrix t n n
+ Numeric.Matrix.Bidiagonal: [bdVDet] :: BiDiag -> Scalar t
+ Numeric.Matrix.Bidiagonal: [bdV] :: BiDiag -> Matrix t m m
+ Numeric.Matrix.Bidiagonal: biDiag :: forall (t :: Type) (n :: Nat) (m :: Nat). (PrimBytes t, Num t) => Dims '[n, m] -> Vector t (Min n m) -> Vector t (Min n m) -> Matrix t n m
+ Numeric.Matrix.Bidiagonal: bidiagonalHouseholder :: forall (t :: Type) (n :: Nat) (m :: Nat). (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m) => Matrix t n m -> BiDiag t n m
+ Numeric.Matrix.Bidiagonal: data BiDiag (t :: Type) (n :: Nat) (m :: Nat)
+ Numeric.Matrix.Bidiagonal: instance (GHC.Classes.Eq t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m, Numeric.Dimensions.Dim.KnownDim (Data.Type.Lits.Min n m), Numeric.DataFrame.Internal.Backend.KnownBackend t '[Data.Type.Lits.Min n m]) => GHC.Classes.Eq (Numeric.Matrix.Bidiagonal.BiDiag t n m)
+ Numeric.Matrix.Bidiagonal: instance (GHC.Show.Show t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m, Numeric.Dimensions.Dim.KnownDim (Data.Type.Lits.Min n m)) => GHC.Show.Show (Numeric.Matrix.Bidiagonal.BiDiag t n m)
+ Numeric.Matrix.LU: LU :: Matrix t n n -> Matrix t n n -> Matrix t n n -> Scalar t -> LU
+ Numeric.Matrix.LU: [luLower] :: LU -> Matrix t n n
+ Numeric.Matrix.LU: [luPermDet] :: LU -> Scalar t
+ Numeric.Matrix.LU: [luPerm] :: LU -> Matrix t n n
+ Numeric.Matrix.LU: [luUpper] :: LU -> Matrix t n n
+ Numeric.Matrix.LU: class (KnownDim n, Ord t, Fractional t, PrimBytes t, KnownBackend t '[n, n]) => MatrixLU t (n :: Nat)
+ Numeric.Matrix.LU: data LU (t :: Type) (n :: Nat)
+ Numeric.Matrix.LU: detViaLU :: forall (t :: Type) (n :: Nat). MatrixLU t n => Matrix t n n -> Scalar t
+ Numeric.Matrix.LU: instance (GHC.Classes.Eq (Numeric.Matrix.Internal.Matrix t n n), GHC.Classes.Eq t) => GHC.Classes.Eq (Numeric.Matrix.LU.LU t n)
+ Numeric.Matrix.LU: instance (GHC.Show.Show t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n) => GHC.Show.Show (Numeric.Matrix.LU.LU t n)
+ Numeric.Matrix.LU: instance (Numeric.Dimensions.Dim.KnownDim n, GHC.Classes.Ord t, GHC.Real.Fractional t, Numeric.PrimBytes.PrimBytes t, Numeric.DataFrame.Internal.Backend.KnownBackend t '[n, n]) => Numeric.Matrix.LU.MatrixLU t n
+ Numeric.Matrix.LU: inverseViaLU :: forall (t :: Type) (n :: Nat). MatrixLU t n => Matrix t n n -> Matrix t n n
+ Numeric.Matrix.LU: lu :: MatrixLU t n => Matrix t n n -> LU t n
+ Numeric.Matrix.LU: luSolveL :: forall t (n :: Nat) (ds :: [Nat]). (MatrixLU t n, Dimensions ds) => LU t n -> DataFrame t (ds +: n) -> DataFrame t (ds +: n)
+ Numeric.Matrix.LU: luSolveR :: forall t (n :: Nat) (ds :: [Nat]). (MatrixLU t n, Dimensions ds) => LU t n -> DataFrame t (n :+ ds) -> DataFrame t (n :+ ds)
+ Numeric.Matrix.QR: LQ :: Matrix t n m -> Matrix t m m -> Scalar t -> LQ
+ Numeric.Matrix.QR: QR :: Matrix t n n -> Scalar t -> Matrix t n m -> QR
+ Numeric.Matrix.QR: [lqL] :: LQ -> Matrix t n m
+ Numeric.Matrix.QR: [lqQDet] :: LQ -> Scalar t
+ Numeric.Matrix.QR: [lqQ] :: LQ -> Matrix t m m
+ Numeric.Matrix.QR: [qrQDet] :: QR -> Scalar t
+ Numeric.Matrix.QR: [qrQ] :: QR -> Matrix t n n
+ Numeric.Matrix.QR: [qrR] :: QR -> Matrix t n m
+ Numeric.Matrix.QR: class (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m) => MatrixQR t (n :: Nat) (m :: Nat)
+ Numeric.Matrix.QR: data LQ (t :: Type) (n :: Nat) (m :: Nat)
+ Numeric.Matrix.QR: data QR (t :: Type) (n :: Nat) (m :: Nat)
+ Numeric.Matrix.QR: detViaQR :: forall t n. MatrixQR t n n => Matrix t n n -> Scalar t
+ Numeric.Matrix.QR: instance (GHC.Classes.Eq t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m) => GHC.Classes.Eq (Numeric.Matrix.QR.LQ t n m)
+ Numeric.Matrix.QR: instance (GHC.Classes.Eq t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m) => GHC.Classes.Eq (Numeric.Matrix.QR.QR t n m)
+ Numeric.Matrix.QR: instance (GHC.Show.Show t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m) => GHC.Show.Show (Numeric.Matrix.QR.LQ t n m)
+ Numeric.Matrix.QR: instance (GHC.Show.Show t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m) => GHC.Show.Show (Numeric.Matrix.QR.QR t n m)
+ Numeric.Matrix.QR: instance (Numeric.PrimBytes.PrimBytes t, GHC.Classes.Ord t, Numeric.Basics.Epsilon t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m) => Numeric.Matrix.QR.MatrixQR t n m
+ Numeric.Matrix.QR: inverseViaQR :: forall t n. MatrixQR t n n => Matrix t n n -> Matrix t n n
+ Numeric.Matrix.QR: lq :: MatrixQR t n m => Matrix t n m -> LQ t n m
+ Numeric.Matrix.QR: qr :: MatrixQR t n m => Matrix t n m -> QR t n m
+ Numeric.Matrix.QR: qrSolveL :: forall t (n :: Nat) (m :: Nat) (ds :: [Nat]). (MatrixQR t n m, Dimensions ds) => Matrix t n m -> DataFrame t (ds +: m) -> DataFrame t (ds +: n)
+ Numeric.Matrix.QR: qrSolveR :: forall t (n :: Nat) (m :: Nat) (ds :: [Nat]). (MatrixQR t n m, Dimensions ds) => Matrix t n m -> DataFrame t (n :+ ds) -> DataFrame t (m :+ ds)
+ Numeric.Matrix.SVD: SVD :: Matrix t n n -> Vector t (Min n m) -> Matrix t m m -> SVD
+ Numeric.Matrix.SVD: [svdS] :: SVD -> Vector t (Min n m)
+ Numeric.Matrix.SVD: [svdU] :: SVD -> Matrix t n n
+ Numeric.Matrix.SVD: [svdV] :: SVD -> Matrix t m m
+ Numeric.Matrix.SVD: class RealFloatExtras t => MatrixSVD (t :: Type) (n :: Nat) (m :: Nat)
+ Numeric.Matrix.SVD: data SVD (t :: Type) (n :: Nat) (m :: Nat)
+ Numeric.Matrix.SVD: instance (GHC.Classes.Eq (Numeric.Matrix.Internal.Matrix t n n), GHC.Classes.Eq (Numeric.Matrix.Internal.Matrix t m m), GHC.Classes.Eq (Numeric.Vector.Internal.Vector t (Data.Type.Lits.Min n m))) => GHC.Classes.Eq (Numeric.Matrix.SVD.SVD t n m)
+ Numeric.Matrix.SVD: instance (GHC.Show.Show t, Numeric.PrimBytes.PrimBytes t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m, Numeric.Dimensions.Dim.KnownDim (Data.Type.Lits.Min n m)) => GHC.Show.Show (Numeric.Matrix.SVD.SVD t n m)
+ Numeric.Matrix.SVD: instance (Numeric.Basics.RealFloatExtras t, Numeric.Dimensions.Dim.KnownDim n, Numeric.Dimensions.Dim.KnownDim m) => Numeric.Matrix.SVD.MatrixSVD t n m
+ Numeric.Matrix.SVD: instance (Numeric.Basics.RealFloatExtras t, Numeric.Quaternion.Internal.Quaternion t) => Numeric.Matrix.SVD.MatrixSVD t 3 3
+ Numeric.Matrix.SVD: instance Numeric.Basics.RealFloatExtras t => Numeric.Matrix.SVD.MatrixSVD t 1 1
+ Numeric.Matrix.SVD: instance Numeric.Basics.RealFloatExtras t => Numeric.Matrix.SVD.MatrixSVD t 2 2
+ Numeric.Matrix.SVD: svd :: (MatrixSVD t n m, IterativeMethod) => Matrix t n m -> SVD t n m
+ Numeric.Matrix.SVD: svd1 :: (PrimBytes t, Num t, Eq t) => Matrix t 1 1 -> SVD t 1 1
+ Numeric.Matrix.SVD: svd2 :: forall t. RealFloatExtras t => Matrix t 2 2 -> SVD t 2 2
+ Numeric.Matrix.SVD: svd3 :: forall t. (Quaternion t, RealFloatExtras t) => Matrix t 3 3 -> SVD t 3 3
+ Numeric.Matrix.SVD: svd3q :: forall t. (Quaternion t, RealFloatExtras t) => Matrix t 3 3 -> (Quater t, Vector t 3, Quater t)
+ Numeric.Subroutine.Householder: householderReflectionInplaceL :: forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat). (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m) => STDataFrame s t '[n] -> STDataFrame s t '[n, n] -> STDataFrame s t '[n, m] -> Idxs '[n, m] -> ST s Bool
+ Numeric.Subroutine.Householder: householderReflectionInplaceR :: forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat). (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m) => STDataFrame s t '[m] -> STDataFrame s t '[m, m] -> STDataFrame s t '[n, m] -> Idxs '[n, m] -> ST s Bool
+ Numeric.Subroutine.SolveTriangular: solveLowerTriangularL :: forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat]). (PrimBytes t, Fractional t, Eq t, KnownDim n, KnownDim m, n <= m) => STDataFrame s t (ds +: m) -> DataFrame t '[n, m] -> ST s ()
+ Numeric.Subroutine.SolveTriangular: solveLowerTriangularR :: forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat]). (PrimBytes t, Fractional t, Eq t, KnownDim n, KnownDim m, n <= m) => DataFrame t '[n, m] -> STDataFrame s t (n :+ ds) -> ST s ()
+ Numeric.Subroutine.SolveTriangular: solveUpperTriangularL :: forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat]). (PrimBytes t, Fractional t, Eq t, KnownDim m, m <= n) => STDataFrame s t (ds +: m) -> DataFrame t '[n, m] -> ST s ()
+ Numeric.Subroutine.SolveTriangular: solveUpperTriangularR :: forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat]). (PrimBytes t, Fractional t, Eq t, KnownDim m, m <= n) => DataFrame t '[n, m] -> STDataFrame s t (m :+ ds) -> ST s ()
+ Numeric.Subroutine.Sort: class BoundedDim n => SortBy n
+ Numeric.Subroutine.Sort: instance Numeric.Dimensions.Dim.BoundedDim xn => Numeric.Subroutine.Sort.SortBy xn
+ Numeric.Subroutine.Sort: instance Numeric.Dimensions.Dim.KnownDim n => Numeric.Subroutine.Sort.SortBy n
+ Numeric.Subroutine.Sort: instance Numeric.Subroutine.Sort.SortBy 0
+ Numeric.Subroutine.Sort: instance Numeric.Subroutine.Sort.SortBy 1
+ Numeric.Subroutine.Sort: instance Numeric.Subroutine.Sort.SortBy 2
+ Numeric.Subroutine.Sort: instance Numeric.Subroutine.Sort.SortBy 3
+ Numeric.Subroutine.Sort: instance Numeric.Subroutine.Sort.SortBy 4
+ Numeric.Subroutine.Sort: sort :: forall (t :: Type) n ns. (SortableDataFrame t (n : ns), Ord t, SortBy n) => DataFrame t (n : ns) -> DataFrame t (n : ns)
+ Numeric.Subroutine.Sort: sortBy :: forall (t :: Type) n ns. (SortableDataFrame t (n : ns), SortBy n) => (DataFrame t ns -> DataFrame t ns -> Ordering) -> DataFrame t (n : ns) -> DataFrame t (n : ns)
+ Numeric.Subroutine.Sort: sortByInplace :: (SortBy n, PrimBytes t) => (STDataFrame s t ns -> STDataFrame s t ns -> ST s Ordering) -> STDataFrame s t (n : ns) -> ST s ()
+ Numeric.Subroutine.Sort: type family SortableDataFrame (t :: Type) (ns :: [k]) :: Constraint
- Numeric.DataFrame.IO: copyDataFrame :: forall (t :: Type) (b :: Nat) (bi :: Nat) (bd :: Nat) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]). (b ~ ((bi + bd) - 1), PrimBytes t, PrimBytes (DataFrame t (bd :+ bs)), ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> IODataFrame t asbs -> IO ()
+ Numeric.DataFrame.IO: copyDataFrame :: forall (t :: Type) b bi bd as bs asbs. (SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs, PrimArray t (DataFrame t (bd :+ bs)), ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> IODataFrame t asbs -> IO ()
- Numeric.DataFrame.IO: copyDataFrame' :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]). (PrimBytes t, PrimBytes (DataFrame t bs), ConcatList as bs asbs) => Idxs as -> DataFrame t bs -> IODataFrame t asbs -> IO ()
+ Numeric.DataFrame.IO: copyDataFrame' :: forall (t :: Type) as bs asbs. (ExactDims bs, PrimArray t (DataFrame t bs), ConcatList as bs asbs) => Idxs as -> DataFrame t bs -> IODataFrame t asbs -> IO ()
- Numeric.DataFrame.IO: copyMutableDataFrame :: forall (t :: Type) (b :: Nat) (bi :: Nat) (bd :: Nat) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]). (b ~ ((bi + bd) - 1), PrimBytes t, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> IODataFrame t (bd :+ bs) -> IODataFrame t asbs -> IO ()
+ Numeric.DataFrame.IO: copyMutableDataFrame :: forall (t :: Type) b bi bd as bs asbs. (SubFrameIndexCtx b bi bd, ExactDims bs, PrimBytes t, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> IODataFrame t (bd :+ bs) -> IODataFrame t asbs -> IO ()
- Numeric.DataFrame.IO: copyMutableDataFrame' :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]). (PrimBytes t, ConcatList as bs asbs) => Idxs as -> IODataFrame t bs -> IODataFrame t asbs -> IO ()
+ Numeric.DataFrame.IO: copyMutableDataFrame' :: forall (t :: Type) as bs asbs. (ExactDims bs, PrimBytes t, ConcatList as bs asbs) => Idxs as -> IODataFrame t bs -> IODataFrame t asbs -> IO ()
- Numeric.DataFrame.IO: freezeDataFrame :: forall (t :: Type) (ns :: [Nat]). PrimArray t (DataFrame t ns) => IODataFrame t ns -> IO (DataFrame t ns)
+ Numeric.DataFrame.IO: freezeDataFrame :: forall (t :: Type) ns. PrimArray t (DataFrame t ns) => IODataFrame t ns -> IO (DataFrame t ns)
- Numeric.DataFrame.IO: isDataFramePinned :: forall (k :: Type) (t :: Type) (ns :: [k]). KnownDimKind k => IODataFrame t ns -> Bool
+ Numeric.DataFrame.IO: isDataFramePinned :: forall (t :: Type) ns. IODataFrame t ns -> Bool
- Numeric.DataFrame.IO: newDataFrame :: forall t (ns :: [Nat]). (PrimBytes t, Dimensions ns) => IO (IODataFrame t ns)
+ Numeric.DataFrame.IO: newDataFrame :: forall (t :: Type) ns. (PrimBytes t, Dimensions ns) => IO (IODataFrame t ns)
- Numeric.DataFrame.IO: newPinnedDataFrame :: forall t (ns :: [Nat]). (PrimBytes t, Dimensions ns) => IO (IODataFrame t ns)
+ Numeric.DataFrame.IO: newPinnedDataFrame :: forall (t :: Type) ns. (PrimBytes t, Dimensions ns) => IO (IODataFrame t ns)
- Numeric.DataFrame.IO: readDataFrame :: forall (t :: Type) (ns :: [Nat]). PrimBytes t => IODataFrame t ns -> Idxs ns -> IO (DataFrame t ('[] :: [Nat]))
+ Numeric.DataFrame.IO: readDataFrame :: forall (t :: Type) ns. PrimBytes (DataFrame t ('[] :: KindOf ns)) => IODataFrame t ns -> Idxs ns -> IO (DataFrame t ('[] :: KindOf ns))
- Numeric.DataFrame.IO: readDataFrameOff :: forall (t :: Type) (ns :: [Nat]). PrimBytes t => IODataFrame t ns -> Int -> IO (DataFrame t ('[] :: [Nat]))
+ Numeric.DataFrame.IO: readDataFrameOff :: forall (t :: Type) ns. PrimBytes (DataFrame t ('[] :: KindOf ns)) => IODataFrame t ns -> Int -> IO (DataFrame t ('[] :: KindOf ns))
- Numeric.DataFrame.IO: thawDataFrame :: forall (t :: Type) (ns :: [Nat]). (Dimensions ns, PrimBytes (DataFrame t ns)) => DataFrame t ns -> IO (IODataFrame t ns)
+ Numeric.DataFrame.IO: thawDataFrame :: forall (t :: Type) ns. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> IO (IODataFrame t ns)
- Numeric.DataFrame.IO: thawPinDataFrame :: forall (t :: Type) (ns :: [Nat]). (Dimensions ns, PrimBytes (DataFrame t ns)) => DataFrame t ns -> IO (IODataFrame t ns)
+ Numeric.DataFrame.IO: thawPinDataFrame :: forall (t :: Type) ns. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> IO (IODataFrame t ns)
- Numeric.DataFrame.IO: unsafeFreezeDataFrame :: forall (t :: Type) (ns :: [Nat]). PrimArray t (DataFrame t ns) => IODataFrame t ns -> IO (DataFrame t ns)
+ Numeric.DataFrame.IO: unsafeFreezeDataFrame :: forall (t :: Type) ns. PrimArray t (DataFrame t ns) => IODataFrame t ns -> IO (DataFrame t ns)
- Numeric.DataFrame.IO: unsafeThawDataFrame :: forall (t :: Type) (ns :: [Nat]). (Dimensions ns, PrimBytes (DataFrame t ns), PrimBytes t) => DataFrame t ns -> IO (IODataFrame t ns)
+ Numeric.DataFrame.IO: unsafeThawDataFrame :: forall (t :: Type) ns. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> IO (IODataFrame t ns)
- Numeric.DataFrame.IO: withDataFramePtr :: forall (k :: Type) (t :: Type) (ns :: [k]) (r :: Type). (PrimBytes t, KnownDimKind k) => IODataFrame t ns -> (Ptr t -> IO r) -> IO r
+ Numeric.DataFrame.IO: withDataFramePtr :: forall (t :: Type) ns (r :: Type). PrimBytes t => IODataFrame t ns -> (Ptr t -> IO r) -> IO r
- Numeric.DataFrame.IO: writeDataFrame :: forall t (ns :: [Nat]). PrimBytes t => IODataFrame t ns -> Idxs ns -> DataFrame t ('[] :: [Nat]) -> IO ()
+ Numeric.DataFrame.IO: writeDataFrame :: forall (t :: Type) ns. PrimBytes (DataFrame t ('[] :: KindOf ns)) => IODataFrame t ns -> Idxs ns -> DataFrame t ('[] :: KindOf ns) -> IO ()
- Numeric.DataFrame.IO: writeDataFrameOff :: forall (t :: Type) (ns :: [Nat]). PrimBytes t => IODataFrame t ns -> Int -> DataFrame t ('[] :: [Nat]) -> IO ()
+ Numeric.DataFrame.IO: writeDataFrameOff :: forall (t :: Type) ns. PrimBytes (DataFrame t ('[] :: KindOf ns)) => IODataFrame t ns -> Int -> DataFrame t ('[] :: KindOf ns) -> IO ()
- Numeric.DataFrame.Internal.Backend: inferBounded :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Bounded t, KnownBackend t ds b) => Dict (Bounded (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferBounded :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Bounded t, KnownBackend t ds b) => Dict (Bounded (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferEq :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Eq t, KnownBackend t ds b) => Dict (Eq (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferEq :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Eq t, KnownBackend t ds b) => Dict (Eq (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferFloating :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Floating t, KnownBackend t ds b) => Dict (Floating (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferFloating :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Floating t, KnownBackend t ds b) => Dict (Floating (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferFractional :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Fractional t, KnownBackend t ds b) => Dict (Fractional (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferFractional :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Fractional t, KnownBackend t ds b) => Dict (Fractional (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferNum :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Num t, KnownBackend t ds b) => Dict (Num (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferNum :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Num t, KnownBackend t ds b) => Dict (Num (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferOrd :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Ord t, KnownBackend t ds b) => Dict (Ord (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferOrd :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Ord t, KnownBackend t ds b) => Dict (Ord (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferPONonTransitive :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Ord t, KnownBackend t ds b) => Dict (Ord (ProductOrd (Backend I t ds b)))
+ Numeric.DataFrame.Internal.Backend: inferPONonTransitive :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Ord t, KnownBackend t ds b) => Dict (Ord (ProductOrd (Backend i t ds b)))
- Numeric.DataFrame.Internal.Backend: inferPOPartial :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Ord t, KnownBackend t ds b) => Dict (Ord (ProductOrd (Backend I t ds b)))
+ Numeric.DataFrame.Internal.Backend: inferPOPartial :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Ord t, KnownBackend t ds b) => Dict (Ord (ProductOrd (Backend i t ds b)))
- Numeric.DataFrame.Internal.Backend: inferPrimArray :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (PrimBytes t, KnownBackend t ds b) => Dict (PrimArray t (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferPrimArray :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (PrimBytes t, KnownBackend t ds b) => Dict (PrimArray t (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferPrimBytes :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (PrimBytes t, Dimensions ds, KnownBackend t ds b) => Dict (PrimBytes (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferPrimBytes :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (PrimBytes t, Dimensions ds, KnownBackend t ds b) => Dict (PrimBytes (Backend i t ds b))
- Numeric.DataFrame.Internal.Backend: inferPrimElem :: forall (t :: Type) (d :: Nat) (ds :: [Nat]). KnownBackend t (d : ds) => DFBackend t (d : ds) -> Dict (PrimBytes t)
+ Numeric.DataFrame.Internal.Backend: inferPrimElem :: forall (t :: Type) (d :: Nat) (ds :: [Nat]) (i :: Type). KnownBackend t (d : ds) => Backend i t (d : ds) (BackendFamily t (d : ds)) -> Dict (PrimBytes t)
- Numeric.DataFrame.Internal.Backend: inferProductOrder :: forall (t :: Type) (ds :: [Nat]) (b :: Type). (Ord t, KnownBackend t ds b) => Dict (ProductOrder (Backend I t ds b))
+ Numeric.DataFrame.Internal.Backend: inferProductOrder :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type). (Ord t, KnownBackend t ds b) => Dict (ProductOrder (Backend i t ds b))
- Numeric.DataFrame.Internal.Mutable: copyDataFrame# :: forall (t :: Type) (b :: Nat) (bi :: Nat) (bd :: Nat) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (b ~ ((bi + bd) - 1), PrimBytes t, PrimBytes (DataFrame t (bd :+ bs)), ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: copyDataFrame# :: forall (t :: Type) (k :: Type) (b :: k) (bi :: k) (bd :: k) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs, PrimArray t (DataFrame t (bd :+ bs)), ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
- Numeric.DataFrame.Internal.Mutable: copyDataFrame'# :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (PrimBytes t, PrimBytes (DataFrame t bs), ConcatList as bs asbs) => Idxs as -> DataFrame t bs -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: copyDataFrame'# :: forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (ExactDims bs, PrimArray t (DataFrame t bs), ConcatList as bs asbs) => Idxs as -> DataFrame t bs -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
- Numeric.DataFrame.Internal.Mutable: copyMDataFrame# :: forall (t :: Type) (b :: Nat) (bi :: Nat) (bd :: Nat) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (b ~ ((bi + bd) - 1), PrimBytes t, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> MDataFrame s t (bd :+ bs) -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: copyMDataFrame# :: forall (t :: Type) (k :: Type) (b :: k) (bi :: k) (bd :: k) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (SubFrameIndexCtx b bi bd, ExactDims bs, PrimBytes t, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> MDataFrame s t (bd :+ bs) -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
- Numeric.DataFrame.Internal.Mutable: copyMDataFrame'# :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (PrimBytes t, ConcatList as bs asbs) => Idxs as -> MDataFrame s t bs -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: copyMDataFrame'# :: forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s. (ExactDims bs, PrimBytes t, ConcatList as bs asbs) => Idxs as -> MDataFrame s t bs -> MDataFrame s t asbs -> State# s -> (# State# s, () #)
- Numeric.DataFrame.Internal.Mutable: data MDataFrame s t (ns :: [Nat])
+ Numeric.DataFrame.Internal.Mutable: data MDataFrame s t (ns :: [k])
- Numeric.DataFrame.Internal.Mutable: freezeDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. PrimArray t (DataFrame t ns) => MDataFrame s t ns -> State# s -> (# State# s, DataFrame t ns #)
+ Numeric.DataFrame.Internal.Mutable: freezeDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. PrimArray t (DataFrame t ns) => MDataFrame s t ns -> State# s -> (# State# s, DataFrame t ns #)
- Numeric.DataFrame.Internal.Mutable: isDataFramePinned# :: forall (t :: Type) (ns :: [Nat]) s. MDataFrame s t ns -> Bool
+ Numeric.DataFrame.Internal.Mutable: isDataFramePinned# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. MDataFrame s t ns -> Bool
- Numeric.DataFrame.Internal.Mutable: newDataFrame# :: forall t (ns :: [Nat]) s. (PrimBytes t, Dimensions ns) => State# s -> (# State# s, MDataFrame s t ns #)
+ Numeric.DataFrame.Internal.Mutable: newDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. (PrimBytes t, Dimensions ns) => State# s -> (# State# s, MDataFrame s t ns #)
- Numeric.DataFrame.Internal.Mutable: newPinnedDataFrame# :: forall t (ns :: [Nat]) s. (PrimBytes t, Dimensions ns) => State# s -> (# State# s, MDataFrame s t ns #)
+ Numeric.DataFrame.Internal.Mutable: newPinnedDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. (PrimBytes t, Dimensions ns) => State# s -> (# State# s, MDataFrame s t ns #)
- Numeric.DataFrame.Internal.Mutable: readDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => MDataFrame s t ns -> Idxs ns -> State# s -> (# State# s, t #)
+ Numeric.DataFrame.Internal.Mutable: readDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. PrimBytes (DataFrame t ('[] :: [k])) => MDataFrame s t ns -> Idxs ns -> State# s -> (# State# s, DataFrame t ('[] :: [k]) #)
- Numeric.DataFrame.Internal.Mutable: readDataFrameOff# :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => MDataFrame s t ns -> Int# -> State# s -> (# State# s, t #)
+ Numeric.DataFrame.Internal.Mutable: readDataFrameOff# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. PrimBytes (DataFrame t ('[] :: [k])) => MDataFrame s t ns -> Int -> State# s -> (# State# s, DataFrame t ('[] :: [k]) #)
- Numeric.DataFrame.Internal.Mutable: thawDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. (Dimensions ns, PrimBytes (DataFrame t ns)) => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)
+ Numeric.DataFrame.Internal.Mutable: thawDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)
- Numeric.DataFrame.Internal.Mutable: thawPinDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. (Dimensions ns, PrimBytes (DataFrame t ns)) => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)
+ Numeric.DataFrame.Internal.Mutable: thawPinDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)
- Numeric.DataFrame.Internal.Mutable: unsafeFreezeDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. PrimArray t (DataFrame t ns) => MDataFrame s t ns -> State# s -> (# State# s, DataFrame t ns #)
+ Numeric.DataFrame.Internal.Mutable: unsafeFreezeDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. PrimArray t (DataFrame t ns) => MDataFrame s t ns -> State# s -> (# State# s, DataFrame t ns #)
- Numeric.DataFrame.Internal.Mutable: unsafeThawDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. (Dimensions ns, PrimBytes (DataFrame t ns), PrimBytes t) => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)
+ Numeric.DataFrame.Internal.Mutable: unsafeThawDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)
- Numeric.DataFrame.Internal.Mutable: withDataFramePtr# :: forall (t :: Type) (ns :: [Nat]) (r :: Type). PrimBytes t => MDataFrame RealWorld t ns -> (Addr# -> State# RealWorld -> (# State# RealWorld, r #)) -> State# RealWorld -> (# State# RealWorld, r #)
+ Numeric.DataFrame.Internal.Mutable: withDataFramePtr# :: forall (t :: Type) (k :: Type) (ns :: [k]) (r :: Type). PrimBytes t => MDataFrame RealWorld t ns -> (Addr# -> State# RealWorld -> (# State# RealWorld, r #)) -> State# RealWorld -> (# State# RealWorld, r #)
- Numeric.DataFrame.Internal.Mutable: writeDataFrame# :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => MDataFrame s t ns -> Idxs ns -> t -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: writeDataFrame# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. PrimBytes (DataFrame t ('[] :: [k])) => MDataFrame s t ns -> Idxs ns -> DataFrame t ('[] :: [k]) -> State# s -> (# State# s, () #)
- Numeric.DataFrame.Internal.Mutable: writeDataFrameOff# :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => MDataFrame s t ns -> Int# -> t -> State# s -> (# State# s, () #)
+ Numeric.DataFrame.Internal.Mutable: writeDataFrameOff# :: forall (t :: Type) (k :: Type) (ns :: [k]) s. PrimBytes (DataFrame t ('[] :: [k])) => MDataFrame s t ns -> Int -> DataFrame t ('[] :: [k]) -> State# s -> (# State# s, () #)
- Numeric.DataFrame.Internal.PrimArray: broadcast :: PrimArray t a => t -> a
+ Numeric.DataFrame.Internal.PrimArray: broadcast :: forall (t :: Type) (a :: Type). PrimArray t a => t -> a
- Numeric.DataFrame.Internal.PrimArray: fromElems :: PrimArray t a => CumulDims -> Int# -> ByteArray# -> a
+ Numeric.DataFrame.Internal.PrimArray: fromElems :: forall (t :: Type) (a :: Type). PrimArray t a => CumulDims -> Int# -> ByteArray# -> a
- Numeric.DataFrame.ST: copyDataFrame :: forall (t :: Type) (b :: Nat) (bi :: Nat) (bd :: Nat) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (b ~ ((bi + bd) - 1), PrimBytes t, PrimBytes (DataFrame t (bd :+ bs)), ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> STDataFrame s t asbs -> ST s ()
+ Numeric.DataFrame.ST: copyDataFrame :: forall (t :: Type) b bi bd as bs asbs s. (SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs, PrimArray t (DataFrame t (bd :+ bs)), ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> STDataFrame s t asbs -> ST s ()
- Numeric.DataFrame.ST: copyDataFrame' :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (PrimBytes t, PrimBytes (DataFrame t bs), ConcatList as bs asbs) => Idxs as -> DataFrame t bs -> STDataFrame s t asbs -> ST s ()
+ Numeric.DataFrame.ST: copyDataFrame' :: forall (t :: Type) as bs asbs s. (ExactDims bs, PrimArray t (DataFrame t bs), ConcatList as bs asbs) => Idxs as -> DataFrame t bs -> STDataFrame s t asbs -> ST s ()
- Numeric.DataFrame.ST: copyMutableDataFrame :: forall (t :: Type) (b :: Nat) (bi :: Nat) (bd :: Nat) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (b ~ ((bi + bd) - 1), PrimBytes t, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> STDataFrame s t (bd :+ bs) -> STDataFrame s t asbs -> ST s ()
+ Numeric.DataFrame.ST: copyMutableDataFrame :: forall (t :: Type) b bi bd as bs asbs s. (SubFrameIndexCtx b bi bd, ExactDims bs, PrimBytes t, ConcatList as (b :+ bs) asbs) => Idxs (as +: bi) -> STDataFrame s t (bd :+ bs) -> STDataFrame s t asbs -> ST s ()
- Numeric.DataFrame.ST: copyMutableDataFrame' :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s. (PrimBytes t, ConcatList as bs asbs) => Idxs as -> STDataFrame s t bs -> STDataFrame s t asbs -> ST s ()
+ Numeric.DataFrame.ST: copyMutableDataFrame' :: forall (t :: Type) as bs asbs s. (ExactDims bs, PrimBytes t, ConcatList as bs asbs) => Idxs as -> STDataFrame s t bs -> STDataFrame s t asbs -> ST s ()
- Numeric.DataFrame.ST: freezeDataFrame :: forall (t :: Type) (ns :: [Nat]) s. PrimArray t (DataFrame t ns) => STDataFrame s t ns -> ST s (DataFrame t ns)
+ Numeric.DataFrame.ST: freezeDataFrame :: forall (t :: Type) ns s. PrimArray t (DataFrame t ns) => STDataFrame s t ns -> ST s (DataFrame t ns)
- Numeric.DataFrame.ST: isDataFramePinned :: forall (k :: Type) (t :: Type) (ns :: [k]) s. KnownDimKind k => STDataFrame s t ns -> Bool
+ Numeric.DataFrame.ST: isDataFramePinned :: forall (t :: Type) ns s. STDataFrame s t ns -> Bool
- Numeric.DataFrame.ST: newDataFrame :: forall t (ns :: [Nat]) s. (PrimBytes t, Dimensions ns) => ST s (STDataFrame s t ns)
+ Numeric.DataFrame.ST: newDataFrame :: forall (t :: Type) ns s. (PrimBytes t, Dimensions ns) => ST s (STDataFrame s t ns)
- Numeric.DataFrame.ST: newPinnedDataFrame :: forall t (ns :: [Nat]) s. (PrimBytes t, Dimensions ns) => ST s (STDataFrame s t ns)
+ Numeric.DataFrame.ST: newPinnedDataFrame :: forall (t :: Type) ns s. (PrimBytes t, Dimensions ns) => ST s (STDataFrame s t ns)
- Numeric.DataFrame.ST: readDataFrame :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => STDataFrame s t ns -> Idxs ns -> ST s (DataFrame t ('[] :: [Nat]))
+ Numeric.DataFrame.ST: readDataFrame :: forall (t :: Type) ns s. PrimBytes (DataFrame t ('[] :: KindOf ns)) => STDataFrame s t ns -> Idxs ns -> ST s (DataFrame t ('[] :: KindOf ns))
- Numeric.DataFrame.ST: readDataFrameOff :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => STDataFrame s t ns -> Int -> ST s (DataFrame t ('[] :: [Nat]))
+ Numeric.DataFrame.ST: readDataFrameOff :: forall (t :: Type) ns s. PrimBytes (DataFrame t ('[] :: KindOf ns)) => STDataFrame s t ns -> Int -> ST s (DataFrame t ('[] :: KindOf ns))
- Numeric.DataFrame.ST: thawDataFrame :: forall (t :: Type) (ns :: [Nat]) s. (Dimensions ns, PrimBytes (DataFrame t ns)) => DataFrame t ns -> ST s (STDataFrame s t ns)
+ Numeric.DataFrame.ST: thawDataFrame :: forall (t :: Type) ns s. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> ST s (STDataFrame s t ns)
- Numeric.DataFrame.ST: thawPinDataFrame :: forall (t :: Type) (ns :: [Nat]) s. (Dimensions ns, PrimBytes (DataFrame t ns)) => DataFrame t ns -> ST s (STDataFrame s t ns)
+ Numeric.DataFrame.ST: thawPinDataFrame :: forall (t :: Type) ns s. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> ST s (STDataFrame s t ns)
- Numeric.DataFrame.ST: unsafeFreezeDataFrame :: forall (t :: Type) (ns :: [Nat]) s. PrimArray t (DataFrame t ns) => STDataFrame s t ns -> ST s (DataFrame t ns)
+ Numeric.DataFrame.ST: unsafeFreezeDataFrame :: forall (t :: Type) ns s. PrimArray t (DataFrame t ns) => STDataFrame s t ns -> ST s (DataFrame t ns)
- Numeric.DataFrame.ST: unsafeThawDataFrame :: forall (t :: Type) (ns :: [Nat]) s. (Dimensions ns, PrimBytes (DataFrame t ns), PrimBytes t) => DataFrame t ns -> ST s (STDataFrame s t ns)
+ Numeric.DataFrame.ST: unsafeThawDataFrame :: forall (t :: Type) ns s. (Dimensions ns, PrimArray t (DataFrame t ns)) => DataFrame t ns -> ST s (STDataFrame s t ns)
- Numeric.DataFrame.ST: writeDataFrame :: forall t (ns :: [Nat]) s. PrimBytes t => STDataFrame s t ns -> Idxs ns -> DataFrame t ('[] :: [Nat]) -> ST s ()
+ Numeric.DataFrame.ST: writeDataFrame :: forall (t :: Type) ns s. PrimBytes (DataFrame t ('[] :: KindOf ns)) => STDataFrame s t ns -> Idxs ns -> DataFrame t ('[] :: KindOf ns) -> ST s ()
- Numeric.DataFrame.ST: writeDataFrameOff :: forall (t :: Type) (ns :: [Nat]) s. PrimBytes t => STDataFrame s t ns -> Int -> DataFrame t ('[] :: [Nat]) -> ST s ()
+ Numeric.DataFrame.ST: writeDataFrameOff :: forall (t :: Type) ns s. PrimBytes (DataFrame t ('[] :: KindOf ns)) => STDataFrame s t ns -> Int -> DataFrame t ('[] :: KindOf ns) -> ST s ()
- Numeric.DataFrame.SubSpace: class (ConcatList as bs asbs, Dimensions as, Dimensions bs, Dimensions asbs, PrimArray t (DataFrame t bs), PrimArray t (DataFrame t asbs), PrimBytes (DataFrame t bs), PrimBytes (DataFrame t asbs)) => SubSpace (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) | asbs as -> bs, asbs bs -> as, as bs -> asbs
+ Numeric.DataFrame.SubSpace: class (ConcatList as bs asbs, SubSpaceCtx t as bs asbs, PrimBytes t, KnownDimKind k) => SubSpace (t :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) | asbs as -> bs, asbs bs -> as, as bs -> asbs where {
- Numeric.DataFrame.SubSpace: element :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) f. (SubSpace t as bs asbs, Applicative f) => Idxs as -> (DataFrame t bs -> f (DataFrame t bs)) -> DataFrame t asbs -> f (DataFrame t asbs)
+ Numeric.DataFrame.SubSpace: element :: forall t as bs asbs f. (SubSpace t as bs asbs, Applicative f) => Idxs as -> (DataFrame t bs -> f (DataFrame t bs)) -> DataFrame t asbs -> f (DataFrame t asbs)
- Numeric.DataFrame.SubSpace: elementWise :: forall (s :: Type) (bs' :: [Nat]) (asbs' :: [Nat]) (f :: Type -> Type). (SubSpace t as bs asbs, Applicative f, SubSpace s as bs' asbs') => (DataFrame s bs' -> f (DataFrame t bs)) -> DataFrame s asbs' -> f (DataFrame t asbs)
+ Numeric.DataFrame.SubSpace: elementWise :: forall t as bs asbs s bs' asbs' f. (SubSpace t as bs asbs, SubSpace s as bs' asbs', Applicative f) => (DataFrame s bs' -> f (DataFrame t bs)) -> DataFrame s asbs' -> f (DataFrame t asbs)
- Numeric.DataFrame.SubSpace: ewfoldMap :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) m. (Monoid m, SubSpace t as bs asbs) => (DataFrame t bs -> m) -> DataFrame t asbs -> m
+ Numeric.DataFrame.SubSpace: ewfoldMap :: forall t as bs asbs m. (SubSpace t as bs asbs, Monoid m) => (DataFrame t bs -> m) -> DataFrame t asbs -> m
- Numeric.DataFrame.SubSpace: ewfoldl :: SubSpace t as bs asbs => (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: ewfoldl :: forall t as bs asbs b. SubSpace t as bs asbs => (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b
- Numeric.DataFrame.SubSpace: ewfoldr :: SubSpace t as bs asbs => (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: ewfoldr :: forall t as bs asbs b. SubSpace t as bs asbs => (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b
- Numeric.DataFrame.SubSpace: ewgen :: SubSpace t as bs asbs => DataFrame t bs -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: ewgen :: forall t as bs asbs. (SubSpace t as bs asbs, Dimensions as) => DataFrame t bs -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: ewmap :: forall s (bs' :: [Nat]) (asbs' :: [Nat]). (SubSpace t as bs asbs, SubSpace s as bs' asbs') => (DataFrame s bs' -> DataFrame t bs) -> DataFrame s asbs' -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: ewmap :: forall t as bs asbs s bs' asbs'. (SubSpace t as bs asbs, SubSpace s as bs' asbs') => (DataFrame s bs' -> DataFrame t bs) -> DataFrame s asbs' -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: ewzip :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s (bs' :: [Nat]) (asbs' :: [Nat]) r (bs'' :: [Nat]) (asbs'' :: [Nat]). (SubSpace t as bs asbs, SubSpace s as bs' asbs', SubSpace r as bs'' asbs'') => (DataFrame t bs -> DataFrame s bs' -> DataFrame r bs'') -> DataFrame t asbs -> DataFrame s asbs' -> DataFrame r asbs''
+ Numeric.DataFrame.SubSpace: ewzip :: forall t as bs asbs l bsL asbsL r bsR asbsR. (SubSpace t as bs asbs, SubSpace l as bsL asbsL, SubSpace r as bsR asbsR) => (DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs) -> DataFrame l asbsL -> DataFrame r asbsR -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: index :: SubSpace t as bs asbs => Idxs as -> DataFrame t asbs -> DataFrame t bs
+ Numeric.DataFrame.SubSpace: index :: forall t as bs asbs. SubSpace t as bs asbs => Idxs as -> DataFrame t asbs -> DataFrame t bs
- Numeric.DataFrame.SubSpace: indexWise :: forall (s :: Type) (bs' :: [Nat]) (asbs' :: [Nat]) (f :: Type -> Type). (SubSpace t as bs asbs, Applicative f, SubSpace s as bs' asbs') => (Idxs as -> DataFrame s bs' -> f (DataFrame t bs)) -> DataFrame s asbs' -> f (DataFrame t asbs)
+ Numeric.DataFrame.SubSpace: indexWise :: forall t as bs asbs s bs' asbs' f. (SubSpace t as bs asbs, SubSpace s as bs' asbs', Applicative f) => (Idxs as -> DataFrame s bs' -> f (DataFrame t bs)) -> DataFrame s asbs' -> f (DataFrame t asbs)
- Numeric.DataFrame.SubSpace: infixl 4 !
+ Numeric.DataFrame.SubSpace: infixl 4 .!
- Numeric.DataFrame.SubSpace: iwfoldMap :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) m. (Monoid m, SubSpace t as bs asbs) => (Idxs as -> DataFrame t bs -> m) -> DataFrame t asbs -> m
+ Numeric.DataFrame.SubSpace: iwfoldMap :: forall t as bs asbs m. (SubSpace t as bs asbs, Monoid m) => (Idxs as -> DataFrame t bs -> m) -> DataFrame t asbs -> m
- Numeric.DataFrame.SubSpace: iwfoldl :: SubSpace t as bs asbs => (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: iwfoldl :: forall t as bs asbs b. SubSpace t as bs asbs => (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b
- Numeric.DataFrame.SubSpace: iwfoldr :: SubSpace t as bs asbs => (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b
+ Numeric.DataFrame.SubSpace: iwfoldr :: forall t as bs asbs b. SubSpace t as bs asbs => (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b
- Numeric.DataFrame.SubSpace: iwgen :: SubSpace t as bs asbs => (Idxs as -> DataFrame t bs) -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: iwgen :: forall t as bs asbs. (SubSpace t as bs asbs, Dimensions as) => (Idxs as -> DataFrame t bs) -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: iwmap :: forall s (bs' :: [Nat]) (asbs' :: [Nat]). (SubSpace t as bs asbs, SubSpace s as bs' asbs') => (Idxs as -> DataFrame s bs' -> DataFrame t bs) -> DataFrame s asbs' -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: iwmap :: forall t as bs asbs s bs' asbs'. (SubSpace t as bs asbs, SubSpace s as bs' asbs') => (Idxs as -> DataFrame s bs' -> DataFrame t bs) -> DataFrame s asbs' -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: iwzip :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s (bs' :: [Nat]) (asbs' :: [Nat]) r (bs'' :: [Nat]) (asbs'' :: [Nat]). (SubSpace t as bs asbs, SubSpace s as bs' asbs', SubSpace r as bs'' asbs'') => (Idxs as -> DataFrame t bs -> DataFrame s bs' -> DataFrame r bs'') -> DataFrame t asbs -> DataFrame s asbs' -> DataFrame r asbs''
+ Numeric.DataFrame.SubSpace: iwzip :: forall t as bs asbs l bsL asbsL r bsR asbsR. (SubSpace t as bs asbs, SubSpace l as bsL asbsL, SubSpace r as bsR asbsR) => (Idxs as -> DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs) -> DataFrame l asbsL -> DataFrame r asbsR -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: slice :: forall (bi :: Nat) (bd :: Nat) (b' :: Nat) (bs' :: [Nat]). (SubSpace t as bs asbs, b' ~ ((bi + bd) - 1), bs ~ (b' :+ bs'), KnownDim bd) => Idxs (as +: bi) -> DataFrame t asbs -> DataFrame t (bd :+ bs')
+ Numeric.DataFrame.SubSpace: slice :: forall (t :: Type) b bi bd as bs asbs. (KnownDimKind (KindOfEl asbs), CanSlice t asbs, SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs, PrimArray t (DataFrame t (bd :+ bs))) => Idxs (as +: bi) -> DataFrame t asbs -> DataFrame t (bd :+ bs)
- Numeric.DataFrame.SubSpace: update :: SubSpace t as bs asbs => Idxs as -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: update :: forall t as bs asbs. SubSpace t as bs asbs => Idxs as -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs
- Numeric.DataFrame.SubSpace: updateSlice :: forall (bi :: Nat) (bd :: Nat) (b' :: Nat) (bs' :: [Nat]). (SubSpace t as bs asbs, b' ~ ((bi + bd) - 1), bs ~ (b' :+ bs'), KnownDim bd) => Idxs (as +: bi) -> DataFrame t (bd :+ bs') -> DataFrame t asbs -> DataFrame t asbs
+ Numeric.DataFrame.SubSpace: updateSlice :: forall (t :: Type) b bi bd as bs asbs. (KnownDimKind (KindOfEl asbs), CanSlice t asbs, SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs, ConcatList as (b :+ bs) asbs, PrimArray t (DataFrame t (bd :+ bs))) => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> DataFrame t asbs -> DataFrame t asbs
- Numeric.DataFrame.Type: class InferKnownBackend (t :: k) ds
+ Numeric.DataFrame.Type: class InferKnownBackend t (ds :: [Nat])
- Numeric.DataFrame.Type: constrainDF :: forall (ds :: [XNat]) (ys :: [XNat]) (l :: Type) (ts :: l). (BoundedDims ds, All KnownXNatType ds) => DataFrame ts ys -> Maybe (DataFrame ts ds)
+ Numeric.DataFrame.Type: constrainDF :: forall (ds :: [XNat]) (ys :: [XNat]) t. (BoundedDims ds, All KnownDimType ds) => DataFrame t ys -> Maybe (DataFrame t ds)
- Numeric.DataFrame.Type: fromList :: forall (t :: Type) (ds :: [Nat]). (PrimBytes t, Dimensions ds) => [DataFrame t ds] -> DataFrame t (XN 0 : AsXDims ds)
+ Numeric.DataFrame.Type: fromList :: forall (t :: Type) (ds :: [Nat]) (xds :: [XNat]). (PrimBytes t, Dimensions ds, xds ~ Map N ds, ds ~ UnMap N xds) => [DataFrame t ds] -> DataFrame t (XN 0 : xds)
- Numeric.DataFrame.Type: pattern Dn :: forall (xn :: XNat). KnownXNatType xn => forall (n :: Nat). (KnownDim n, xn ~ N n) => Dim n -> Dim xn
+ Numeric.DataFrame.Type: pattern Dn :: forall (d :: XNat). KnownDimType d => forall (n :: Nat). (KindOf d ~ XNat, d ~ N n) => Dim n -> Dim d
- Numeric.DataFrame.Type: pattern KnownDims :: forall (ds :: [Nat]). () => (All KnownDim ds, All (BoundedDim :: Nat -> Constraint) ds, RepresentableList ds, Dimensions ds) => Dims ds
+ Numeric.DataFrame.Type: pattern XDims :: forall (ds :: [XNat]). KnownDimKind (KindOfEl ds) => forall (ns :: [Nat]). (KindOfEl ds ~ XNat, FixedDims ds ns) => Dims ns -> Dims ds
- Numeric.DataFrame.Type: type DFBackend t (ds :: [Nat]) = Backend I t ds BackendFamily t ds
+ Numeric.DataFrame.Type: type DFBackend (t :: Type) (ds :: [Nat]) = Backend I t ds (BackendFamily t ds)
- Numeric.DataFrame.Type: type Dims (xs :: [k]) = TypedList (Dim :: k -> Type) xs
+ Numeric.DataFrame.Type: type Dims = TypedList (Dim :: k -> Type)
- Numeric.DataFrame.Type: type Idxs (xs :: [k]) = TypedList (Idx :: k -> Type) xs
+ Numeric.DataFrame.Type: type Idxs = TypedList (Idx :: k -> Type)
- Numeric.DataFrame.Type: type N (n :: Nat) = N n
+ Numeric.DataFrame.Type: type N = N
- Numeric.DataFrame.Type: type XN (n :: Nat) = XN n
+ Numeric.DataFrame.Type: type XN = XN
- Numeric.Quaternion: class Quaternion t where {
+ Numeric.Quaternion: class (Floating (Quater t), Floating t, Ord t, PrimBytes t, KnownBackend t '[3], KnownBackend t '[4], KnownBackend t '[3, 3], KnownBackend t '[4, 4]) => Quaternion t where {
- Numeric.Scalar: fromScalar :: SubSpace t ds '[] ds => Scalar t -> DataFrame t ds
+ Numeric.Scalar: fromScalar :: PrimArray t (DataFrame t ds) => Scalar t -> DataFrame t (ds :: [Nat])
- Numeric.Scalar: scalar :: t -> Scalar t
+ Numeric.Scalar: scalar :: t -> DataFrame t ('[] :: [Nat])
- Numeric.Scalar: unScalar :: Scalar t -> t
+ Numeric.Scalar: unScalar :: DataFrame t ('[] :: [Nat]) -> t
- Numeric.Vector: (.*.) :: (Num t, Num (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t n -> Vector t n -> Vector t n
+ Numeric.Vector: (.*.) :: (Num t, Num (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t (n :: Nat) -> Vector t n -> Vector t n
- Numeric.Vector: (·) :: (Num t, Num (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t n -> Vector t n -> Scalar t
+ Numeric.Vector: (·) :: (Num t, Num (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t (n :: Nat) -> Vector t n -> Scalar t
- Numeric.Vector: cross :: (Num t, SubSpace t '[3] '[] '[3]) => Vector t 3 -> Vector t 3 -> Vector t 3
+ Numeric.Vector: cross :: (Num t, PrimBytes t) => Vector t 3 -> Vector t 3 -> Vector t 3
- Numeric.Vector: det2 :: (Num t, SubSpace t '[2] '[] '[2]) => Vector t 2 -> Vector t 2 -> Scalar t
+ Numeric.Vector: det2 :: (Num t, PrimBytes t) => Vector t 2 -> Vector t 2 -> Scalar t
- Numeric.Vector: dot :: (Num t, Num (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t n -> Vector t n -> Scalar t
+ Numeric.Vector: dot :: (Num t, Num (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t (n :: Nat) -> Vector t n -> Scalar t
- Numeric.Vector: normL1 :: (Num t, SubSpace t '[n] '[] '[n]) => Vector t n -> Scalar t
+ Numeric.Vector: normL1 :: (Num (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n]) => Vector t (n :: k) -> DataFrame t ('[] :: [k])
- Numeric.Vector: normL2 :: (Floating t, SubSpace t '[n] '[] '[n]) => Vector t n -> Scalar t
+ Numeric.Vector: normL2 :: (Floating (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n]) => Vector t (n :: k) -> DataFrame t ('[] :: [k])
- Numeric.Vector: normLNInf :: (Ord t, Num t, SubSpace t '[n] '[] '[n]) => Vector t n -> Scalar t
+ Numeric.Vector: normLNInf :: (Ord (DataFrame t ('[] :: [k])), Num (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n]) => Vector t (n :: k) -> DataFrame t ('[] :: [k])
- Numeric.Vector: normLP :: (Floating t, SubSpace t '[n] '[] '[n]) => Int -> Vector t n -> Scalar t
+ Numeric.Vector: normLP :: (Floating (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n]) => Int -> Vector t (n :: k) -> DataFrame t ('[] :: [k])
- Numeric.Vector: normLPInf :: (Ord t, Num t, SubSpace t '[n] '[] '[n]) => Vector t n -> Scalar t
+ Numeric.Vector: normLPInf :: (Ord (DataFrame t ('[] :: [k])), Num (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n]) => Vector t (n :: k) -> DataFrame t ('[] :: [k])
- Numeric.Vector: normalized :: (Floating t, Fractional (Vector t n), SubSpace t '[n] '[] '[n]) => Vector t n -> Vector t n
+ Numeric.Vector: normalized :: forall (k :: Type) (t :: Type) (n :: k). (Floating (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n]) => Vector t n -> Vector t n
Files
- Setup.hs +31/−1
- bench/subspacefolds.hs +10/−10
- easytensor.cabal +31/−12
- src-base/Numeric/DataFrame/Internal/Backend/Family.hs +28/−22
- src-base/Numeric/DataFrame/Internal/Backend/Family/ArrayBase.hs +119/−140
- src-base/Numeric/DataFrame/Internal/Backend/Family/DoubleX2.hs +6/−8
- src-base/Numeric/DataFrame/Internal/Backend/Family/DoubleX3.hs +6/−7
- src-base/Numeric/DataFrame/Internal/Backend/Family/DoubleX4.hs +6/−7
- src-base/Numeric/DataFrame/Internal/Backend/Family/FloatX2.hs +6/−7
- src-base/Numeric/DataFrame/Internal/Backend/Family/FloatX3.hs +6/−7
- src-base/Numeric/DataFrame/Internal/Backend/Family/FloatX4.hs +11/−13
- src-base/Numeric/DataFrame/Internal/Backend/Family/ScalarBase.hs +11/−10
- src/Numeric/Basics.hs +238/−0
- src/Numeric/DataFrame.hs +6/−0
- src/Numeric/DataFrame/Contraction.hs +14/−13
- src/Numeric/DataFrame/IO.hs +276/−138
- src/Numeric/DataFrame/Internal/Backend.hs +63/−37
- src/Numeric/DataFrame/Internal/Backend.hs-boot +42/−46
- src/Numeric/DataFrame/Internal/BackendI.hs +38/−11
- src/Numeric/DataFrame/Internal/BackendI.hs-boot +0/−9
- src/Numeric/DataFrame/Internal/Mutable.hs +401/−153
- src/Numeric/DataFrame/Internal/PrimArray.hs +218/−24
- src/Numeric/DataFrame/ST.hs +271/−126
- src/Numeric/DataFrame/SubSpace.hs +1265/−503
- src/Numeric/DataFrame/Type.hs +503/−88
- src/Numeric/Matrix.hs +2/−4
- src/Numeric/Matrix/Bidiagonal.hs +123/−0
- src/Numeric/Matrix/Internal.hs +0/−243
- src/Numeric/Matrix/Internal/Double.hs +199/−0
- src/Numeric/Matrix/Internal/Float.hs +199/−0
- src/Numeric/Matrix/Internal/Mat44d.hs +0/−186
- src/Numeric/Matrix/Internal/Mat44f.hs +0/−186
- src/Numeric/Matrix/LU.hs +201/−0
- src/Numeric/Matrix/QR.hs +231/−0
- src/Numeric/Matrix/SVD.hs +799/−0
- src/Numeric/PrimBytes.hs +11/−12
- src/Numeric/ProductOrd/NonTransitive.hs +0/−2
- src/Numeric/ProductOrd/Partial.hs +0/−2
- src/Numeric/Quaternion/Internal.hs +7/−1
- src/Numeric/Quaternion/Internal/QDouble.hs +228/−152
- src/Numeric/Quaternion/Internal/QFloat.hs +228/−152
- src/Numeric/Scalar/Internal.hs +5/−20
- src/Numeric/Subroutine/Householder.hs +278/−0
- src/Numeric/Subroutine/SolveTriangular.hs +173/−0
- src/Numeric/Subroutine/Sort.hs +250/−0
- src/Numeric/Vector/Internal.hs +40/−39
- test/Numeric/Arbitraries.hs +614/−0
- test/Numeric/DataFrame/Arbitraries.hs +0/−274
- test/Numeric/DataFrame/BasicTest.hs +1/−2
- test/Numeric/DataFrame/SubSpaceTest.hs +110/−31
- test/Numeric/Matrix/BidiagonalTest.hs +144/−0
- test/Numeric/Matrix/LUTest.hs +104/−0
- test/Numeric/Matrix/QRTest.hs +203/−0
- test/Numeric/Matrix/SVDTest.hs +182/−0
- test/Numeric/MatrixDoubleTest.hs +101/−126
- test/Numeric/MatrixFloatTest.hs +101/−132
- test/Numeric/PrimBytesTest.hs +3/−3
- test/Numeric/QuaterDoubleTest.hs +52/−57
- test/Numeric/QuaterFloatTest.hs +52/−57
- test/Numeric/Subroutine/SolveTriangularTest.hs +249/−0
- test/Spec.hs +12/−1
Setup.hs view
@@ -1,2 +1,32 @@+{-+Disable some errors and warnings during the haddock pass+ (caused by compiler plugins and hs-boot)+ -}+{-# OPTIONS_GHC -Wall #-}+module Main (main) where+ import Distribution.Simple-main = defaultMain+import Distribution.Simple.Setup++main :: IO ()+main = defaultMainWithHooks simpleUserHooks+ { confHook = \a -> confHook simpleUserHooks a . tweakFlags }++tweakFlags :: ConfigFlags -> ConfigFlags+tweakFlags flags = flags+ { configProgramArgs = addHaddockArgs (configProgramArgs flags) }++addHaddockArgs :: [(String, [String])] -> [(String, [String])]+addHaddockArgs []+ = [("haddock", newHaddockGhcArgs)]+addHaddockArgs (("haddock", args):otherProgsArgs)+ = ("haddock", args ++ newHaddockGhcArgs) : otherProgsArgs+addHaddockArgs (progArgs:otherProgsArgs)+ = progArgs : addHaddockArgs otherProgsArgs++newHaddockGhcArgs :: [String]+newHaddockGhcArgs =+ [ "--optghc=-fdefer-type-errors"+ , "--optghc=-fno-warn-deferred-type-errors"+ , "--optghc=-fno-warn-missing-home-modules"+ ]
bench/subspacefolds.hs view
@@ -23,47 +23,47 @@ seq t1 putStrLn $ "Created DataFrame, elapsed time is " ++ show (diffUTCTime t1 t0) putStrLn "\nRunning a ewfoldl on scalar elements..."- let rezEwf = ewfoldl @Float @DList @'[] (\a x -> return $! fromMaybe x a + fromMaybe 0 a / (x+1)) (Just 1) df+ let rezEwf = ewfoldl' @Float @DList @'[] (\a x -> return $! fromMaybe x a + fromMaybe 0 a / (x+1)) (Just 1) df t2 <- rezEwf `seq` getCurrentTime seq t2 putStrLn $ "Done; elapsed time = " ++ show (diffUTCTime t2 t1) print rezEwf putStrLn "\nRunning a iwfoldl on scalar elements (not using idx)..."- let rezIwf = iwfoldl @Float @DList @'[] (\_ a x -> a + a / (x+1)) 1 df+ let rezIwf = iwfoldl' @Float @DList @'[] (\_ a x -> a + a / (x+1)) 1 df t3 <- rezIwf `seq` getCurrentTime seq t3 putStrLn $ "Done; elapsed time = " ++ show (diffUTCTime t3 t2) print rezIwf putStrLn "\nRunning a iwfoldr on scalar elements (using fromEnum idx)..."- let rezIwf2 = iwfoldr @Float @DList @'[] (\i x a -> return $! fromMaybe 0 a + x / ((1+) . fromIntegral $ fromEnum i)) (Just 0) df+ let rezIwf2 = iwfoldr' @Float @DList @'[] (\i x a -> return $! fromMaybe 0 a + x / ((1+) . fromIntegral $ fromEnum i)) (Just 0) df t4 <- rezIwf2 `seq` getCurrentTime seq t4 putStrLn $ "Done; elapsed time = " ++ show (diffUTCTime t4 t3) print rezIwf2 putStrLn "\nRunning a iwfoldl on scalar elements (enforcing idx)..."- let rezIwf3 = iwfoldl @Float @DList @'[] (\i a x -> i `seq` return $! fromMaybe 0 a + fromMaybe x a / (x+1)) (Just 1) df+ let rezIwf3 = iwfoldl' @Float @DList @'[] (\i a x -> i `seq` return $! fromMaybe 0 a + fromMaybe x a / (x+1)) (Just 1) df t5 <- rezIwf3 `seq` getCurrentTime seq t5 putStrLn $ "Done; elapsed time = " ++ show (diffUTCTime t5 t4) print rezIwf3 putStrLn "\nRunning a ewfoldl on vector4 elements..."- let rezEwv1 = ewfoldl @Float @(Init DList) @'[Last DList]- (\a x -> return $! fromMaybe 2 a + fromMaybe 0 a / (1 + iwgen @_ @_ @'[] (\(Idx i:*U) -> x ! Idx (i+1) :* U )) )+ let rezEwv1 = ewfoldl' @Float @(Init DList) @'[Last DList]+ (\a x -> return $! fromMaybe 2 a + fromMaybe 0 a / (1 + iwgen @_ @_ @'[] (\(Idx i:*U) -> x ! (i+1) )) ) (Just (3 :: DataFrame Float '[4])) df t6 <- rezEwv1 `seq` getCurrentTime seq t6 putStrLn $ "Done; elapsed time = " ++ show (diffUTCTime t6 t5) print rezEwv1 putStrLn "\nRunning a ewfoldr on vector3 elements..."- let rezEwv2 = ewfoldr @Float @(Init DList) @'[Last DList]- (\x a -> return $! fromMaybe 2 a + fromMaybe 1 a / (1 + iwgen @_ @_ @'[] (\(Idx i:*U) -> x ! Idx (i+1):* U )))+ let rezEwv2 = ewfoldr' @Float @(Init DList) @'[Last DList]+ (\x a -> return $! fromMaybe 2 a + fromMaybe 1 a / (1 + iwgen @_ @_ @'[] (\(Idx i:*U) -> x ! (i+1) ))) (Just (3 :: DataFrame Float '[3])) df t7 <- rezEwv2 `seq` getCurrentTime seq t7 putStrLn $ "Done; elapsed time = " ++ show (diffUTCTime t7 t6) print rezEwv2 putStrLn "\nRunning a ewfoldr with matrix products..."- let rezEwm = ewfoldr @Float @(Take 3 DList) @(Drop 3 DList)+ let rezEwm = ewfoldr' @Float @(Take 3 DList) @(Drop 3 DList) (\x a -> a + x %* (DF5 1 0.5 0.1 0.01 0.001) ) (1 :: DataFrame Float (Init (Drop 3 DList) +: 3)) df t8 <- rezEwm `seq` getCurrentTime@@ -74,4 +74,4 @@ putStrLn "Checking indexes"- print $ df ! (2:*1:*1:*3:*1:*U :: Idxs (Take 5 DList))+ print $ df.!2.!1.!1.!3.!1
easytensor.cabal view
@@ -1,11 +1,13 @@--- This file has been generated from package.yaml by hpack version 0.28.2.+cabal-version: 1.24++-- This file has been generated from package.yaml by hpack version 0.31.1. -- -- see: https://github.com/sol/hpack ----- hash: c1b3d6ebe751438aebdd514a166566c79aa2a98083755f019015772fc95cb34e+-- hash: 75fa5546720cb775f5ce9a26382fcd60253bba851441e469156242ee52655f97 name: easytensor-version: 2.0.1.0+version: 2.1.0.0 synopsis: Pure, type-indexed haskell vector, matrix, and tensor library. description: Pure, type-indexed haskell vector, matrix, and tensor library. Features dimensionality type-checking for all operations. Generic n-dimensional versions are implemented using low-level prim ops. Allows ad-hoc replacement with fixed low-dimensionality vectors and matrices without changing user interface. Please see the README on GitHub at <https://github.com/achirkin/easytensor#readme> category: math, geometry@@ -16,14 +18,18 @@ copyright: Copyright: (c) 2019 Artem Chirkin license: BSD3 license-file: LICENSE-build-type: Simple-cabal-version: >= 1.10+build-type: Custom source-repository head type: git location: https://github.com/achirkin/easytensor subdir: easytensor +custom-setup+ setup-depends:+ Cabal+ , base+ flag unsafeindices description: Disable bound checks when accessing elements of a tensor, matrix, or vector. manual: True@@ -31,6 +37,7 @@ library exposed-modules:+ Numeric.Basics Numeric.DataFrame Numeric.DataFrame.Contraction Numeric.DataFrame.Internal.Backend@@ -41,17 +48,24 @@ Numeric.DataFrame.SubSpace Numeric.DataFrame.Type Numeric.Matrix+ Numeric.Matrix.Bidiagonal+ Numeric.Matrix.LU+ Numeric.Matrix.QR+ Numeric.Matrix.SVD Numeric.PrimBytes Numeric.ProductOrd Numeric.ProductOrd.NonTransitive Numeric.ProductOrd.Partial Numeric.Quaternion Numeric.Scalar+ Numeric.Subroutine.Householder+ Numeric.Subroutine.SolveTriangular+ Numeric.Subroutine.Sort Numeric.Vector other-modules: Numeric.Matrix.Internal- Numeric.Matrix.Internal.Mat44d- Numeric.Matrix.Internal.Mat44f+ Numeric.Matrix.Internal.Double+ Numeric.Matrix.Internal.Float Numeric.Scalar.Internal Numeric.Vector.Internal Numeric.Quaternion.Internal@@ -73,7 +87,7 @@ ghc-options: -Wall -Wcompat -Wtabs -Wmonomorphism-restriction -Wmissing-local-signatures -Wmissing-home-modules -Widentities -Wredundant-constraints build-depends: base >=4.11 && <5- , constraints-deriving >=1.0.4.0 && <2+ , constraints-deriving >=1.1.1.0 && <2 , dimensions >=2 && <3 if flag(unsafeindices) cpp-options: -DUNSAFE_INDICES@@ -83,14 +97,19 @@ type: exitcode-stdio-1.0 main-is: Spec.hs other-modules:- Numeric.DataFrame.Arbitraries+ Numeric.Arbitraries Numeric.DataFrame.BasicTest Numeric.DataFrame.SubSpaceTest+ Numeric.Matrix.BidiagonalTest+ Numeric.Matrix.LUTest+ Numeric.Matrix.QRTest+ Numeric.Matrix.SVDTest Numeric.MatrixDoubleTest Numeric.MatrixFloatTest Numeric.PrimBytesTest Numeric.QuaterDoubleTest Numeric.QuaterFloatTest+ Numeric.Subroutine.SolveTriangularTest Spec.Util Paths_easytensor hs-source-dirs:@@ -100,7 +119,7 @@ Cabal , QuickCheck , base >=4.11 && <5- , constraints-deriving >=1.0.4.0 && <2+ , constraints-deriving >=1.1.1.0 && <2 , dimensions >=2 && <3 , easytensor , template-haskell@@ -116,7 +135,7 @@ ghc-options: -Wall -Wcompat -Wtabs -Wmonomorphism-restriction -Wmissing-local-signatures -Wmissing-home-modules -Widentities -Wredundant-constraints build-depends: base >=4.11 && <5- , constraints-deriving >=1.0.4.0 && <2+ , constraints-deriving >=1.1.1.0 && <2 , dimensions >=2 && <3 , easytensor default-language: Haskell2010@@ -131,7 +150,7 @@ ghc-options: -Wall -Wcompat -Wtabs -Wmonomorphism-restriction -Wmissing-local-signatures -Wmissing-home-modules -Widentities -Wredundant-constraints build-depends: base >=4.11 && <5- , constraints-deriving >=1.0.4.0 && <2+ , constraints-deriving >=1.1.1.0 && <2 , dimensions >=2 && <3 , easytensor , time
src-base/Numeric/DataFrame/Internal/Backend/Family.hs view
@@ -14,6 +14,8 @@ {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-}+-- Combined GADT patterns are a bit of a trouble+{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-} -- Constraints @b ~ BackendFamily t ds@ is not redundant; they allows to avoid AllowAmbigousTypes. {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} {- |@@ -66,10 +68,6 @@ BackendFamily Double '[4] = DoubleX4 BackendFamily t ds = ArrayBase t ds --- | Promise that we are sure the backend is ArrayBase.-unsafeDefault :: forall t ds . Dict (BackendFamily t ds ~ ArrayBase t ds)-unsafeDefault = unsafeCoerce# (Dict @(ArrayBase t ds ~ ArrayBase t ds))- -- | Singleton type used to determine the currently used DataFrame backend; -- establishes a good bijection @b <~> t ds@ (better than just using @BackendFamily@ everywhere). data BackendSing (t :: Type) (ds :: [Nat]) (backend :: Type) where@@ -99,33 +97,41 @@ #if defined(__HADDOCK__) || defined(__HADDOCK_VERSION__) instance KnownBackend t ds b where bSing = undefined #else-instance KnownBackend t '[] (ScalarBase t) where bSing = BSC-instance KnownBackend Float '[2] FloatX2 where bSing = BF2-instance KnownBackend Float '[3] FloatX3 where bSing = BF3-instance KnownBackend Float '[4] FloatX4 where bSing = BF4-instance KnownBackend Double '[2] DoubleX2 where bSing = BD2-instance KnownBackend Double '[3] DoubleX3 where bSing = BD3-instance KnownBackend Double '[4] DoubleX4 where bSing = BD4+instance KnownBackend t '[] (ScalarBase t) where bSing = BSC; {-# INLINE bSing #-}+instance KnownBackend Float '[2] FloatX2 where bSing = BF2; {-# INLINE bSing #-}+instance KnownBackend Float '[3] FloatX3 where bSing = BF3; {-# INLINE bSing #-}+instance KnownBackend Float '[4] FloatX4 where bSing = BF4; {-# INLINE bSing #-}+instance KnownBackend Double '[2] DoubleX2 where bSing = BD2; {-# INLINE bSing #-}+instance KnownBackend Double '[3] DoubleX3 where bSing = BD3; {-# INLINE bSing #-}+instance KnownBackend Double '[4] DoubleX4 where bSing = BD4; {-# INLINE bSing #-} instance PrimBytes t => KnownBackend t ds (ArrayBase t ds) where bSing = case unsafeDefault @t @ds of Dict -> BPB+ {-# INLINE bSing #-}+instance {-# INCOHERENT #-}+ (PrimBytes t, Dimensions ds, b ~ BackendFamily t ds)+ => KnownBackend t ds b where+ bSing = case (dims @ds, primTag @t undefined) of+ (U, _) -> BSC+ (D2 :* U, PTagFloat) -> BF2+ (D3 :* U, PTagFloat) -> BF3+ (D4 :* U, PTagFloat) -> BF4+ (D2 :* U, PTagDouble) -> BD2+ (D3 :* U, PTagDouble) -> BD3+ (D4 :* U, PTagDouble) -> BD4+ _ -> case unsafeDefault @t @ds of Dict -> BPB+ {-# INLINE bSing #-}++-- | Promise that we are sure the backend is ArrayBase.+unsafeDefault :: forall t ds . Dict (BackendFamily t ds ~ ArrayBase t ds)+unsafeDefault = unsafeCoerce# (Dict @(ArrayBase t ds ~ ArrayBase t ds)) #endif -- | Find an instance of `KnownBackend` class using `PrimBytes` and `Dimensions`. inferKnownBackend :: forall t ds b . (PrimBytes t, Dimensions ds, b ~ BackendFamily t ds) => Dict (KnownBackend t ds b)-inferKnownBackend = case (dims @ds, primTag @t undefined) of- (U, _) -> Dict- (d :* U, PTagFloat)- | Just Dict <- sameDim (D @2) d -> Dict- | Just Dict <- sameDim (D @3) d -> Dict- | Just Dict <- sameDim (D @4) d -> Dict- (d :* U, PTagDouble)- | Just Dict <- sameDim (D @2) d -> Dict- | Just Dict <- sameDim (D @3) d -> Dict- | Just Dict <- sameDim (D @4) d -> Dict- _ -> case unsafeDefault @t @ds of Dict -> Dict+inferKnownBackend = Dict {-# INLINE inferKnownBackend #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/ArrayBase.hs view
@@ -1,9 +1,9 @@+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-}@@ -24,6 +24,7 @@ import Data.Int import Data.Word import GHC.Base hiding (foldr)+import GHC.Exts (TYPE) import Numeric.DataFrame.Internal.PrimArray import Numeric.Dimensions import Numeric.PrimBytes@@ -40,24 +41,23 @@ (# t -- Same value for each element; -- this is the cheapest way to initialize an array. -- It is also used for Num instances to avoid dependency on Dimensions.- | (# Int# -- Offset in Content array (measured in elements)+ | (# CumulDims -- Steps array; [Word]+ -- similar to steps in OpenCV::Mat, but counted in elements+ -- rather than in bytes.+ -- e.g. 0th element is the size of the content in elements+ -- (data must be contiguous)+ -- 1th element is the size of subspace data minus outer dim.+ , Int# -- Offset in Content array (measured in elements) , ByteArray# -- Content; -- elements are stored row-by-row, plane-by-plane; -- (C-style, rather than Fortran-style); -- similar to C, OpenCV, etc. -- The data must be contiguous! -- (i.e. stride must be the same size as the element size).- , CumulDims -- Steps array; [Word]- -- similar to steps in OpenCV::Mat, but counted in elements- -- rather than in bytes.- -- e.g. 0th element is the size of the content in elements- -- (data must be contiguous)- -- 1th element is the size of subspace data minus outer dim. , Dict (PrimBytes t) #) #) - instance (PrimBytes t, Dimensions ds) => PrimBytes (ArrayBase t ds) where {-# SPECIALIZE instance Dimensions ds => PrimBytes (ArrayBase Float ds) #-} {-# SPECIALIZE instance Dimensions ds => PrimBytes (ArrayBase Double ds) #-}@@ -72,16 +72,13 @@ {-# SPECIALIZE instance Dimensions ds => PrimBytes (ArrayBase Word32 ds) #-} {-# SPECIALIZE instance Dimensions ds => PrimBytes (ArrayBase Word64 ds) #-} - getBytes (ArrayBase a ) = case a of- (# t | #)- | W# nw <- totalDim' @ds- , n <- word2Int# nw- , tbs <- byteSize t -> go tbs (tbs *# n) t- (# | (# _, arr, _, _ #) #) ->- -- very weird trick with touch# allows to workaround GHC bug- -- "internal error: ARR_WORDS object entered!"- -- TODO: report this- case runRW# (\s -> (# touch# arr s, arr #)) of (# _, ba #) -> ba+ getBytes = withArrayContent'+ (\t -> let tbs = byteSize t+ in go tbs (tbs *# totalDim# @ds) t)+ -- very weird trick with touch# allows to workaround GHC bug+ -- "internal error: ARR_WORDS object entered!"+ -- TODO: report this+ (\_ _ arr -> case runRW# (\s -> (# touch# arr s, arr #)) of (# _, ba #) -> ba) where go :: Int# -> Int# -> t -> ByteArray# go tbs bsize t = case runRW#@@ -89,34 +86,29 @@ (# s1, mba #) -> unsafeFreezeByteArray# mba ( loop# 0# tbs bsize (\i -> writeBytes mba i t) s1 ) ) of (# _, ba #) -> ba- {-# NOINLINE go #-} {-# INLINE getBytes #-} - getBytesPinned (ArrayBase a) = case a of- (# t | #)- | W# nw <- totalDim' @ds- , n <- word2Int# nw- , tbs <- byteSize t -> go tbs (tbs *# n) t- (# | (# off, arr, _, _ #) #) ->- if isTrue# (isByteArrayPinned# arr)- then case runRW# (\s -> (# touch# arr s, arr #)) of (# _, ba #) -> ba- else case runRW#- ( \s0 -> case (# sizeofByteArray# arr- , byteAlign @t undefined- #) of- (# bsize, tba #)- | (# s1, mba #) <- newAlignedPinnedByteArray# bsize tba s0- , s2 <- copyByteArray# arr off mba off bsize s1- -> unsafeFreezeByteArray# mba s2- ) of (# _, ba #) -> ba+ getBytesPinned = withArrayContent' f+ (const $ \off arr -> case runRW# (g off arr) of (# _, ba #) -> ba) where- go :: Int# -> Int# -> t -> ByteArray#- go tbs bsize t = case runRW#- ( \s0 -> case newAlignedPinnedByteArray# bsize (byteAlign t) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- ( loop# 0# tbs bsize (\i -> writeBytes mba i t) s1 )- ) of (# _, ba #) -> ba- {-# NOINLINE go #-}+ g :: Int# -> ByteArray# -> State# RealWorld -> (# State# RealWorld, ByteArray# #)+ g off arr s0+ | isTrue# (isByteArrayPinned# arr)+ = (# touch# arr s0, arr #)+ | tba <- byteAlign @t undefined+ , bsize <- sizeofByteArray# arr+ , (# s1, mba #) <- newAlignedPinnedByteArray# bsize tba s0+ , s2 <- copyByteArray# arr off mba off bsize s1+ = unsafeFreezeByteArray# mba s2+ f :: t -> ByteArray#+ f t+ | tbs <- byteSize t+ , bsize <- totalDim# @ds *# tbs+ = case runRW#+ ( \s0 -> case newAlignedPinnedByteArray# bsize (byteAlign t) s0 of+ (# s1, mba #) -> unsafeFreezeByteArray# mba+ ( loop# 0# tbs bsize (\i -> writeBytes mba i t) s1 )+ ) of (# _, ba #) -> ba {-# INLINE getBytesPinned #-} @@ -124,7 +116,7 @@ | tbs <- byteSize (undefined :: t) , (# offN, offRem #) <- quotRemInt# bOff tbs = case offRem of- 0# -> ArrayBase (# | (# offN, ba, steps, Dict #) #)+ 0# -> fromElems' steps offN ba _ -> case cdTotalDim# steps of n -> go (tbs *# n) where steps = cumulDims $ dims @ds@@ -137,8 +129,7 @@ of (# s1, mba #) -> unsafeFreezeByteArray# mba (copyByteArray# ba bOff mba 0# bsize s1)- ) of (# _, r #) -> ArrayBase (# | (# 0#, r, steps, Dict #) #)- {-# NOINLINE go #-}+ ) of (# _, r #) -> fromElems' steps 0# r {-# INLINE fromBytes #-} readBytes mba bOff s0@@ -149,16 +140,17 @@ = case newByteArray# bsize s0 of (# s1, mba1 #) -> case unsafeFreezeByteArray# mba1 (copyMutableByteArray# mba bOff mba1 0# bsize s1) of- (# s2, ba #) -> (# s2, ArrayBase (# | (# 0#, ba, steps, Dict #) #) #)+ (# s2, ba #) -> (# s2, fromElems' steps 0# ba #) {-# INLINE readBytes #-} - writeBytes mba bOff (ArrayBase c)- | tbs <- byteSize (undefined :: t) = case c of- (# t | #) | W# n <- totalDim' @ds ->- loop# bOff tbs (bOff +# word2Int# n *# tbs) (\i -> writeBytes mba i t)- (# | (# offContent, arrContent, steps, _ #) #)- | n <- cdTotalDim# steps->- copyByteArray# arrContent (offContent *# tbs) mba bOff (n *# tbs)+ writeBytes mba bOff+ | tbs <- byteSize (undefined :: t) = withArrayContent'+ (\t -> loop# bOff tbs+ (bOff +# totalDim# @ds *# tbs)+ (\i -> writeBytes mba i t))+ (\steps offContent arrContent ->+ copyByteArray# arrContent (offContent *# tbs)+ mba bOff (cdTotalDim# steps *# tbs)) {-# INLINE writeBytes #-} readAddr addr s0@@ -169,29 +161,28 @@ = case newByteArray# bsize s0 of (# s1, mba1 #) -> case unsafeFreezeByteArray# mba1 (copyAddrToByteArray# addr mba1 0# bsize s1) of- (# s2, ba #) -> (# s2, ArrayBase (# | (# 0# , ba, steps, Dict #) #) #)+ (# s2, ba #) -> (# s2, fromElems' steps 0# ba #) {-# INLINE readAddr #-} - writeAddr (ArrayBase c) addr- | tbs <- byteSize (undefined :: t) = case c of- (# t | #) | W# n <- totalDim' @ds ->- loop# 0# tbs (word2Int# n *# tbs) (\i -> writeAddr t (plusAddr# addr i))- (# | (# offContent, arrContent, steps, _ #) #)- | n <- cdTotalDim# steps ->- copyByteArrayToAddr# arrContent (offContent *# tbs) addr (n *# tbs)+ writeAddr a addr+ | tbs <- byteSize (undefined :: t) = withArrayContent'+ (\t -> loop# 0# tbs+ (totalDim# @ds *# tbs)+ (\i -> writeAddr t (plusAddr# addr i)))+ (\steps offContent arrContent ->+ copyByteArrayToAddr# arrContent (offContent *# tbs)+ addr (cdTotalDim# steps *# tbs)) a {-# INLINE writeAddr #-} - byteSize _ = case totalDim' @ds of -- WARNING: slow!- W# n -> byteSize (undefined :: t) *# word2Int# n+ byteSize _ = byteSize @t undefined *# totalDim# @ds -- WARNING: slow! {-# INLINE byteSize #-} - byteAlign _ = byteAlign (undefined :: t)+ byteAlign _ = byteAlign @t undefined {-# INLINE byteAlign #-} - byteOffset (ArrayBase a) = case a of- (# _ | #) -> 0#- (# | (# off, _, _, _ #) #) -> off *# byteSize (undefined :: t)+ byteOffset = withArrayContent'+ (\_ -> 0#) (\_ off _ -> off *# byteSize @t undefined) {-# INLINE byteOffset #-} byteFieldOffset _ _ = negateInt# 1#@@ -199,8 +190,7 @@ indexArray ba off | steps <- cumulDims $ dims @ds- , n <- cdTotalDim# steps- = ArrayBase (# | (# off *# n, ba, steps, Dict #) #)+ = fromElems' steps (off *# cdTotalDim# steps) ba {-# INLINE indexArray #-} @@ -222,17 +212,17 @@ (ArrayBase (# b | #)) = comb x (f a b) accumV2Idempotent x f comb- a@(ArrayBase (# | (# _, _, steps, Dict #) #))+ a@(ArrayBase (# | (# steps, _, _, Dict #) #)) b@(ArrayBase (# | _ #)) = foldr (comb . (\i -> f (ixOff i a) (ixOff i b))) x [0 .. fromIntegral (cdTotalDim steps) - 1] accumV2Idempotent x f comb (ArrayBase (# a | #))- b@(ArrayBase (# | (# _, _, steps, Dict #) #))+ b@(ArrayBase (# | (# steps, _, _, Dict #) #)) = foldr (comb . (\i -> f a (ixOff i b))) x [0 .. fromIntegral (cdTotalDim steps) - 1] accumV2Idempotent x f comb- a@(ArrayBase (# | (# _, _, steps, Dict #) #))+ a@(ArrayBase (# | (# steps, _, _, Dict #) #)) (ArrayBase (# b | #)) = foldr (comb . (\i -> f (ixOff i a) b)) x [0 .. fromIntegral (cdTotalDim steps) - 1]@@ -242,7 +232,7 @@ mapV :: (t -> t) -> ArrayBase t ds -> ArrayBase t ds mapV f (ArrayBase (# t | #)) = ArrayBase (# f t | #)-mapV f x@(ArrayBase (# | (# offN, ba, steps, Dict #) #))+mapV f x@(ArrayBase (# | (# steps, offN, ba, Dict #) #)) | tbs <- byteSize (undefEl x) , n <- cdTotalDim# steps = case runRW#@@ -251,7 +241,7 @@ ( loop1# n (\i -> writeArray mba i (f (indexArray ba (offN +# i)))) s1 )- ) of (# _, r #) -> ArrayBase (# | (# 0#, r, steps, Dict #) #)+ ) of (# _, r #) -> fromElems' steps 0# r {-# INLINE mapV #-} @@ -259,8 +249,8 @@ -> ArrayBase t ds -> ArrayBase t ds -> ArrayBase t ds zipV f (ArrayBase (# x | #)) b = mapV (f x) b zipV f a (ArrayBase (# y | #)) = mapV (`f` y) a-zipV f a@(ArrayBase (# | (# oa, ba, steps, Dict #) #))- (ArrayBase (# | (# ob, bb, _, _ #) #))+zipV f a@(ArrayBase (# | (# steps, oa, ba, Dict #) #))+ (ArrayBase (# | (# _, ob, bb, _ #) #)) | n <- cdTotalDim# steps = case runRW# ( \s0 -> case newByteArray# (byteSize (undefEl a) *# n) s0 of@@ -272,7 +262,7 @@ ) ) s1 )- ) of (# _, r #) -> ArrayBase (# | (# 0#, r, steps, Dict #) #)+ ) of (# _, r #) -> fromElems' steps 0# r {-# INLINE zipV #-} @@ -397,8 +387,8 @@ {-# SPECIALIZE instance Bounded (ArrayBase Word16 ds) #-} {-# SPECIALIZE instance Bounded (ArrayBase Word32 ds) #-} {-# SPECIALIZE instance Bounded (ArrayBase Word64 ds) #-}- maxBound = ArrayBase (# maxBound | #)- minBound = ArrayBase (# minBound | #)+ maxBound = broadcast' maxBound+ minBound = broadcast' minBound instance Num t => Num (ArrayBase t ds) where {-# SPECIALIZE instance Num (ArrayBase Float ds) #-}@@ -425,7 +415,7 @@ {-# INLINE abs #-} signum = mapV signum {-# INLINE signum #-}- fromInteger i = ArrayBase (# fromInteger i | #)+ fromInteger = broadcast' . fromInteger {-# INLINE fromInteger #-} instance Fractional t => Fractional (ArrayBase t ds) where@@ -435,14 +425,14 @@ {-# INLINE (/) #-} recip = mapV recip {-# INLINE recip #-}- fromRational r = ArrayBase (# fromRational r | #)+ fromRational = broadcast' . fromRational {-# INLINE fromRational #-} instance Floating t => Floating (ArrayBase t ds) where {-# SPECIALIZE instance Floating (ArrayBase Float ds) #-} {-# SPECIALIZE instance Floating (ArrayBase Double ds) #-}- pi = ArrayBase (# pi | #)+ pi = broadcast' pi {-# INLINE pi #-} exp = mapV exp {-# INLINE exp #-}@@ -480,26 +470,9 @@ {-# INLINE atanh #-} instance PrimBytes t => PrimArray t (ArrayBase t ds) where- {-# SPECIALIZE instance PrimArray Float (ArrayBase Float ds) #-}- {-# SPECIALIZE instance PrimArray Double (ArrayBase Double ds) #-}- {-# SPECIALIZE instance PrimArray Int (ArrayBase Int ds) #-}- {-# SPECIALIZE instance PrimArray Word (ArrayBase Word ds) #-}- {-# SPECIALIZE instance PrimArray Int8 (ArrayBase Int8 ds) #-}- {-# SPECIALIZE instance PrimArray Int16 (ArrayBase Int16 ds) #-}- {-# SPECIALIZE instance PrimArray Int32 (ArrayBase Int32 ds) #-}- {-# SPECIALIZE instance PrimArray Int64 (ArrayBase Int64 ds) #-}- {-# SPECIALIZE instance PrimArray Word8 (ArrayBase Word8 ds) #-}- {-# SPECIALIZE instance PrimArray Word16 (ArrayBase Word16 ds) #-}- {-# SPECIALIZE instance PrimArray Word32 (ArrayBase Word32 ds) #-}- {-# SPECIALIZE instance PrimArray Word64 (ArrayBase Word64 ds) #-} - broadcast t = ArrayBase (# t | #)- {-# INLINE broadcast #-}-- ix# i (ArrayBase a) = case a of- (# t | #) -> t- (# | (# off, arr, _, _ #) #) -> indexArray arr (off +# i)- {-# INLINE ix# #-}+ broadcast# t = ArrayBase (# t | #)+ {-# INLINE broadcast# #-} gen# steps f z0 = go (byteSize @t undefined *# n) where@@ -510,57 +483,63 @@ (# s2, z1 #) -> case unsafeFreezeByteArray# mba s2 of (# s3, ba #) -> (# s3, (# z1, ba #) #) ) of (# _, (# z1, ba #) #)- -> (# z1, ArrayBase (# | (# 0# , ba, steps, Dict #) #) #)- {-# NOINLINE go #-}+ -> (# z1, fromElems' steps 0# ba #) loop0 mba i z s | isTrue# (i ==# n) = (# s, z #) | otherwise = case f z of (# z', x #) -> loop0 mba (i +# 1#) z' (writeArray mba i x s) {-# INLINE gen# #-} - upd# steps i x (ArrayBase (# a | #)) = go (byteSize x)- where- n = cdTotalDim# steps- go tbs = case runRW#- ( \s0 -> case newByteArray# (tbs *# n) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- (writeArray mba i x- (loop1# n (\j -> writeArray mba j a) s1)- )- ) of (# _, r #) -> ArrayBase (# | (# 0# , r, steps, Dict #) #)- {-# NOINLINE go #-}- upd# _ i x (ArrayBase (# | (# offN, ba, steps, Dict #) #))- = go (byteSize x)+ upd# steps i x = withArrayContent' f g where n = cdTotalDim# steps- go tbs = case runRW#- ( \s0 -> case newByteArray# (tbs *# n) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- (writeArray mba i x- (copyByteArray# ba (offN *# tbs) mba 0# (tbs *# n) s1)- )- ) of (# _, r #) -> ArrayBase (# | (# 0#, r, steps, Dict #) #)- {-# NOINLINE go #-}+ f a = go (byteSize x)+ where+ go tbs = case runRW#+ ( \s0 -> case newByteArray# (tbs *# n) s0 of+ (# s1, mba #) -> unsafeFreezeByteArray# mba+ (writeArray mba i x+ (loop1# n (\j -> writeArray mba j a) s1)+ )+ ) of (# _, r #) -> fromElems' steps 0# r+ g _ offN ba = go (byteSize x)+ where+ go tbs = case runRW#+ ( \s0 -> case newByteArray# (tbs *# n) s0 of+ (# s1, mba #) -> unsafeFreezeByteArray# mba+ (writeArray mba i x+ (copyByteArray# ba (offN *# tbs) mba 0# (tbs *# n) s1)+ )+ ) of (# _, r #) -> fromElems' steps 0# r {-# INLINE upd# #-} - arrayContent# (ArrayBase a) = case a of- (# x | #) -> (# x | #)- (# | (# off, arr, cd, _ #) #) -> (# | (# cd, off, arr #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# = withArrayContent'+ {-# INLINE withArrayContent# #-} - offsetElems (ArrayBase a) = case a of- (# _ | #) -> 0#- (# | (# off, _, _, _ #) #) -> off- {-# INLINE offsetElems #-}+ fromElems# = fromElems'+ {-# INLINE fromElems# #-} - uniqueOrCumulDims (ArrayBase a) = case a of- (# t | #) -> Left t- (# | (# _, _, steps, _ #) #) -> Right steps- {-# INLINE uniqueOrCumulDims #-}+withArrayContent' ::+ forall (t :: Type) (ds :: [Nat]) (rep :: RuntimeRep) (r :: TYPE rep)+ . (t -> r)+ -> (CumulDims -> Int# -> ByteArray# -> r)+ -> ArrayBase t ds -> r+withArrayContent' f _ (ArrayBase (# e | #)) = f e+withArrayContent' _ g (ArrayBase (# | (# steps, off, ba, _ #) #)) = g steps off ba+{-# INLINE withArrayContent' #-} - fromElems steps off ba = ArrayBase (# | (# off, ba, steps, Dict #) #)- {-# INLINE fromElems #-}+fromElems' :: forall (t :: Type) (ds :: [Nat])+ . PrimBytes t => CumulDims -> Int# -> ByteArray# -> ArrayBase t ds+fromElems' steps off ba = ArrayBase (# | (# steps, off, ba, Dict #) #)+{-# INLINE fromElems' #-} +broadcast' :: t -> ArrayBase t ds+broadcast' t = ArrayBase (# t | #)+{-# INLINE broadcast' #-}++totalDim# :: forall (ds :: [Nat]) . Dimensions ds => Int#+totalDim# = case totalDim' @ds of W# n -> word2Int# n+{-# INLINE totalDim# #-} loop# :: Int# -- ^ initial value -> Int# -- ^ step
src-base/Numeric/DataFrame/Internal/Backend/Family/DoubleX2.hs view
@@ -4,12 +4,10 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} module Numeric.DataFrame.Internal.Backend.Family.DoubleX2 (DoubleX2 (..)) where - import GHC.Base import Numeric.DataFrame.Internal.PrimArray import Numeric.PrimBytes@@ -368,8 +366,8 @@ instance PrimArray Double DoubleX2 where - broadcast (D# x) = DoubleX2# x x- {-# INLINE broadcast #-}+ broadcast# (D# x) = DoubleX2# x x+ {-# INLINE broadcast# #-} ix# 0# (DoubleX2# a1 _) = D# a1 ix# 1# (DoubleX2# _ a2) = D# a2@@ -386,8 +384,8 @@ upd# _ _ _ x = x {-# INLINE upd# #-} - arrayContent# x = (# | (# CumulDims [ELEM_N, 1], 0#, getBytes x #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# _ g x = g (CumulDims [ELEM_N, 1]) 0# (getBytes x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-}@@ -395,7 +393,7 @@ uniqueOrCumulDims _ = Right (CumulDims [ELEM_N, 1]) {-# INLINE uniqueOrCumulDims #-} - fromElems _ off ba = DoubleX2#+ fromElems# _ off ba = DoubleX2# (indexDoubleArray# ba off) (indexDoubleArray# ba (off +# 1#))- {-# INLINE fromElems #-}+ {-# INLINE fromElems# #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/DoubleX3.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} module Numeric.DataFrame.Internal.Backend.Family.DoubleX3 (DoubleX3 (..)) where@@ -399,8 +398,8 @@ instance PrimArray Double DoubleX3 where - broadcast (D# x) = DoubleX3# x x x- {-# INLINE broadcast #-}+ broadcast# (D# x) = DoubleX3# x x x+ {-# INLINE broadcast# #-} ix# 0# (DoubleX3# a1 _ _) = D# a1 ix# 1# (DoubleX3# _ a2 _) = D# a2@@ -420,8 +419,8 @@ upd# _ _ _ x = x {-# INLINE upd# #-} - arrayContent# x = (# | (# CumulDims [ELEM_N, 1], 0#, getBytes x #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# _ g x = g (CumulDims [ELEM_N, 1]) 0# (getBytes x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-}@@ -429,8 +428,8 @@ uniqueOrCumulDims _ = Right (CumulDims [ELEM_N, 1]) {-# INLINE uniqueOrCumulDims #-} - fromElems _ off ba = DoubleX3#+ fromElems# _ off ba = DoubleX3# (indexDoubleArray# ba off) (indexDoubleArray# ba (off +# 1#)) (indexDoubleArray# ba (off +# 2#))- {-# INLINE fromElems #-}+ {-# INLINE fromElems# #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/DoubleX4.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} module Numeric.DataFrame.Internal.Backend.Family.DoubleX4 (DoubleX4 (..)) where@@ -426,8 +425,8 @@ instance PrimArray Double DoubleX4 where - broadcast (D# x) = DoubleX4# x x x x- {-# INLINE broadcast #-}+ broadcast# (D# x) = DoubleX4# x x x x+ {-# INLINE broadcast# #-} ix# 0# (DoubleX4# a1 _ _ _) = D# a1 ix# 1# (DoubleX4# _ a2 _ _) = D# a2@@ -450,8 +449,8 @@ upd# _ _ _ x = x {-# INLINE upd# #-} - arrayContent# x = (# | (# CumulDims [ELEM_N, 1], 0#, getBytes x #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# _ g x = g (CumulDims [ELEM_N, 1]) 0# (getBytes x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-}@@ -459,9 +458,9 @@ uniqueOrCumulDims _ = Right (CumulDims [ELEM_N, 1]) {-# INLINE uniqueOrCumulDims #-} - fromElems _ off ba = DoubleX4#+ fromElems# _ off ba = DoubleX4# (indexDoubleArray# ba off) (indexDoubleArray# ba (off +# 1#)) (indexDoubleArray# ba (off +# 2#)) (indexDoubleArray# ba (off +# 3#))- {-# INLINE fromElems #-}+ {-# INLINE fromElems# #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/FloatX2.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} module Numeric.DataFrame.Internal.Backend.Family.FloatX2 (FloatX2 (..)) where@@ -368,8 +367,8 @@ instance PrimArray Float FloatX2 where - broadcast (F# x) = FloatX2# x x- {-# INLINE broadcast #-}+ broadcast# (F# x) = FloatX2# x x+ {-# INLINE broadcast# #-} ix# 0# (FloatX2# a1 _) = F# a1 ix# 1# (FloatX2# _ a2) = F# a2@@ -386,8 +385,8 @@ upd# _ _ _ x = x {-# INLINE upd# #-} - arrayContent# x = (# | (# CumulDims [ELEM_N, 1], 0#, getBytes x #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# _ g x = g (CumulDims [ELEM_N, 1]) 0# (getBytes x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-}@@ -395,7 +394,7 @@ uniqueOrCumulDims _ = Right (CumulDims [ELEM_N, 1]) {-# INLINE uniqueOrCumulDims #-} - fromElems _ off ba = FloatX2#+ fromElems# _ off ba = FloatX2# (indexFloatArray# ba off) (indexFloatArray# ba (off +# 1#))- {-# INLINE fromElems #-}+ {-# INLINE fromElems# #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/FloatX3.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} module Numeric.DataFrame.Internal.Backend.Family.FloatX3 (FloatX3 (..)) where@@ -400,8 +399,8 @@ instance PrimArray Float FloatX3 where - broadcast (F# x) = FloatX3# x x x- {-# INLINE broadcast #-}+ broadcast# (F# x) = FloatX3# x x x+ {-# INLINE broadcast# #-} ix# 0# (FloatX3# a1 _ _) = F# a1 ix# 1# (FloatX3# _ a2 _) = F# a2@@ -421,8 +420,8 @@ upd# _ _ _ x = x {-# INLINE upd# #-} - arrayContent# x = (# | (# CumulDims [ELEM_N, 1], 0#, getBytes x #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# _ g x = g (CumulDims [ELEM_N, 1]) 0# (getBytes x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-}@@ -430,8 +429,8 @@ uniqueOrCumulDims _ = Right (CumulDims [ELEM_N, 1]) {-# INLINE uniqueOrCumulDims #-} - fromElems _ off ba = FloatX3#+ fromElems# _ off ba = FloatX3# (indexFloatArray# ba off) (indexFloatArray# ba (off +# 1#)) (indexFloatArray# ba (off +# 2#))- {-# INLINE fromElems #-}+ {-# INLINE fromElems# #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/FloatX4.hs view
@@ -5,7 +5,6 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-}@@ -20,6 +19,7 @@ import Numeric.ProductOrd import qualified Numeric.ProductOrd.NonTransitive as NonTransitive import qualified Numeric.ProductOrd.Partial as Partial+import Unsafe.Coerce (unsafeCoerce) data FloatX4 = FloatX4# Float# Float# Float# Float#@@ -432,8 +432,8 @@ instance PrimArray Float FloatX4 where - broadcast (F# x) = FloatX4# x x x x- {-# INLINE broadcast #-}+ broadcast# (F# x) = FloatX4# x x x x+ {-# INLINE broadcast# #-} ix# 0# (FloatX4# a1 _ _ _) = F# a1 ix# 1# (FloatX4# _ a2 _ _) = F# a2@@ -456,8 +456,8 @@ upd# _ _ _ x = x {-# INLINE upd# #-} - arrayContent# x = (# | (# CumulDims [ELEM_N, 1], 0#, getBytes x #) #)- {-# INLINE arrayContent# #-}+ withArrayContent# _ g x = g (CumulDims [ELEM_N, 1]) 0# (getBytes x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-}@@ -465,13 +465,12 @@ uniqueOrCumulDims _ = Right (CumulDims [ELEM_N, 1]) {-# INLINE uniqueOrCumulDims #-} - fromElems _ off ba = FloatX4#+ fromElems# _ off ba = FloatX4# (indexFloatArray# ba off) (indexFloatArray# ba (off +# 1#)) (indexFloatArray# ba (off +# 2#)) (indexFloatArray# ba (off +# 3#))- {-# INLINE fromElems #-}-+ {-# INLINE fromElems# #-} -------------------------------------------------------------------------------- -- Rewrite rules to improve efficiency of algorithms@@ -485,14 +484,13 @@ -------------------------------------------------------------------------------- getIdxOffset :: Idxs '[4] -> Int#-getIdxOffset is = case unsafeCoerce# is of- [W# i] -> word2Int# i- _ -> 0#+getIdxOffset is = case unsafeCoerce is of+ ~[w] -> case w of W# i -> word2Int# i {-# INLINE getIdxOffset #-} {-# RULES-"index/FloatX4" forall i . index @Float @'[4] i- = unsafeCoerce# (ix# @Float @FloatX4 (getIdxOffset i))+"index/FloatX4" forall i . index @Float @'[4] @'[] @'[4] i+ = unsafeCoerce (ix# @Float @FloatX4 (getIdxOffset i)) #-}
src-base/Numeric/DataFrame/Internal/Backend/Family/ScalarBase.hs view
@@ -9,7 +9,8 @@ module Numeric.DataFrame.Internal.Backend.Family.ScalarBase (ScalarBase (..)) where -import GHC.Base+import Data.Coerce+import Numeric.Basics import Numeric.DataFrame.Internal.PrimArray import Numeric.PrimBytes import Numeric.ProductOrd@@ -18,7 +19,7 @@ -- | Specialize ScalarBase type without any arrays newtype ScalarBase t = ScalarBase { _unScalarBase :: t }- deriving ( Enum, Eq, Bounded, Integral+ deriving ( Enum, Eq, Bounded, Integral, Epsilon, RealExtras, RealFloatExtras , Num, Fractional, Floating, Ord, Real, RealFrac, RealFloat , PrimBytes) @@ -28,23 +29,23 @@ deriving instance Ord t => Ord (Partial.ProductOrd (ScalarBase t)) instance PrimBytes t => PrimArray t (ScalarBase t) where- broadcast = unsafeCoerce#- {-# INLINE broadcast #-}- ix# _ = unsafeCoerce#+ broadcast# = coerce+ {-# INLINE broadcast# #-}+ ix# _ = coerce {-# INLINE ix# #-}- gen# _ = unsafeCoerce#+ gen# _ = coerce {-# INLINE gen# #-} upd# _ 0# = const . ScalarBase upd# _ _ = const id {-# INLINE upd# #-}- arrayContent# x = (# _unScalarBase x | #)- {-# INLINE arrayContent# #-}+ withArrayContent# f _ x = f (_unScalarBase x)+ {-# INLINE withArrayContent# #-} offsetElems _ = 0# {-# INLINE offsetElems #-} uniqueOrCumulDims = Left . _unScalarBase {-# INLINE uniqueOrCumulDims #-}- fromElems _ off ba = indexArray ba off- {-# INLINE fromElems #-}+ fromElems# _ off ba = indexArray ba off+ {-# INLINE fromElems# #-} _suppressHlintUnboxedTuplesWarning :: () -> (# (), () #) _suppressHlintUnboxedTuplesWarning = undefined
+ src/Numeric/Basics.hs view
@@ -0,0 +1,238 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ViewPatterns #-}+module Numeric.Basics+ ( -- * Constants+ pattern M_E, pattern M_LOG2E, pattern M_LOG10E, pattern M_LN2, pattern M_LN10+ , pattern M_SQRT2, pattern M_SQRT1_2+ , pattern M_PI, pattern M_PI_2, pattern M_PI_3, pattern M_PI_4+ , pattern M_1_PI, pattern M_2_PI, pattern M_2_SQRTPI+ , Epsilon (..), pattern M_EPS+ -- * Functions+ , RealExtras (..), RealFloatExtras (..)+ , negateUnless+ -- * Exceptions+ , IterativeMethod, TooManyIterations, tooManyIterations+ ) where++import Data.Int+import Data.Word+import GHC.Exception+import GHC.Stack+import Numeric.PrimBytes++-- | \( e \)+pattern M_E :: (Eq a, Fractional a) => Fractional a => a+pattern M_E = 2.718281828459045235360287471352662498++-- | \( \log_2 e \)+pattern M_LOG2E :: (Eq a, Fractional a) => Fractional a => a+pattern M_LOG2E = 1.442695040888963407359924681001892137++-- | \( \log_{10} e \)+pattern M_LOG10E :: (Eq a, Fractional a) => Fractional a => a+pattern M_LOG10E = 0.434294481903251827651128918916605082++-- | \( \log_e 2 \)+pattern M_LN2 :: (Eq a, Fractional a) => Fractional a => a+pattern M_LN2 = 0.693147180559945309417232121458176568++-- | \( \log_e 10 \)+pattern M_LN10 :: (Eq a, Fractional a) => Fractional a => a+pattern M_LN10 = 2.302585092994045684017991454684364208++-- | \( \sqrt{2} \)+pattern M_SQRT2 :: (Eq a, Fractional a) => Fractional a => a+pattern M_SQRT2 = 1.414213562373095048801688724209698079++-- | \( \frac{1}{\sqrt{2}} \)+pattern M_SQRT1_2 :: (Eq a, Fractional a) => Fractional a => a+pattern M_SQRT1_2 = 0.707106781186547524400844362104849039++-- | \( \pi \)+pattern M_PI :: (Eq a, Fractional a) => Fractional a => a+pattern M_PI = 3.1415926535897932384626433832795028841971+-- | \( \frac{\pi}{2} \)+pattern M_PI_2 :: (Eq a, Fractional a) => Fractional a => a+pattern M_PI_2 = 1.570796326794896619231321691639751442++-- | \( \frac{\pi}{3} \)+pattern M_PI_3 :: (Eq a, Fractional a) => Fractional a => a+pattern M_PI_3 = 1.047197551196597746154214461093167628++-- | \( \frac{\pi}{4} \)+pattern M_PI_4 :: (Eq a, Fractional a) => Fractional a => a+pattern M_PI_4 = 0.785398163397448309615660845819875721++-- | \( \frac{1}{\pi} \)+pattern M_1_PI :: (Eq a, Fractional a) => Fractional a => a+pattern M_1_PI = 0.318309886183790671537767526745028724++-- | \( \frac{2}{\pi} \)+pattern M_2_PI :: (Eq a, Fractional a) => Fractional a => a+pattern M_2_PI = 0.636619772367581343075535053490057448++-- | \( \frac{2}{\sqrt{\pi}} \)+pattern M_2_SQRTPI :: (Eq a, Fractional a) => Fractional a => a+pattern M_2_SQRTPI = 1.128379167095512573896158903121545172++-- | Define a meaningful precision for complex floating-point operations.+class (Eq a, Floating a) => Epsilon a where+ -- | A small positive number that depends on the type of @a@.+ -- Compare your values to @epsilon@ to tell if they are near zero.+ epsilon :: a++instance Epsilon Double where+ epsilon = 1e-12++instance Epsilon Float where+ epsilon = 1e-6++-- | A small positive number that depends on the type of @a@.+pattern M_EPS :: Epsilon a => a+pattern M_EPS <- ((epsilon ==) -> True)+ where+ M_EPS = epsilon++-- | Negate if @False@.+-- This is a useful alternative to `signum`,+-- when @signum 0 == 0@ causing some troubles.+negateUnless :: Num t => Bool -> t -> t+negateUnless True = id+negateUnless False = negate+{-# INLINE negateUnless #-}++-- | Extra functions for `Real` types.+class (Real a, PrimBytes a) => RealExtras a where+ -- | @copysign x y@ returns a value with the magnitude of x and the sign of y.+ --+ -- NB: in future, this function is to reimplemented using primops+ -- and should behave the same way as its C analogue.+ copysign :: a -> a -> a+ copysign x y+ | (x >= 0) == (y >= 0) = x+ | otherwise = negate x+ {-# INLINE copysign #-}++instance RealExtras Int+instance RealExtras Int8+instance RealExtras Int16+instance RealExtras Int32+instance RealExtras Int64+instance RealExtras Word+instance RealExtras Word8+instance RealExtras Word16+instance RealExtras Word32+instance RealExtras Word64++instance RealExtras Float where+ copysign = c'copysignf+ {-# INLINE copysign #-}++instance RealExtras Double where+ copysign = c'copysignd+ {-# INLINE copysign #-}++-- | Extra functions for `RealFrac` types.+class (Epsilon a, RealExtras a, RealFloat a) => RealFloatExtras a where+ -- | \( \sqrt{ x^2 + y^2 } \).+ --+ -- NB: in future, this function is to reimplemented using primops+ -- and should behave the same way as its C analogue.+ hypot :: a -> a -> a+ hypot x y = scaleFloat ea (sqrt (an*an + bn*bn))+ where+ x' = abs x+ y' = abs y+ (a,b) = if x' >= y' then (x', y') else (y', x')+ (_, ea) = decodeFloat a+ an = scaleFloat (negate ea) a+ bn = scaleFloat (negate ea) b+ {-# INLINE hypot #-}+ -- | Maximum finite number representable by this FP type.+ maxFinite :: a++instance RealFloatExtras Float where+ hypot = c'hypotf+ {-# INLINE hypot #-}+ maxFinite = 3.40282347e+38+ {-# INLINE maxFinite #-}+++instance RealFloatExtras Double where+ hypot = c'hypotd+ {-# INLINE hypot #-}+ maxFinite = 1.7976931348623157e+308+ {-# INLINE maxFinite #-}++foreign import ccall unsafe "hypot" c'hypotd :: Double -> Double -> Double+foreign import ccall unsafe "hypotf" c'hypotf :: Float -> Float -> Float+foreign import ccall unsafe "copysign" c'copysignd :: Double -> Double -> Double+foreign import ccall unsafe "copysignf" c'copysignf :: Float -> Float -> Float++{- |+This is just an alias for the `HasCallStack` constraint.+It servers two goals:++ 1. Document functions that use iterative methods and can fail+ (supposedly in extremely rare cases).+ 2. Provide a call stack in case of failure.++Use `tooManyIterations` function where necessary if you implement an iterative+algorithm and annotate your implementation function with `IterativeMethod` constraint.++ -}+type IterativeMethod = HasCallStack++-- | Throw a `TooManyIterations` exception.+tooManyIterations ::+ IterativeMethod+ => String -- ^ Label (e.g. function name)+ -> a+tooManyIterations s+ = throw TooManyIterations+ { tmiUserInfo = s+ , tmiCallStack = callStack+ }++{- |+Typically, this exception can occur in an iterative algorithm;+when the number of iterations exceeds a pre-defined limit, but the stop condition+is not fulfilled.++Use the `IterativeMethod` constraint to annotate functions that throw this exception.+ -}+data TooManyIterations+ = TooManyIterations+ { tmiUserInfo :: String+ -- ^ Short description of the error, e.g. a function name.+ , tmiCallStack :: CallStack+ -- ^ Function call stack.+ -- Note, this field is ignored in the `Eq` and `Ord` instances.+ }++-- | Note, this instance ignores `oodCallStack`+instance Eq TooManyIterations where+ (==) a b = tmiUserInfo a == tmiUserInfo b++-- | Note, this instance ignores `oodCallStack`+instance Ord TooManyIterations where+ compare a b = compare (tmiUserInfo a) (tmiUserInfo b)++instance Show TooManyIterations where+ showsPrec p e = addLoc errStr+ where+ addLoc s+ = let someE = errorCallWithCallStackException s (tmiCallStack e)+ errc :: ErrorCall+ errc = case fromException someE of+ Nothing -> ErrorCall s+ Just ec -> ec+ in showsPrec p errc+ errStr = unlines+ [ "An iterative method has exceeded the limit for the iteration number."+ , "User info: " ++ tmiUserInfo e+ ]++instance Exception TooManyIterations
src/Numeric/DataFrame.hs view
@@ -7,6 +7,8 @@ -- * Functionality , module Numeric.DataFrame.SubSpace , module Numeric.DataFrame.Contraction+ , module Numeric.Basics+ , module Numeric.Subroutine.Sort ) where import Numeric.DataFrame.Internal.Backend ()@@ -18,3 +20,7 @@ import Numeric.Matrix import Numeric.Scalar import Numeric.Vector++import Numeric.Basics++import Numeric.Subroutine.Sort (sort, sortBy)
src/Numeric/DataFrame/Contraction.hs view
@@ -1,16 +1,17 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE InstanceSigs #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-} ----------------------------------------------------------------------------- -- | -- Module : Numeric.DataFrame.Contraction
src/Numeric/DataFrame/IO.hs view
@@ -2,14 +2,18 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ViewPatterns #-} ----------------------------------------------------------------------------- -- | -- Module : Numeric.DataFrame.IO@@ -22,208 +26,348 @@ module Numeric.DataFrame.IO ( IODataFrame (XIOFrame), SomeIODataFrame (..)- , newDataFrame, newPinnedDataFrame+ , castDataFrame+ , newDataFrame, newPinnedDataFrame, oneMoreDataFrame+ , subDataFrameView, subDataFrameView' , copyDataFrame, copyMutableDataFrame , copyDataFrame', copyMutableDataFrame'+ , copyDataFrameOff, copyMutableDataFrameOff , freezeDataFrame, unsafeFreezeDataFrame- , thawDataFrame, thawPinDataFrame, unsafeThawDataFrame+ , thawDataFrame, thawPinDataFrame, unsafeThawDataFrame, withThawDataFrame , writeDataFrame, writeDataFrameOff , readDataFrame, readDataFrameOff- , withDataFramePtr, isDataFramePinned+ , isDataFramePinned, getDataFrameSteps+ , withDataFramePtr ) where -import GHC.Base-import GHC.IO (IO (..))-import GHC.Ptr (Ptr (..))+import GHC.IO (IO (..))+import GHC.Ptr (Ptr (..)) +import Control.Monad.ST (RealWorld)+import Data.Coerce+import Data.Kind import Numeric.DataFrame.Internal.Mutable import Numeric.DataFrame.Internal.PrimArray import Numeric.DataFrame.Type import Numeric.Dimensions+import Unsafe.Coerce -- | Mutable DataFrame that lives in IO. -- Internal representation is always a MutableByteArray.-data family IODataFrame (t :: Type) (ns :: [k])---- | Pure wrapper on a mutable byte array-newtype instance IODataFrame t (ns :: [Nat]) = IODataFrame (MDataFrame RealWorld t (ns :: [Nat]))+newtype IODataFrame (t :: Type) (ns :: [k]) = IODataFrame (MDataFrame RealWorld t (ns :: [k])) -- | Data frame with some dimensions missing at compile time. -- Pattern-match against its constructor to get a Nat-indexed mutable data frame.-data instance IODataFrame t (xs :: [XNat])- = forall (ns :: [Nat]) . Dimensions ns- => XIOFrame (IODataFrame t ns)+pattern XIOFrame :: forall (t :: Type) (xns :: [XNat]) . ()+ => forall (ns :: [Nat]) . (FixedDims xns ns, Dimensions ns)+ => IODataFrame t ns -> IODataFrame t xns+pattern XIOFrame df <- (mkXIOFramePat -> XIOFramePat df)+ where+ XIOFrame = castDataFrame+{-# COMPLETE XIOFrame #-} +data XIOFramePat (t :: Type) (xns :: [XNat])+ = forall (ns :: [Nat]) . (FixedDims xns ns, Dimensions ns)+ => XIOFramePat (IODataFrame t ns)++mkXIOFramePat :: forall (t :: Type) (xns :: [XNat])+ . IODataFrame t xns -> XIOFramePat t xns+mkXIOFramePat df+ | SomeDims ds <- fromSteps (getDataFrameSteps df)+ , XDims (Dims :: Dims ns) <- (unsafeCoerce ds) :: Dims xns+ = XIOFramePat @t @xns @ns (unsafeCoerce df)+ | otherwise+ = error "XIOFrame pattern: impossible args"+ -- | Mutable DataFrame of unknown dimensionality data SomeIODataFrame (t :: Type) = forall (ns :: [Nat]) . Dimensions ns => SomeIODataFrame (IODataFrame t ns) +-- | Allow coercing between @XNat@-indexed and @Nat@-indexed Mutable DataFrames.+castDataFrame ::+ forall (t :: Type) (xns :: [XNat]) (ns :: [Nat])+ . FixedDims xns ns+ => IODataFrame t ns -> IODataFrame t xns+castDataFrame = coerce (castDataFrame# @t @xns @ns)+{-# INLINE castDataFrame #-}+ -- | Create a new mutable DataFrame.-newDataFrame :: forall t (ns :: [Nat])- . ( PrimBytes t, Dimensions ns)- => IO (IODataFrame t ns)-newDataFrame = IODataFrame <$> IO (newDataFrame# @t @ns)+newDataFrame ::+ forall (t :: Type) ns+ . (PrimBytes t, Dimensions ns) => IO (IODataFrame t ns)+newDataFrame = coerce (newDataFrame# @t @_ @ns) {-# INLINE newDataFrame #-} - -- | Create a new mutable DataFrame.-newPinnedDataFrame :: forall t (ns :: [Nat])- . ( PrimBytes t, Dimensions ns)- => IO (IODataFrame t ns)-newPinnedDataFrame = IODataFrame <$> IO (newPinnedDataFrame# @t @ns)+newPinnedDataFrame ::+ forall (t :: Type) ns+ . (PrimBytes t, Dimensions ns) => IO (IODataFrame t ns)+newPinnedDataFrame = coerce (newPinnedDataFrame# @t @_ @ns) {-# INLINE newPinnedDataFrame #-} +-- | Create a new mutable DataFrame of the same size.+oneMoreDataFrame ::+ forall (t :: Type) ns+ . IODataFrame t ns -> IO (IODataFrame t ns)+oneMoreDataFrame = coerce (oneMoreDataFrame# @t @_ @ns)+{-# INLINE oneMoreDataFrame #-} +-- | View a part of a DataFrame.+--+-- This function does not perform a copy.+-- All changes to a new DataFrame will be reflected in the original DataFrame as well.+--+-- If any of the dims in @as@ or @b@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+subDataFrameView ::+ forall (t :: Type) b bi bd as bs asbs+ . (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs)+ => Idxs (as +: bi) -> IODataFrame t asbs -> IODataFrame t (bd :+ bs)+subDataFrameView = coerce (subDataFrameView# @t @_ @b @bi @bd @as @bs @asbs)++-- | View a part of a DataFrame.+--+-- This function does not perform a copy.+-- All changes to a new DataFrame will be reflected in the original DataFrame as well.+--+-- This is a simpler version of @subDataFrameView@ that allows+-- to view over one index at a time.+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+subDataFrameView' ::+ forall (t :: Type) as bs asbs+ . ConcatList as bs asbs+ => Idxs as -> IODataFrame t asbs -> IODataFrame t bs+subDataFrameView' = coerce (subDataFrameView'# @t @_ @as @bs @asbs)+ -- | Copy one DataFrame into another mutable DataFrame at specified position. ----- In contrast to @copyMDataFrame'@, this function allows to copy over a range of contiguous--- indices over a single dimension.+-- In contrast to @copyDataFrame'@, this function allows to copy over a range+-- of contiguous indices over a single dimension. -- For example, you can write a 3x4 matrix into a 7x4 matrix, starting at indices 0..3.-copyDataFrame :: forall (t :: Type)- (b :: Nat) (bi :: Nat) (bd :: Nat)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- . ( b ~ (bi + bd - 1)- , PrimBytes t- , PrimBytes (DataFrame t (bd :+ bs))- , ConcatList as (b :+ bs) asbs- )- => Idxs (as +: bi) -> DataFrame t (bd :+ bs)- -> IODataFrame t asbs -> IO ()-copyDataFrame ei df (IODataFrame mdf) = IO (copyDataFrame# ei df mdf)+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function copies only as+-- many elements as fits into the dataframe along this dimension (possibly none).+copyDataFrame ::+ forall (t :: Type) b bi bd as bs asbs+ . ( SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs+ , PrimArray t (DataFrame t (bd :+ bs))+ , ConcatList as (b :+ bs) asbs )+ => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> IODataFrame t asbs -> IO ()+copyDataFrame = coerce (copyDataFrame# @t @_ @b @bi @bd @as @bs @asbs) {-# INLINE copyDataFrame #-} -- | Copy one mutable DataFrame into another mutable DataFrame at specified position. ----- In contrast to @copyMDataFrame'@, this function allows to copy over a range of contiguous--- indices over a single dimension.+-- In contrast to @copyMutableDataFrame'@, this function allows to copy over a range+-- of contiguous indices over a single dimension. -- For example, you can write a 3x4 matrix into a 7x4 matrix, starting at indices 0..3.-copyMutableDataFrame :: forall (t :: Type)- (b :: Nat) (bi :: Nat) (bd :: Nat)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- . ( b ~ (bi + bd - 1)- , PrimBytes t- , ConcatList as (b :+ bs) asbs- )- => Idxs (as +: bi) -> IODataFrame t (bd :+ bs)- -> IODataFrame t asbs -> IO ()-copyMutableDataFrame ei (IODataFrame mdfA) (IODataFrame mdfB)- = IO (copyMDataFrame# ei mdfA mdfB)+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function copies only as+-- many elements as fits into the dataframe along this dimension (possibly none).+copyMutableDataFrame ::+ forall (t :: Type) b bi bd as bs asbs+ . ( SubFrameIndexCtx b bi bd+ , ExactDims bs+ , PrimBytes t+ , ConcatList as (b :+ bs) asbs )+ => Idxs (as +: bi) -> IODataFrame t (bd :+ bs) -> IODataFrame t asbs -> IO ()+copyMutableDataFrame = coerce (copyMDataFrame# @t @_ @b @bi @bd @as @bs @asbs) {-# INLINE copyMutableDataFrame #-} -- | Copy one DataFrame into another mutable DataFrame at specified position. ----- This is a simpler version of @copyDataFrame@ that allows to copy over one index at a time.-copyDataFrame' :: forall (t :: Type)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- . ( PrimBytes t- , PrimBytes (DataFrame t bs)- , ConcatList as bs asbs- )- => Idxs as -> DataFrame t bs- -> IODataFrame t asbs -> IO ()-copyDataFrame' ei df (IODataFrame mdf) = IO (copyDataFrame'# ei df mdf)+-- This is a simpler version of @copyDataFrame@ that allows+-- to copy over one index at a time.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+copyDataFrame' ::+ forall (t :: Type) as bs asbs+ . ( ExactDims bs+ , PrimArray t (DataFrame t bs)+ , ConcatList as bs asbs )+ => Idxs as -> DataFrame t bs -> IODataFrame t asbs -> IO ()+copyDataFrame' = coerce (copyDataFrame'# @t @_ @as @bs @asbs) {-# INLINE copyDataFrame' #-} -- | Copy one mutable DataFrame into another mutable DataFrame at specified position. ----- This is a simpler version of @copyDataFrame@ that allows to copy over one index at a time.-copyMutableDataFrame' :: forall (t :: Type)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- . ( PrimBytes t- , ConcatList as bs asbs- )- => Idxs as -> IODataFrame t bs- -> IODataFrame t asbs -> IO ()-copyMutableDataFrame' ei (IODataFrame mdfA) (IODataFrame mdfB)- = IO (copyMDataFrame'# ei mdfA mdfB)+-- This is a simpler version of @copyMutableDataFrame@ that allows+-- to copy over one index at a time.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+copyMutableDataFrame' ::+ forall (t :: Type) as bs asbs+ . (ExactDims bs, PrimBytes t, ConcatList as bs asbs)+ => Idxs as -> IODataFrame t bs -> IODataFrame t asbs -> IO ()+copyMutableDataFrame' = coerce (copyMDataFrame'# @t @_ @as @bs @asbs) {-# INLINE copyMutableDataFrame' #-} +-- | Copy one DataFrame into another mutable DataFrame by offset in+-- primitive elements.+--+-- This is a low-level copy function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+copyDataFrameOff ::+ forall (t :: Type) as bs asbs+ . ( Dimensions bs+ , PrimArray t (DataFrame t bs)+ , ConcatList as bs asbs )+ => Int -> DataFrame t bs -> IODataFrame t asbs -> IO ()+copyDataFrameOff = coerce (copyDataFrameOff# @t @_ @as @bs @asbs)+{-# INLINE copyDataFrameOff #-}++-- | Copy one mutable DataFrame into another mutable DataFrame by offset in+-- primitive elements.+--+-- This is a low-level copy function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds+copyMutableDataFrameOff ::+ forall (t :: Type) as bs asbs+ . (ExactDims bs, PrimBytes t, ConcatList as bs asbs)+ => Int -> IODataFrame t bs -> IODataFrame t asbs -> IO ()+copyMutableDataFrameOff = coerce (copyMDataFrameOff# @t @_ @as @bs @asbs)+{-# INLINE copyMutableDataFrameOff #-}+ -- | Make a mutable DataFrame immutable, without copying.-unsafeFreezeDataFrame :: forall (t :: Type) (ns :: [Nat])- . PrimArray t (DataFrame t ns)- => IODataFrame t ns -> IO (DataFrame t ns)-unsafeFreezeDataFrame (IODataFrame mdf) = IO (unsafeFreezeDataFrame# mdf)+unsafeFreezeDataFrame ::+ forall (t :: Type) ns+ . PrimArray t (DataFrame t ns)+ => IODataFrame t ns -> IO (DataFrame t ns)+unsafeFreezeDataFrame = coerce (unsafeFreezeDataFrame# @t @_ @ns) {-# INLINE unsafeFreezeDataFrame #-} - -- | Copy content of a mutable DataFrame into a new immutable DataFrame.-freezeDataFrame :: forall (t :: Type) (ns :: [Nat])- . PrimArray t (DataFrame t ns)- => IODataFrame t ns -> IO (DataFrame t ns)-freezeDataFrame (IODataFrame mdf) = IO (freezeDataFrame# mdf)+freezeDataFrame ::+ forall (t :: Type) ns+ . PrimArray t (DataFrame t ns)+ => IODataFrame t ns -> IO (DataFrame t ns)+freezeDataFrame = coerce (freezeDataFrame# @t @_ @ns) {-# INLINE freezeDataFrame #-} -- | Create a new mutable DataFrame and copy content of immutable one in there.-thawDataFrame :: forall (t :: Type) (ns :: [Nat])- . (Dimensions ns, PrimBytes (DataFrame t ns))- => DataFrame t ns -> IO (IODataFrame t ns)-thawDataFrame df = IODataFrame <$> IO (thawDataFrame# df)+thawDataFrame ::+ forall (t :: Type) ns+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns -> IO (IODataFrame t ns)+thawDataFrame = coerce (thawDataFrame# @t @_ @ns) {-# INLINE thawDataFrame #-} -- | Create a new mutable DataFrame and copy content of immutable one in there. -- The result array is pinned and aligned.-thawPinDataFrame :: forall (t :: Type) (ns :: [Nat])- . (Dimensions ns, PrimBytes (DataFrame t ns))- => DataFrame t ns -> IO (IODataFrame t ns)-thawPinDataFrame df = IODataFrame <$> IO (thawPinDataFrame# df)+thawPinDataFrame ::+ forall (t :: Type) ns+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns -> IO (IODataFrame t ns)+thawPinDataFrame = coerce (thawPinDataFrame# @t @_ @ns) {-# INLINE thawPinDataFrame #-} -- | UnsafeCoerces an underlying byte array.-unsafeThawDataFrame :: forall (t :: Type) (ns :: [Nat])- . ( Dimensions ns- , PrimBytes (DataFrame t ns), PrimBytes t)- => DataFrame t ns -> IO (IODataFrame t ns)-unsafeThawDataFrame df = IODataFrame <$> IO (unsafeThawDataFrame# df)+unsafeThawDataFrame ::+ forall (t :: Type) ns+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns+ -> IO (IODataFrame t ns)+unsafeThawDataFrame = coerce (unsafeThawDataFrame# @t @_ @ns) {-# INLINE unsafeThawDataFrame #-} ---- | Write a single element at the specified index-writeDataFrame :: forall t (ns :: [Nat])- . PrimBytes t- => IODataFrame t ns -> Idxs ns -> DataFrame t ('[] :: [Nat]) -> IO ()-writeDataFrame (IODataFrame mdf) ei = IO . writeDataFrame# mdf ei . unsafeCoerce#-{-# INLINE writeDataFrame #-}----- | Read a single element at the specified index-readDataFrame :: forall (t :: Type) (ns :: [Nat])- . PrimBytes t- => IODataFrame t ns -> Idxs ns -> IO (DataFrame t ('[] :: [Nat]))-readDataFrame (IODataFrame mdf) = unsafeCoerce# . IO . readDataFrame# mdf-{-# INLINE readDataFrame #-}-+-- | Given two continuations @f@ and @g@.+-- If the input DataFrame is a single broadcast value, use it in @f@.+-- Otherwise, create a new mutable DataFrame and copy content of immutable one+-- in there; then use it in @g@.+--+-- This function is useful when @thawDataFrame@ cannot be used due to+-- @Dimensions ns@ constraint being not available.+withThawDataFrame ::+ forall (t :: Type) ns r+ . PrimArray t (DataFrame t ns)+ => (t -> IO r)+ -> (IODataFrame t ns -> IO r)+ -> DataFrame t ns -> IO r+withThawDataFrame = coerce (withThawDataFrame# @t @_ @ns @r) --- | Write a single element at the specified element offset-writeDataFrameOff :: forall (t :: Type) (ns :: [Nat])- . PrimBytes t- => IODataFrame t ns -> Int -> DataFrame t ('[] :: [Nat]) -> IO ()-writeDataFrameOff (IODataFrame mdf) (I# i)- = IO . writeDataFrameOff# mdf i . unsafeCoerce#+-- | Write a single element at the specified element offset.+--+-- This is a low-level write function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+writeDataFrameOff ::+ forall (t :: Type) ns+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => IODataFrame t ns -> Int -> DataFrame t ('[] :: KindOf ns) -> IO ()+writeDataFrameOff = coerce (writeDataFrameOff# @t @_ @ns) {-# INLINE writeDataFrameOff #-} +-- | Write a single element at the specified index.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+writeDataFrame ::+ forall (t :: Type) ns+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => IODataFrame t ns -> Idxs ns -> DataFrame t ('[] :: KindOf ns) -> IO ()+writeDataFrame = coerce (writeDataFrame# @t @_ @ns)+{-# INLINE writeDataFrame #-} --- | Read a single element at the specified element offset-readDataFrameOff :: forall (t :: Type) (ns :: [Nat])- . PrimBytes t- => IODataFrame t ns -> Int -> IO (DataFrame t ('[] :: [Nat]))-readDataFrameOff (IODataFrame mdf) (I# i)- = unsafeCoerce# (IO (readDataFrameOff# mdf i))+-- | Read a single element at the specified element offset.+--+-- This is a low-level read function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you read beyond the DataFrame bounds.+readDataFrameOff ::+ forall (t :: Type) ns+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => IODataFrame t ns -> Int -> IO (DataFrame t ('[] :: KindOf ns))+readDataFrameOff = coerce (readDataFrameOff# @t @_ @ns) {-# INLINE readDataFrameOff #-} +-- | Read a single element at the specified index.+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+readDataFrame ::+ forall (t :: Type) ns+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => IODataFrame t ns -> Idxs ns -> IO (DataFrame t ('[] :: KindOf ns))+readDataFrame = coerce (readDataFrame# @t @_ @ns)+{-# INLINE readDataFrame #-} -- | Check if the byte array wrapped by this DataFrame is pinned, -- which means cannot be relocated by GC.-isDataFramePinned :: forall (k :: Type) (t :: Type) (ns :: [k])- . KnownDimKind k- => IODataFrame t ns -> Bool-isDataFramePinned df = case dimKind @k of- DimNat -> case df of- IODataFrame x -> isDataFramePinned# x- DimXNat -> case df of- XIOFrame (IODataFrame x) -> isDataFramePinned# x+isDataFramePinned ::+ forall (t :: Type) ns . IODataFrame t ns -> Bool+isDataFramePinned = coerce (isDataFramePinned# @t @_ @ns)+{-# INLINE isDataFramePinned #-} +-- | Get cumulative dimensions @ns@ of a @IODataFrame t ns@+getDataFrameSteps ::+ forall (t :: Type) ns . IODataFrame t ns -> CumulDims+getDataFrameSteps = coerce (getDataFrameSteps# @t @_ @ns)+{-# INLINE getDataFrameSteps #-} -- | Allow arbitrary IO operations on a pointer to the beginning of the data -- keeping the data from garbage collecting until the arg function returns.@@ -233,15 +377,9 @@ -- -- Warning: use this function on a pinned DataFrame only; -- otherwise, the data may be relocated before the arg fun finishes.-withDataFramePtr :: forall (k :: Type) (t :: Type) (ns :: [k]) (r :: Type)- . (PrimBytes t, KnownDimKind k)- => IODataFrame t ns- -> ( Ptr t -> IO r )- -> IO r-withDataFramePtr df k = case dimKind @k of- DimNat -> case df of- IODataFrame x- -> IO $ withDataFramePtr# x (\p -> case k (Ptr p) of IO f -> f)- DimXNat -> case df of- XIOFrame (IODataFrame x)- -> IO $ withDataFramePtr# x (\p -> case k (Ptr p) of IO f -> f)+withDataFramePtr ::+ forall (t :: Type) ns (r :: Type)+ . PrimBytes t => IODataFrame t ns -> (Ptr t -> IO r) -> IO r+withDataFramePtr (IODataFrame x) k+ = IO (withDataFramePtr# x (\p -> case k (Ptr p) of IO f -> f))+{-# INLINE withDataFramePtr #-}
src/Numeric/DataFrame/Internal/Backend.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RoleAnnotations #-}@@ -9,15 +10,39 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} #if defined(__HADDOCK__) || defined(__HADDOCK_VERSION__) {-# OPTIONS_GHC -fno-warn-simplifiable-class-constraints #-} #else {-# OPTIONS_GHC -fplugin Data.Constraint.Deriving #-} #endif+{-+This module contains all generic and specific instances for all backend+(implementation) types. Its hs-boot counterpart provides only generic instances+(that do not depend on BackendFamily).+Import this module only at the root of the module hierarchy, where the specific+implementation-dependent modules do not need to depend on it.+But do not forget to import this module in the places that will be imported by+the user (such as Numeric.DataFrame). So that the user can benefit from the+compile-time knowledge of the specific backend implementation. +By design the import chain is as follows:++NDI.Backend.Family.hs-boot NDI.Backend.Family+ \ |+ NDI.Backend.hs-boot NDI.Backend (instances only)+ \ /+ NDI.BackendI /+ \ /+ Numeric.DataFrame.Type /+ \ /+ \ /+ Numeric.DataFrame++ -} module Numeric.DataFrame.Internal.Backend- ( DFBackend, Backend (..), BackendFamily, KnownBackend ()+ ( Backend (..), BackendFamily, KnownBackend () , inferKnownBackend, inferPrimElem -- * Auto-deriving instances , inferEq, inferOrd@@ -47,15 +72,15 @@ inferKnownBackend, inferPrimArray, inferPrimElem)-import Numeric.DataFrame.Internal.BackendI (I) --- | Implementation behind the DataFrame-type DFBackend (t :: Type) (ds :: [Nat]) = Backend I t ds (BackendFamily t ds) -- | Backend resolver: -- Use this constraint to find any class instances defined for all DataFrame implementations, -- e.g. @Num@, @PrimBytes@, etc.-type KnownBackend (t :: Type) (ds :: [Nat]) = Impl.KnownBackend t ds (BackendFamily t ds)+class Impl.KnownBackend t ds (BackendFamily t ds)+ => KnownBackend (t :: Type) (ds :: [Nat])+instance Impl.KnownBackend t ds (BackendFamily t ds)+ => KnownBackend (t :: Type) (ds :: [Nat]) -- | A newtype wrapper for all DataFrame implementations. -- I need two layers of wrappers to provide default overlappable instances to@@ -65,12 +90,12 @@ newtype Backend (i :: Type) (t :: Type) (ds :: [Nat]) (backend :: Type) = Backend { _getBackend :: backend } type role Backend phantom phantom phantom representational-type instance DeriveContext (Backend I t ds b) = b ~ BackendFamily t ds--- When typechecker knows what the third parameter @b@ is, no instances overlap,+type instance DeriveContext (Backend i t ds b) = b ~ BackendFamily t ds+-- When typechecker knows what the fourth parameter @b@ is, no instances overlap, -- because @b@ is always always a concrete type. -- The "dynamic instances" derived via ToInstance have to be incoherent, -- because @BackendFamily t ds@ as a type family (before resolving it)--- is no more conrete than just @b@.+-- is no more concrete than just @b@. {-# ANN type Backend (DeriveAll' NoOverlap ["KnownBackend"]) #-} @@ -80,47 +105,48 @@ . (PrimBytes t, Dimensions ds) => Dict (KnownBackend t ds) inferKnownBackend- = Impl.inferKnownBackend @t @ds @(BackendFamily t ds)+ = case Impl.inferKnownBackend @t @ds @(BackendFamily t ds) of+ Dict -> Dict inferPrimElem- :: forall (t :: Type) (d :: Nat) (ds :: [Nat])+ :: forall (t :: Type) (d :: Nat) (ds :: [Nat]) (i :: Type) . KnownBackend t (d ': ds)- => DFBackend t (d ': ds) -> Dict (PrimBytes t)+ => Backend i t (d ': ds) (BackendFamily t (d ': ds)) -> Dict (PrimBytes t) inferPrimElem = Impl.inferPrimElem @t @d @ds . _getBackend inferEq- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Eq t, Impl.KnownBackend t ds b)- => Dict (Eq (Backend I t ds b))+ => Dict (Eq (Backend i t ds b)) inferEq = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds)) $ inferDeriveContext @t @ds @b undefined inferOrd- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (Ord (Backend I t ds b))+ => Dict (Ord (Backend i t ds b)) inferOrd = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds)) $ inferDeriveContext @t @ds @b undefined inferProductOrder- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (ProductOrder (Backend I t ds b))+ => Dict (ProductOrder (Backend i t ds b)) inferProductOrder = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds)) $ inferDeriveContext @t @ds @b undefined inferPONonTransitive- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (Ord (NonTransitive.ProductOrd (Backend I t ds b)))+ => Dict (Ord (NonTransitive.ProductOrd (Backend i t ds b))) inferPONonTransitive = mapDict (unsafeDerive NonTransitive.ProductOrd) . mapDict toBackend@@ -128,9 +154,9 @@ $ inferDeriveContext @t @ds @b undefined inferPOPartial- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (Ord (Partial.ProductOrd (Backend I t ds b)))+ => Dict (Ord (Partial.ProductOrd (Backend i t ds b))) inferPOPartial = mapDict (unsafeDerive Partial.ProductOrd) . mapDict toBackend@@ -138,9 +164,9 @@ $ inferDeriveContext @t @ds @b undefined inferBounded- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Bounded t, Impl.KnownBackend t ds b)- => Dict (Bounded (Backend I t ds b))+ => Dict (Bounded (Backend i t ds b)) inferBounded -- Here is an ugly trick: -- We don't have instance of @Bounded Float@ and @Bounded Double@.@@ -160,27 +186,27 @@ $ inferDeriveContext @t @ds @b undefined inferNum- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Num t, Impl.KnownBackend t ds b)- => Dict (Num (Backend I t ds b))+ => Dict (Num (Backend i t ds b)) inferNum = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds)) $ inferDeriveContext @t @ds @b undefined inferFractional- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Fractional t, Impl.KnownBackend t ds b)- => Dict (Fractional (Backend I t ds b))+ => Dict (Fractional (Backend i t ds b)) inferFractional = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds)) $ inferDeriveContext @t @ds @b undefined inferFloating- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Floating t, Impl.KnownBackend t ds b)- => Dict (Floating (Backend I t ds b))+ => Dict (Floating (Backend i t ds b)) inferFloating = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds))@@ -188,18 +214,18 @@ inferPrimBytes- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (PrimBytes t, Dimensions ds, Impl.KnownBackend t ds b)- => Dict (PrimBytes (Backend I t ds b))+ => Dict (PrimBytes (Backend i t ds b)) inferPrimBytes = mapDict toBackend . mapDict (Sub (Impl.inferBackendInstance @t @ds)) $ inferDeriveContext @t @ds @b undefined inferPrimArray- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (PrimBytes t, Impl.KnownBackend t ds b)- => Dict (PrimArray t (Backend I t ds b))+ => Dict (PrimArray t (Backend i t ds b)) inferPrimArray = mapDict toBackend . mapDict (Sub (Impl.inferPrimArray @t @ds))@@ -208,14 +234,14 @@ -- This is the rule that cannot be encoded in the type system, but enforced -- as an invariant: Backend t ds b implies DeriveContext t ds b-inferDeriveContext :: forall (t :: Type) (ds :: [Nat]) (b :: Type)- . Backend I t ds b- -> Dict (DeriveContext (Backend I t ds b))+inferDeriveContext :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type)+ . Backend i t ds b+ -> Dict (DeriveContext (Backend i t ds b)) inferDeriveContext _ = unsafeCoerce (Dict :: Dict (b ~ b)) {-# INLINE inferDeriveContext #-} -- Backend is the newtype wrapper over b. -- It has the same represenation and I expect it to have the same instance behavior.-toBackend :: forall c t ds b . c b :- c (Backend I t ds b)+toBackend :: forall c t ds b i . c b :- c (Backend i t ds b) toBackend = unsafeDerive Backend {-# INLINE toBackend #-}
src/Numeric/DataFrame/Internal/Backend.hs-boot view
@@ -1,17 +1,22 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE ExplicitForAll #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RoleAnnotations #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitForAll #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RoleAnnotations #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-} #if defined(__HADDOCK__) || defined(__HADDOCK_VERSION__) {-# OPTIONS_GHC -fno-warn-simplifiable-class-constraints #-} #endif module Numeric.DataFrame.Internal.Backend- ( DFBackend, Backend (..), BackendFamily, KnownBackend ()+ ( Backend (..), BackendFamily, KnownBackend () , inferKnownBackend, inferPrimElem -- * Auto-deriving instances , inferEq, inferOrd@@ -32,21 +37,12 @@ import {-# SOURCE #-} Numeric.DataFrame.Internal.Backend.Family (BackendFamily) import {-# SOURCE #-} qualified Numeric.DataFrame.Internal.Backend.Family as Impl (KnownBackend)-import {-# SOURCE #-} Numeric.DataFrame.Internal.BackendI (I) --- | Implementation behind the DataFrame-type DFBackend (t :: Type) (ds :: [Nat]) = Backend I t ds (BackendFamily t ds)---- | Backend resolver:--- Use this constraint to find any class instances defined for all DataFrame implementations,--- e.g. @Num@, @PrimBytes@, etc.-type KnownBackend (t :: Type) (ds :: [Nat]) = Impl.KnownBackend t ds (BackendFamily t ds)+class Impl.KnownBackend t ds (BackendFamily t ds)+ => KnownBackend (t :: Type) (ds :: [Nat])+instance Impl.KnownBackend t ds (BackendFamily t ds)+ => KnownBackend (t :: Type) (ds :: [Nat]) --- | A newtype wrapper for all DataFrame implementations.--- I need two layers of wrappers to provide default overlappable instances to--- all type classes using KnownBackend mechanics.--- Type arguments are redundant here;--- nevertheless, they improve readability of error messages. newtype Backend (i :: Type) (t :: Type) (ds :: [Nat]) (backend :: Type) = Backend { _getBackend :: backend } type role Backend phantom phantom phantom representational@@ -57,61 +53,61 @@ => Dict (KnownBackend t ds) inferPrimElem- :: forall (t :: Type) (d :: Nat) (ds :: [Nat])+ :: forall (t :: Type) (d :: Nat) (ds :: [Nat]) (i :: Type) . KnownBackend t (d ': ds)- => DFBackend t (d ': ds) -> Dict (PrimBytes t)+ => Backend i t (d ': ds) (BackendFamily t (d ': ds)) -> Dict (PrimBytes t) inferEq- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Eq t, Impl.KnownBackend t ds b)- => Dict (Eq (Backend I t ds b))+ => Dict (Eq (Backend i t ds b)) inferOrd- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (Ord (Backend I t ds b))+ => Dict (Ord (Backend i t ds b)) inferProductOrder- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (ProductOrder (Backend I t ds b))+ => Dict (ProductOrder (Backend i t ds b)) inferPONonTransitive- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (Ord (NonTransitive.ProductOrd (Backend I t ds b)))+ => Dict (Ord (NonTransitive.ProductOrd (Backend i t ds b))) inferPOPartial- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Ord t, Impl.KnownBackend t ds b)- => Dict (Ord (Partial.ProductOrd (Backend I t ds b)))+ => Dict (Ord (Partial.ProductOrd (Backend i t ds b))) inferBounded- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Bounded t, Impl.KnownBackend t ds b)- => Dict (Bounded (Backend I t ds b))+ => Dict (Bounded (Backend i t ds b)) inferNum- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Num t, Impl.KnownBackend t ds b)- => Dict (Num (Backend I t ds b))+ => Dict (Num (Backend i t ds b)) inferFractional- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Fractional t, Impl.KnownBackend t ds b)- => Dict (Fractional (Backend I t ds b))+ => Dict (Fractional (Backend i t ds b)) inferFloating- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (Floating t, Impl.KnownBackend t ds b)- => Dict (Floating (Backend I t ds b))+ => Dict (Floating (Backend i t ds b)) inferPrimBytes- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (PrimBytes t, Dimensions ds, Impl.KnownBackend t ds b)- => Dict (PrimBytes (Backend I t ds b))+ => Dict (PrimBytes (Backend i t ds b)) inferPrimArray- :: forall (t :: Type) (ds :: [Nat]) (b :: Type)+ :: forall (t :: Type) (ds :: [Nat]) (b :: Type) (i :: Type) . (PrimBytes t, Impl.KnownBackend t ds b)- => Dict (PrimArray t (Backend I t ds b))+ => Dict (PrimArray t (Backend i t ds b))
src/Numeric/DataFrame/Internal/BackendI.hs view
@@ -7,31 +7,54 @@ {-# LANGUAGE TypeApplications #-} #if defined(__HADDOCK__) || defined(__HADDOCK_VERSION__) {-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS_GHC -fdefer-type-errors #-}-{-# OPTIONS_GHC -fno-warn-deferred-type-errors #-} {-# OPTIONS_GHC -fno-warn-missing-methods #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} {-# OPTIONS_GHC -fno-warn-simplifiable-class-constraints #-} #else {-# OPTIONS_GHC -fplugin Data.Constraint.Deriving #-} #endif+{-+The purpose of this module is to provide incoherent instances of common type classes+for the type @Backend@.+It imports @Backend.hs-boot@ module and thus does not depend on the actual+implementation of @BackendFamily@.+Import this module instead of @Backend.hs-boot@ in the places that must not depend+on @BackendFamily@, such as @Numeric.DataFrame.Type@. +By design the import chain is as follows:++NDI.Backend.Family.hs-boot NDI.Backend.Family+ \ |+ NDI.Backend.hs-boot NDI.Backend (instances only)+ \ /+ NDI.BackendI /+ \ /+ Numeric.DataFrame.Type /+ \ /+ \ /+ Numeric.DataFrame++ -} module Numeric.DataFrame.Internal.BackendI- ( I+ ( DFBackend, Backend (..), Backend.BackendFamily, Backend.KnownBackend ()+ , Backend.inferKnownBackend, Backend.inferPrimElem+#if !defined(__HADDOCK__) && !defined(__HADDOCK_VERSION__) , inferEq, inferOrd , inferProductOrder, inferPONonTransitive, inferPOPartial , inferBounded, inferNum , inferFractional, inferFloating , inferPrimBytes, inferPrimArray+#endif ) where --import Data.Constraint.Deriving (OverlapMode (..),- ToInstance (..))+#if !defined(__HADDOCK__) && !defined(__HADDOCK_VERSION__)+import Data.Constraint (Dict)+import Data.Constraint.Deriving (OverlapMode (..), ToInstance (..))+#endif import Data.Kind (Type) import Numeric.DataFrame.Internal.PrimArray (PrimArray)-import Numeric.Dimensions (Dict, Dimensions, Nat)+import Numeric.Dimensions (Dimensions, Nat) import Numeric.PrimBytes (PrimBytes) import Numeric.ProductOrd (ProductOrder) import qualified Numeric.ProductOrd.NonTransitive as NonTransitive@@ -43,6 +66,9 @@ +-- | Implementation behind the DataFrame+type DFBackend (t :: Type) (ds :: [Nat]) = Backend I t ds (Impl.BackendFamily t ds)+ {- | The instance keeper for the `Backend` type. Using this data as a tag to the `Backend` type allows to define `Backend` instances@@ -52,6 +78,10 @@ data I ++-- The instances below are bumb stubs needed only to compile haddock+#if !defined(__HADDOCK__) && !defined(__HADDOCK_VERSION__)+ {-# ANN inferEq (ToInstance Incoherent) #-} inferEq :: forall (t :: Type) (ds :: [Nat]) (b :: Type)@@ -129,10 +159,7 @@ => Dict (PrimArray t (Backend I t ds b)) inferPrimArray = Backend.inferPrimArray @t @ds @b ----- The instances below are bumb stubs needed only to compile haddock-#if defined(__HADDOCK__) || defined(__HADDOCK_VERSION__)+#else instance forall (t :: Type) (ds :: [Nat]) (b :: Type)
− src/Numeric/DataFrame/Internal/BackendI.hs-boot
@@ -1,9 +0,0 @@-module Numeric.DataFrame.Internal.BackendI (I) where--{- | The instance keeper for the `Backend` type.-- Using this data as a tag to the `Backend` type allows to define `Backend` instances- in two different modules without any orphans.-- -}-data I
src/Numeric/DataFrame/Internal/Mutable.hs view
@@ -8,6 +8,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-}@@ -17,264 +18,511 @@ -- Copyright : (c) Artem Chirkin -- License : BSD3 ----- Maintainer : chirkin@arch.ethz.ch ----- Interfrace to perform primitive stateful operations on mutable frames.+-- Interface to perform primitive stateful operations on mutable frames. -- ----------------------------------------------------------------------------- module Numeric.DataFrame.Internal.Mutable ( MDataFrame ()+ , castDataFrame# , newDataFrame#, newPinnedDataFrame#+ , oneMoreDataFrame#+ , subDataFrameView#, subDataFrameView'# , copyDataFrame#, copyMDataFrame# , copyDataFrame'#, copyMDataFrame'#+ , copyDataFrameOff#, copyMDataFrameOff# , freezeDataFrame#, unsafeFreezeDataFrame#- , thawDataFrame#, thawPinDataFrame#, unsafeThawDataFrame#+ , thawDataFrame#, thawPinDataFrame#, unsafeThawDataFrame#, withThawDataFrame# , writeDataFrame#, writeDataFrameOff# , readDataFrame#, readDataFrameOff# , withDataFramePtr#, isDataFramePinned#+ , getDataFrameSteps# ) where - import GHC.Base import Numeric.DataFrame.Internal.PrimArray import Numeric.DataFrame.Type import Numeric.Dimensions import Numeric.PrimBytes - -- | Mutable DataFrame type. -- Keeps element offset, number of elements, and a mutable byte storage-data MDataFrame s t (ns :: [Nat])+data MDataFrame s t (ns :: [k]) = MDataFrame# Int# CumulDims (MutableByteArray# s) +-- | Allow coercing between @XNat@-indexed and @Nat@-indexed Mutable DataFrames.+castDataFrame# ::+ forall (t :: Type) (xns :: [XNat]) (ns :: [Nat]) s+ . FixedDims xns ns+ => MDataFrame s t ns -> MDataFrame s t xns+castDataFrame# (MDataFrame# o c a) = MDataFrame# o c a+{-# INLINE castDataFrame# #-} -- | Create a new mutable DataFrame.-newDataFrame# :: forall t (ns :: [Nat]) s- . ( PrimBytes t, Dimensions ns)- => State# s -> (# State# s, MDataFrame s t ns #)-newDataFrame# s0+newDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . (PrimBytes t, Dimensions ns)+ => State# s -> (# State# s, MDataFrame s t ns #)+newDataFrame# | steps <- cumulDims $ dims @ns , n <- cdTotalDim# steps- , (# s1, mba #) <- newByteArray# (n *# byteSize @t undefined) s0- = (# s1, MDataFrame# 0# steps mba #)+ = if isTrue# (n ==# 0#)+ then \s0 -> (# s0, error "Empty DataFrame (DF0)" #)+ else \s0 -> case newByteArray# (n *# byteSize @t undefined) s0 of+ (# s1, mba #) -> (# s1, MDataFrame# 0# steps mba #) {-# INLINE newDataFrame# #-} -- | Create a new mutable DataFrame.-newPinnedDataFrame# :: forall t (ns :: [Nat]) s- . ( PrimBytes t, Dimensions ns)- => State# s -> (# State# s, MDataFrame s t ns #)-newPinnedDataFrame# s0+newPinnedDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . (PrimBytes t, Dimensions ns)+ => State# s -> (# State# s, MDataFrame s t ns #)+newPinnedDataFrame# | steps <- cumulDims $ dims @ns , n <- cdTotalDim# steps- , (# s1, mba #) <- newAlignedPinnedByteArray#- (n *# byteSize @t undefined)- (byteAlign @t undefined) s0- = (# s1, MDataFrame# 0# steps mba #)+ = if isTrue# (n ==# 0#)+ then \s0 -> (# s0, error "Empty DataFrame (DF0)" #)+ else \s0 -> case newAlignedPinnedByteArray#+ (n *# byteSize @t undefined)+ (byteAlign @t undefined) s0 of+ (# s1, mba #) -> (# s1, MDataFrame# 0# steps mba #) {-# INLINE newPinnedDataFrame# #-} +-- | Create a new mutable DataFrame of the same size.+oneMoreDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . MDataFrame s t ns -> State# s -> (# State# s, MDataFrame s t ns #)+oneMoreDataFrame# mdf@(MDataFrame# off steps mba) s0+ | 0# <- cdTotalDim# steps = (# s0, mdf #)+ | (# s1, bs #) <- getSizeofMutableByteArray# mba s0+ , (# s2, mba' #) <- newByteArray# (bs -# off) s1+ = (# s2, MDataFrame# 0# steps mba' #)+{-# INLINE oneMoreDataFrame# #-}++-- | View a part of a DataFrame.+--+-- This function does not perform a copy.+-- All changes to a new DataFrame will be reflected in the original DataFrame as well.+--+-- If any of the dims in @as@ or @b@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+subDataFrameView# ::+ forall (t :: Type) (k :: Type)+ (b :: k) (bi :: k) (bd :: k)+ (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs)+ => Idxs (as +: bi) -> MDataFrame s t asbs -> MDataFrame s t (bd :+ bs)+subDataFrameView# ei (MDataFrame# offM stepsM arr)+ = MDataFrame# (case offA of I# i -> i) stepsA arr+ where+ (offA, stepsA) = getOffAndStepsSub (I# offM) stepsM ei (dim @bd)++-- | View a part of a DataFrame.+--+-- This function does not perform a copy.+-- All changes to a new DataFrame will be reflected in the original DataFrame as well.+--+-- This is a simpler version of @subDataFrameView@ that allows+-- to view over one index at a time.+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+subDataFrameView'# ::+ forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . ConcatList as bs asbs+ => Idxs as -> MDataFrame s t asbs -> MDataFrame s t bs+subDataFrameView'# ei (MDataFrame# offM stepsM arr)+ = MDataFrame# (case offA of I# i -> i) stepsA arr+ where+ (offA, stepsA) = getOffAndSteps (I# offM) stepsM ei+ -- | Copy one DataFrame into another mutable DataFrame at specified position. ----- In contrast to @copyDataFrame'@, this function allows to copy over a range of contiguous--- indices over a single dimension.+-- In contrast to @copyDataFrame'@, this function allows to copy over a range+-- of contiguous indices over a single dimension. -- For example, you can write a 3x4 matrix into a 7x4 matrix, starting at indices 0..3.-copyDataFrame# :: forall (t :: Type)- (b :: Nat) (bi :: Nat) (bd :: Nat)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( b ~ (bi + bd - 1)- , PrimBytes t- , PrimBytes (DataFrame t (bd :+ bs))- , ConcatList as (b :+ bs) asbs- )- => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> MDataFrame s t asbs- -> State# s -> (# State# s, () #)-copyDataFrame# ei df (MDataFrame# off steps mba) s- | I# i <- cdIx steps ei- = (# writeBytes mba ((off +# i) *# byteSize @t undefined) df s, () #)+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function copies only as+-- many elements as fits into the dataframe along this dimension (possibly none).+copyDataFrame# ::+ forall (t :: Type) (k :: Type)+ (b :: k) (bi :: k) (bd :: k)+ (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . ( SubFrameIndexCtx b bi bd, KnownDim bd+ , ExactDims bs+ , PrimArray t (DataFrame t (bd :+ bs))+ , ConcatList as (b :+ bs) asbs )+ => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> MDataFrame s t asbs+ -> State# s -> (# State# s, () #)+copyDataFrame# ei df (MDataFrame# offM stepsM arrDest)+ | elS <- byteSize @t undefined+ , Just (I# offDest, stepsB)+ <- getOffAndStepsSubM (I# offM) stepsM ei (dim @bd)+ , n <- cdTotalDim# stepsB+ , isTrue# (n ># 0#) -- is there enough space to write anything?+ = withArrayContent+ (\e s -> (# fillArray arrDest offDest n e s, () #))+ (\_ offSrc arrSrc s ->+ (# copyByteArray# arrSrc (offSrc *# elS)+ arrDest (offDest *# elS) (n *# elS) s, () #)) df+ | otherwise = \s -> (# s, () #) {-# INLINE copyDataFrame# #-} - {-# ANN copyMDataFrame# "HLint: ignore Use camelCase" #-} -- | Copy one mutable DataFrame into another mutable DataFrame at specified position. ----- In contrast to @copyMDataFrame'@, this function allows to copy over a range of contiguous--- indices over a single dimension.+-- In contrast to @copyMutableDataFrame'@, this function allows to copy over a range+-- of contiguous indices over a single dimension. -- For example, you can write a 3x4 matrix into a 7x4 matrix, starting at indices 0..3.-copyMDataFrame# :: forall (t :: Type)- (b :: Nat) (bi :: Nat) (bd :: Nat)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( b ~ (bi + bd - 1)- , PrimBytes t- , ConcatList as (b :+ bs) asbs- )- => Idxs (as +: bi) -> MDataFrame s t (bd :+ bs) -> MDataFrame s t asbs- -> State# s -> (# State# s, () #)- -- all information for copying in this case is taken from the inside- -- of the data types at the term level;- -- steps zips with indices just fine independently of their types- -- and length.- -- Thus, it's fine here to just unsafeCoerce# an index.-copyMDataFrame# ei df =- copyMDataFrame'#- (unsafeCoerce# ei :: Idxs as)- (unsafeCoerce# df :: MDataFrame s t (b :+ bs))+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function copies only as+-- many elements as fits into the dataframe along this dimension (possibly none).+copyMDataFrame# ::+ forall (t :: Type) (k :: Type)+ (b :: k) (bi :: k) (bd :: k)+ (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . ( SubFrameIndexCtx b bi bd+ , ExactDims bs+ , PrimBytes t+ , ConcatList as (b :+ bs) asbs )+ => Idxs (as +: bi) -> MDataFrame s t (bd :+ bs) -> MDataFrame s t asbs+ -> State# s -> (# State# s, () #)+copyMDataFrame# ei (MDataFrame# offA (CumulDims ~(bb:b:_)) arrA)+ (MDataFrame# offM stepsM arrM)+ | elS <- byteSize @t undefined+ , Just (I# offDest, stepsB)+ <- getOffAndStepsSubM (I# offM) stepsM ei (unsafeCoerce# (quot bb b))+ , n <- cdTotalDim# stepsB+ , isTrue# (n ># 0#) -- is there enough space to write anything?+ = \s -> (# copyMutableByteArray# arrA (offA *# elS)+ arrM (offDest *# elS) (n *# elS) s+ , () #)+ | otherwise = \s -> (# s, () #) {-# INLINE copyMDataFrame# #-} {-# ANN copyDataFrame'# "HLint: ignore Use camelCase" #-} -- | Copy one DataFrame into another mutable DataFrame at specified position. ----- This is a simpler version of @copyDataFrame@ that allows to copy over one index at a time.-copyDataFrame'# :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( PrimBytes t- , PrimBytes (DataFrame t bs)- , ConcatList as bs asbs- )- => Idxs as -> DataFrame t bs -> MDataFrame s t asbs- -> State# s -> (# State# s, () #)-copyDataFrame'# ei df (MDataFrame# off steps mba) s- | I# i <- cdIx steps ei- = (# writeBytes mba ((off +# i) *# byteSize @t undefined) df s, () #)+-- This is a simpler version of @copyDataFrame@ that allows+-- to copy over one index at a time.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+copyDataFrame'# ::+ forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . ( ExactDims bs+ , PrimArray t (DataFrame t bs)+ , ConcatList as bs asbs )+ => Idxs as -> DataFrame t bs -> MDataFrame s t asbs+ -> State# s -> (# State# s, () #)+copyDataFrame'# ei df (MDataFrame# offM stepsM arrDest)+ | elS <- byteSize @t undefined+ , Just (I# offDest, stepsA) <- getOffAndStepsM (I# offM) stepsM ei+ , n <- cdTotalDim# stepsA+ = withArrayContent+ (\e s -> (# fillArray arrDest offDest n e s, () #))+ (\_ offSrc arrSrc s ->+ (# copyByteArray# arrSrc (offSrc *# elS)+ arrDest (offDest *# elS) (n *# elS) s, () #)) df+ | otherwise = \s -> (# s, () #) {-# INLINE copyDataFrame'# #-} {-# ANN copyMDataFrame'# "HLint: ignore Use camelCase" #-} -- | Copy one mutable DataFrame into another mutable DataFrame at specified position. ----- This is a simpler version of @copyMDataFrame@ that allows to copy over one index at a time.-copyMDataFrame'# :: forall (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( PrimBytes t- , ConcatList as bs asbs- )- => Idxs as -> MDataFrame s t bs -> MDataFrame s t asbs- -> State# s -> (# State# s, () #)-copyMDataFrame'# ei (MDataFrame# offA stepsA arrA) (MDataFrame# offM stepsM arrM) s+-- This is a simpler version of @copyMutableDataFrame@ that allows+-- to copy over one index at a time.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+copyMDataFrame'# ::+ forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . (ExactDims bs, PrimBytes t, ConcatList as bs asbs)+ => Idxs as -> MDataFrame s t bs -> MDataFrame s t asbs+ -> State# s -> (# State# s, () #)+copyMDataFrame'# ei (MDataFrame# offA stepsA arrA) (MDataFrame# offM stepsM arrM) | elS <- byteSize @t undefined , lenA <- cdTotalDim# stepsA- , I# i <- cdIx stepsM ei- = (# copyMutableByteArray# arrA (offA *# elS)- arrM ((offM +# i) *# elS) (lenA *# elS) s- , () #)+ , Just (I# i) <- cdIxM stepsM ei+ = \s -> (# copyMutableByteArray#+ arrA (offA *# elS)+ arrM ((offM +# i) *# elS) (lenA *# elS) s, () #)+ | otherwise = \s -> (# s, () #) {-# INLINE copyMDataFrame'# #-} +-- | Copy one DataFrame into another mutable DataFrame by offset in+-- primitive elements.+--+-- This is a low-level copy function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+copyDataFrameOff# ::+ forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . ( Dimensions bs+ , PrimArray t (DataFrame t bs)+ , ConcatList as bs asbs )+ => Int -> DataFrame t bs -> MDataFrame s t asbs+ -> State# s -> (# State# s, () #)+copyDataFrameOff# (I# off) df (MDataFrame# offM _ arrDest)+ | elS <- byteSize @t undefined+ , offDest <- offM +# off+ = withArrayContent+ (\e s ->+ (# fillArray arrDest offDest+ (case totalDim (dims @bs) of W# n -> word2Int# n) e s, () #))+ (\steps offSrc arrSrc s ->+ (# copyByteArray# arrSrc (offSrc *# elS)+ arrDest (offDest *# elS)+ (cdTotalDim# steps *# elS) s, () #)) df+{-# INLINE copyDataFrameOff# #-}++-- | Copy one mutable DataFrame into another mutable DataFrame by offset in+-- primitive elements.+--+-- This is a low-level copy function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+copyMDataFrameOff# ::+ forall (t :: Type) (k :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k]) s+ . (ExactDims bs, PrimBytes t, ConcatList as bs asbs)+ => Int -> MDataFrame s t bs -> MDataFrame s t asbs+ -> State# s -> (# State# s, () #)+copyMDataFrameOff# (I# off) (MDataFrame# offA stepsA arrA)+ (MDataFrame# offM _ arrM)+ | elS <- byteSize @t undefined+ , lenA <- cdTotalDim# stepsA+ = \s -> (# copyMutableByteArray#+ arrA (offA *# elS)+ arrM ((offM +# off) *# elS) (lenA *# elS) s, () #)+{-# INLINE copyMDataFrameOff# #-}+ -- | Make a mutable DataFrame immutable, without copying.-unsafeFreezeDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . PrimArray t (DataFrame t ns)- => MDataFrame s t ns- -> State# s -> (# State# s, DataFrame t ns #)-unsafeFreezeDataFrame# (MDataFrame# offM steps arrM) s1- | (# s2, arrA #) <- unsafeFreezeByteArray# arrM s1- = (# s2, fromElems steps offM arrA #)+unsafeFreezeDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . PrimArray t (DataFrame t ns)+ => MDataFrame s t ns+ -> State# s -> (# State# s, DataFrame t ns #)+unsafeFreezeDataFrame# (MDataFrame# offM steps arrM) s0+ | 0# <- cdTotalDim# steps+ = (# s0, error "Empty DataFrame (DF0)" #)+ | (# s1, arrA #) <- unsafeFreezeByteArray# arrM s0+ = (# s1, fromElems steps offM arrA #) {-# INLINE unsafeFreezeDataFrame# #-} -- | Copy content of a mutable DataFrame into a new immutable DataFrame.-freezeDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . PrimArray t (DataFrame t ns)- => MDataFrame s t ns -> State# s -> (# State# s, DataFrame t ns #)+freezeDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . PrimArray t (DataFrame t ns)+ => MDataFrame s t ns -> State# s -> (# State# s, DataFrame t ns #) freezeDataFrame# (MDataFrame# offM steps arrM) s0+ | 0# <- cdTotalDim# steps+ = (# s0, error "Empty DataFrame (DF0)" #) | elS <- byteSize @t undefined , n <- cdTotalDim# steps , (# s1, mba #) <- newByteArray# (n *# elS) s0 , s2 <- copyMutableByteArray# arrM (offM *# elS) mba 0# (n *# elS) s1 , (# s3, arrA #) <- unsafeFreezeByteArray# mba s2- = (# s3, fromElems steps 0# arrA #)+ = (# s3, fromElems steps 0# arrA #) {-# INLINE freezeDataFrame# #-} -- | Create a new mutable DataFrame and copy content of immutable one in there.-thawDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . ( Dimensions ns- , PrimBytes (DataFrame t ns))- => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)+thawDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #) thawDataFrame# df s0- | arrA <- getBytes df- , boff <- byteOffset df- , bsize <- byteSize df- , steps <- cumulDims $ dims @ns- , (# s1, arrM #) <- newByteArray# bsize s0- , s2 <- copyByteArray# arrA boff arrM 0# bsize s1- = (# s2, MDataFrame# 0# steps arrM #)+ | nw == 0+ = (# s0, error "Empty DataFrame (DF0)" #)+ | bSize <- case nw of W# w -> byteSize @t undefined *# word2Int# w+ , (# s1, arrM #) <- newByteArray# bSize s0+ , r <- MDataFrame# 0# steps arrM+ , (# s2, _ #) <- copyDataFrameOff# 0 df r s1+ = (# s2, r #)+ where+ nw = cdTotalDim steps+ steps = getSteps (dims @ns) df {-# INLINE thawDataFrame# #-} -- | Create a new mutable DataFrame and copy content of immutable one in there. -- The result array is pinned and aligned.-thawPinDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . ( Dimensions ns- , PrimBytes (DataFrame t ns))- => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #)+thawPinDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns -> State# s -> (# State# s, MDataFrame s t ns #) thawPinDataFrame# df s0- | arrA <- getBytes df- , boff <- byteOffset df- , bsize <- byteSize df- , steps <- cumulDims $ dims @ns- , (# s1, arrM #) <- newAlignedPinnedByteArray# bsize (byteAlign df) s0- , s2 <- copyByteArray# arrA boff arrM 0# bsize s1- = (# s2, MDataFrame# 0# steps arrM #)+ | nw == 0+ = (# s0, error "Empty DataFrame (DF0)" #)+ | bSize <- case nw of W# w -> byteSize @t undefined *# word2Int# w+ , (# s1, arrM #) <- newAlignedPinnedByteArray# bSize (byteAlign @t undefined) s0+ , r <- MDataFrame# 0# steps arrM+ , (# s2, _ #) <- copyDataFrameOff# 0 df r s1+ = (# s2, r #)+ where+ nw = cdTotalDim steps+ steps = getSteps (dims @ns) df {-# INLINE thawPinDataFrame# #-} -- | UnsafeCoerces an underlying byte array.-unsafeThawDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . ( Dimensions ns- , PrimBytes (DataFrame t ns), PrimBytes t)- => DataFrame t ns- -> State# s -> (# State# s, MDataFrame s t ns #)-unsafeThawDataFrame# df s0- | elS <- byteSize @t undefined- , arrA <- getBytes df- , boff <- byteOffset df- , steps <- cumulDims $ dims @ns- = (# s0- , MDataFrame# (quotInt# boff elS) steps (unsafeCoerce# arrA)- #)+unsafeThawDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns+ -> State# s -> (# State# s, MDataFrame s t ns #)+unsafeThawDataFrame# = withArrayContent f g+ where+ f :: t -> State# s -> (# State# s, MDataFrame s t ns #)+ f e s0+ | steps <- cumulDims (dims @ns)+ , n <- cdTotalDim# steps+ , (# s1, arrM #) <- newByteArray# (n *# byteSize @t undefined) s0+ = (# fillArray arrM 0# n e s1, MDataFrame# 0# steps arrM #)+ g :: CumulDims -> Int# -> ByteArray# -> State# s -> (# State# s, MDataFrame s t ns #)+ g steps off ba s0+ = (# s0, MDataFrame# off steps (unsafeCoerce# ba) #) {-# INLINE unsafeThawDataFrame# #-} +-- | Given two continuations @f@ and @g@.+-- If the input DataFrame is a single broadcast value, use it in @f@.+-- Otherwise, create a new mutable DataFrame and copy content of immutable one+-- in there; then use it in @g@.+--+-- This function is useful when @thawDataFrame@ cannot be used due to+-- @Dimensions ns@ constraint being not available.+withThawDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) (r :: Type) s+ . PrimArray t (DataFrame t ns)+ => (t -> State# s -> (# State# s, r #)) -- ^ f+ -> (MDataFrame s t ns -> State# s -> (# State# s, r #)) -- ^ g+ -> DataFrame t ns+ -> State# s -> (# State# s, r #)+withThawDataFrame# f g = withArrayContent f g'+ where+ g' :: CumulDims -> Int# -> ByteArray# -> State# s -> (# State# s, r #)+ g' steps eOff arrA s0 = case cdTotalDim# steps of+ 0# -> g (error "Empty DataFrame (DF0)") s0+ elems+ | elS <- byteSize @t undefined+ , bsize <- elS *# elems+ , (# s1, arrM #) <- newByteArray# bsize s0+ , s2 <- copyByteArray# arrA (eOff *# elS) arrM 0# bsize s1+ -> g (MDataFrame# 0# steps arrM) s2+{-# INLINE withThawDataFrame# #-} --- | Write a single element at the specified element offset-writeDataFrameOff# :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => MDataFrame s t ns -> Int# -> t -> State# s -> (# State# s, () #)-writeDataFrameOff# (MDataFrame# off _ mba) i x s+-- | Write a single element at the specified element offset.+--+-- This is a low-level write function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+writeDataFrameOff# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . PrimBytes (DataFrame t ('[] :: [k]))+ => MDataFrame s t ns -> Int -> DataFrame t ('[] :: [k])+ -> State# s -> (# State# s, () #)+writeDataFrameOff# (MDataFrame# off _ mba) (I# i) x s = (# writeArray mba (off +# i) x s, () #) {-# INLINE writeDataFrameOff# #-} --- | Write a single element at the specified index-writeDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => MDataFrame s t ns -> Idxs ns -> t -> State# s -> (# State# s, () #)+-- | Write a single element at the specified index.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+writeDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . PrimBytes (DataFrame t ('[] :: [k]))+ => MDataFrame s t ns -> Idxs ns -> DataFrame t ('[] :: [k])+ -> State# s -> (# State# s, () #) writeDataFrame# mdf@(MDataFrame# _ st _) ei- | I# i <- cdIx st ei = writeDataFrameOff# mdf i+ | Just off <- (cdIxM st ei)+ = writeDataFrameOff# mdf off+ | otherwise = const (\s -> (# s, () #)) {-# INLINE writeDataFrame# #-} --- | Read a single element at the specified element offset-readDataFrameOff# :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => MDataFrame s t ns -> Int# -> State# s -> (# State# s, t #)-readDataFrameOff# (MDataFrame# off _ mba) i = readArray mba (off +# i)+-- | Read a single element at the specified element offset.+--+-- This is a low-level read function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you read beyond the DataFrame bounds.+readDataFrameOff# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . PrimBytes (DataFrame t ('[] :: [k]))+ => MDataFrame s t ns -> Int+ -> State# s -> (# State# s, DataFrame t ('[] :: [k]) #)+readDataFrameOff# (MDataFrame# off _ mba) (I# i)+ = readArray @(DataFrame t ('[] :: [k])) mba (off +# i) {-# INLINE readDataFrameOff# #-} --- | Read a single element at the specified index-readDataFrame# :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => MDataFrame s t ns -> Idxs ns -> State# s -> (# State# s, t #)+-- | Read a single element at the specified index.+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+readDataFrame# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . PrimBytes (DataFrame t ('[] :: [k]))+ => MDataFrame s t ns -> Idxs ns+ -> State# s -> (# State# s, DataFrame t ('[] :: [k]) #) readDataFrame# mdf@(MDataFrame# _ st _) ei- | I# i <- cdIx st ei = readDataFrameOff# mdf i+ = readDataFrameOff# mdf (cdIx st ei) {-# INLINE readDataFrame# #-} -- | Allow arbitrary operations on a pointer to the beginning of the data. -- Only possible with @RealWord@ state (thus, in @IO@) due to semantics of -- @touch#@ operation that keeps the data from being garbage collected.-withDataFramePtr# :: forall (t :: Type) (ns :: [Nat]) (r :: Type)- . PrimBytes t- => MDataFrame RealWorld t ns- -> ( Addr# -> State# RealWorld -> (# State# RealWorld, r #) )- -> State# RealWorld -> (# State# RealWorld, r #)+withDataFramePtr# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) (r :: Type)+ . PrimBytes t+ => MDataFrame RealWorld t ns+ -> (Addr# -> State# RealWorld -> (# State# RealWorld, r #))+ -> State# RealWorld -> (# State# RealWorld, r #) withDataFramePtr# (MDataFrame# off _ mba) k s0 | (# s1, a #) <- unsafeFreezeByteArray# mba s0 , (# s2, r #) <- k ( byteArrayContents# a `plusAddr#` (off *# byteSize @t undefined) ) s1- = (# touch# mba s2, r #)+ = (# touch# mba s2, r #)+{-# INLINE withDataFramePtr# #-} -- | Check if the byte array wrapped by this DataFrame is pinned, -- which means cannot be relocated by GC.-isDataFramePinned# :: forall (t :: Type) (ns :: [Nat]) s- . MDataFrame s t ns -> Bool+isDataFramePinned# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s . MDataFrame s t ns -> Bool isDataFramePinned# (MDataFrame# _ _ mba) = isTrue# (isMutableByteArrayPinned# mba)++-- | Get cumulative dimensions @ns@ of a @MDataFrame s t ns@+getDataFrameSteps# ::+ forall (t :: Type) (k :: Type) (ns :: [k]) s+ . MDataFrame s t ns -> CumulDims+getDataFrameSteps# (MDataFrame# _ c _) = c+{-# INLINE getDataFrameSteps# #-}++-- | Fill a mutable byte array with the same single element+fillArray :: PrimBytes t+ => MutableByteArray# s+ -> Int# -- ^ Offset in elements+ -> Int# -- ^ Number of elements+ -> t+ -> State# s -> State# s+fillArray mba off n e+ = let lim = off +# n+ go i s | isTrue# (i >=# lim) = s+ | otherwise = go (i +# 1#) (writeArray mba i e s)+ in go off+{-# INLINE fillArray #-}
src/Numeric/DataFrame/Internal/PrimArray.hs view
@@ -1,27 +1,40 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitForAll #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-} module Numeric.DataFrame.Internal.PrimArray ( PrimArray (..), CumulDims (..)- , cumulDims, cdTotalDim, cdTotalDim#, cdIx+ , cumulDims, cdTotalDim, cdTotalDim#+ , cdIx, cdIxSub, getOffAndSteps, getOffAndStepsSub+ , cdIxM, getOffAndStepsM, getOffAndStepsSubM , ixOff, unsafeFromFlatList, getSteps, fromSteps+ , withArrayContent, fromElems, broadcast ) where -import Data.Monoid as Mon (Monoid (..))-import Data.Semigroup as Sem (Semigroup (..))-import GHC.Base (ByteArray#, Int (..), Int#, Word (..), word2Int#)+import Control.Arrow ((***))+import Data.Monoid as Mon (Monoid (..))+import Data.Semigroup as Sem (Semigroup (..))+import GHC.Base hiding (foldr)+ -- (ByteArray#, Int (..), Int#, RuntimeRep (..), Type,+ -- Word (..), inline, runRW#, touch#, word2Int#, (+#))+import GHC.Exts (TYPE) import Numeric.Dimensions import Numeric.PrimBytes + -- | Given @Dims ns@, @CumulativeDims@ is a list of length @Length ns + 1@; -- which cumulative @totalDim@ accumulated on the right. -- In particular, its first element is @totalDim ds@, -- its last element is always is always @1@. newtype CumulDims = CumulDims { unCumulDims :: [Word] }+ deriving Show instance Sem.Semigroup CumulDims where CumulDims as <> CumulDims bs = CumulDims $ map (head bs *) (init as) ++ bs@@ -49,16 +62,128 @@ -- -- Note, you can take offset of subspace with CumulDims of larger space -- - very convenient!+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system. cdIx :: CumulDims -> Idxs ns -> Int-cdIx ~(CumulDims ~(_:steps))- = fromIntegral . sum . zipWith (*) steps . listIdxs+cdIx steps+ = fromIntegral . fst . getOffAndSteps' 0 0 (unCumulDims steps) . listIdxs +-- | Calculate offset of an Idxs.+--+-- Also check if the last index plus dimVal of subN is not bigger than the+-- corresponding dim inside CumulDims; throw an `OutOfDimBounds` otherwise.+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+cdIxSub :: CumulDims -> Idxs (ns +: idxN) -> Dim subN -> Int+cdIxSub steps idxs d+ = fromIntegral+ . fst . getOffAndSteps' (dimVal d) 0 (unCumulDims steps) $ listIdxs idxs++-- | Calculate offset of an Idxs and return remaining CumulDims.+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+getOffAndSteps :: Int -- ^ Initial offset+ -> CumulDims -> Idxs ns -> (Int, CumulDims)+getOffAndSteps off0 steps+ = (fromIntegral *** CumulDims)+ . getOffAndSteps' 0 (fromIntegral off0) (unCumulDims steps) . listIdxs++-- | Calculate offset of an Idxs and return remaining CumulDims.+--+-- Also check if the last index plus dimVal of subN is not bigger than the+-- corresponding dim inside CumulDims; throw an `OutOfDimBounds` otherwise.+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+getOffAndStepsSub :: Int -- ^ Initial offset+ -> CumulDims -> Idxs (ns +: idxN)+ -> Dim subN -> (Int, CumulDims)+getOffAndStepsSub off0 steps idxs d+ = (fromIntegral *** CumulDims)+ . getOffAndSteps' (dimVal d) (fromIntegral off0) (unCumulDims steps)+ $ listIdxs idxs+++getOffAndSteps' :: Word -> Word -> [Word] -> [Word] -> (Word, [Word])+getOffAndSteps' sub0 off0 steps0 is0 = go sub0 off0 steps0 is0+ where+#ifndef UNSAFE_INDICES+ ds0 = case fromSteps (CumulDims steps0) of SomeDims x -> listDims x+#endif+ go :: Word -> Word -> [Word] -> [Word] -> (Word, [Word])+ go 0 off steps [] = (off, steps)+ go sub off ~(steps@(s:_)) [] = (off, sub*s : steps)+#ifndef UNSAFE_INDICES+ go sub _ ~(bs:(s:_)) [i]+ | b <- quot bs s+ , sub > 0 && i + sub > b+ = outOfDimBoundsNoCallStack+ "{Calculating SubDataFrame offset}"+ i b (Just sub) (Just (ds0, is0))+ go _ _ ~(bs:(s:_)) (i:_)+ | b <- quot bs s+ , i >= b+ = outOfDimBoundsNoCallStack+ "{Calculating SubDataFrame offset}"+ i b Nothing (Just (ds0, is0))+#endif+ go sub off ~(_:steps@(s:_)) (i:ixs)+ = go sub (off + i*s) steps ixs+{-# INLINE getOffAndSteps' #-}++++-- | Same as `cdIx`, but safe; returns @Nothing@ if out of bounds.+cdIxM :: CumulDims -> Idxs ns -> Maybe Int+cdIxM steps+ = fmap (fromIntegral . fst)+ . getOffAndStepsM' 0 0 (unCumulDims steps) . listIdxs++-- | Same as `getOffAndSteps`, but safe; returns @Nothing@ if out of bounds.+-- Trims the first (slicing) dimension of the returned CumulDims to fit+-- the original dataframe if necessary.+getOffAndStepsM :: Int -- ^ Initial offset+ -> CumulDims -> Idxs ns -> Maybe (Int, CumulDims)+getOffAndStepsM off0 steps+ = fmap (fromIntegral *** CumulDims)+ . getOffAndStepsM' 0 (fromIntegral off0) (unCumulDims steps) . listIdxs++-- | Same as `getOffAndStepsSub`, but safe; returns @Nothing@ if out of bounds.+-- Trims the first (slicing) dimension of the returned CumulDims to fit+-- the original dataframe if necessary.+getOffAndStepsSubM :: Int -- ^ Initial offset+ -> CumulDims -> Idxs (ns +: idxN)+ -> Dim subN -> Maybe (Int, CumulDims)+getOffAndStepsSubM off0 steps idxs d+ = fmap (fromIntegral *** CumulDims)+ . getOffAndStepsM' (dimVal d) (fromIntegral off0) (unCumulDims steps)+ $ listIdxs idxs++getOffAndStepsM' :: Word -> Word -> [Word] -> [Word] -> Maybe (Word, [Word])+getOffAndStepsM' = go+ where+ go :: Word -> Word -> [Word] -> [Word] -> Maybe (Word, [Word])+ go _ off steps [] = Just (off, steps)+ go sub off ~(bs:steps@(s:_)) (i:ixs)+ | is >= bs = Nothing+ | not (null ixs) = go sub (off + is) steps ixs+ | sub == 0 = Just (off + is, steps)+ | otherwise = Just (off + is, min (bs - is) (sub*s) : steps)+ where+ is = i*s+{-# INLINE getOffAndStepsM' #-}+ -- | Try to get @CumulDims@ from an array, -- and create it using @Dims@ if failed. getSteps :: PrimArray t a => Dims (ns :: [k]) -> a -> CumulDims-getSteps dds df = case uniqueOrCumulDims df of- Left _ -> cumulDims dds- Right ds -> ds+getSteps dds = withArrayContent (const $ cumulDims dds) (\cd _ _ -> cd) {-# INLINE getSteps #-} -- | Get @Dims@ by "de-accumulating" @CumulDims@.@@ -75,9 +200,14 @@ class PrimBytes t => PrimArray t a | a -> t where -- | Broadcast element into array- broadcast :: t -> a+ --+ -- Warning: do not use this function at the call site; use `broadcast`+ -- instead. Otherwise you will miss some rewrite rules.+ broadcast# :: t -> a -- | Index an array given an offset ix# :: Int# -> a -> t+ ix# i = withArrayContent id (\_ off arr -> indexArray arr (off +# i))+ {-# INLINE ix# #-} -- | Generate an array using an accumulator funtion gen# :: CumulDims -- ^ Dimensionality of the result array;@@ -92,17 +222,26 @@ -- types and is not checked at runtime. -> Int# -> t -> a -> a - -- | If the array represented as a single broadcasted value,- -- return this value.- -- Otherwise, return full array content:+ -- | If the array is represented as a single broadcasted value, return this+ -- this value. Otherwise, return the full array content: -- @CumulDims@, array offset (elements), byte array with the content.- arrayContent# :: a -> (# t | (# CumulDims, Int#, ByteArray# #) #)+ --+ -- Warning: never use this function directly. Use `withArrayContent` instead.+ -- There is a bug in GHC 8.6, such that certain optimizations+ -- (probably, instance specialization/rewrite rules) break the code,+ -- which is only observable at runtime. The effect is that the+ -- content of a `ByteArray#` becomes a garbage. The workaround is+ -- to use a non-inlinable wrapper to disable these optimizations.+ -- In addition, the wrapper function has some rewrite rules, which+ -- can potentially improve performance with other GHC versions.+ withArrayContent# :: forall (rep :: RuntimeRep) (r :: TYPE rep)+ . (t -> r)+ -> (CumulDims -> Int# -> ByteArray# -> r)+ -> a -> r -- | Offset of an array as a number of elements offsetElems :: a -> Int#- offsetElems a = case arrayContent# a of- (# _ | #) -> 0#- (# | (# _, o, _ #) #) -> o+ offsetElems a = withArrayContent (\_ f -> f 0#) (const $ \o _ f -> f o) a (\i -> i) {-# INLINE offsetElems #-} -- | Normally, this returns a cumulative @totalDim@s.@@ -114,18 +253,25 @@ -- Note, this function returns the only unique element only if it is -- a such by construction (there is no equality checks involved). uniqueOrCumulDims :: a -> Either t CumulDims- uniqueOrCumulDims a = case arrayContent# a of- (# x | #) -> Left x- (# | (# cd, _, _ #) #) -> Right cd+ uniqueOrCumulDims = withArrayContent Left (\cd _ _ -> Right cd) {-# INLINE uniqueOrCumulDims #-} - -- | Get array by its offset and cumulative dims in a ByteArray.+ -- | Define an array by its offset and cumulative dims in a ByteArray. -- Both offset and dims are given in element number (not in bytes). --- -- It is better to use this function instead of @fromBytes@ to avoid- -- recalculating @CumulDims@ for implementations that require it.- fromElems :: CumulDims -> Int# -> ByteArray# -> a+ -- Warning: never use this function directly. Use `fromElems` instead.+ -- There is a bug in GHC 8.6, such that certain optimizations+ -- (probably, instance specialization/rewrite rules) break the code,+ -- which is only observable at runtime. The effect is that the+ -- content of a `ByteArray#` becomes a garbage. The workaround is+ -- to use a non-inlinable wrapper to disable these optimizations.+ -- In addition, the wrapper function has some rewrite rules, which+ -- can potentially improve performance with other GHC versions.+ fromElems# :: CumulDims -> Int# -> ByteArray# -> a +{-# WARNING fromElems# "Please, use fromElems instead." #-}+{-# WARNING withArrayContent# "Please, use withArrayContent instead." #-}+ -- | Index array by an integer offset (starting from 0). ixOff :: PrimArray t a => Int -> a -> t ixOff (I# i) = ix# i@@ -138,3 +284,51 @@ where f [] = (# [], x0 #) f (x:xs) = (# xs, x #)++-- | If the array is represented as a single broadcasted value, return this+-- this value. Otherwise, return the full array content:+-- @CumulDims@, array offset (elements), byte array with the content.+withArrayContent :: forall (t :: Type) (a :: Type)+ (rep :: RuntimeRep) (r :: TYPE rep)+ . PrimArray t a+ => (t -> r)+ -> (CumulDims -> Int# -> ByteArray# -> r)+ -> a -> r+withArrayContent = withArrayContent#+#if __GLASGOW_HASKELL__ == 806+{-# NOINLINE withArrayContent #-}+#else+{-# INLINE[1] withArrayContent #-}+#endif++-- | Define an array by its offset and cumulative dims in a ByteArray.+-- Both offset and dims are given in element number (not in bytes).+--+-- It is better to use this function instead of @fromBytes@ to avoid+-- recalculating @CumulDims@ for implementations that require it.+fromElems :: forall (t :: Type) (a :: Type)+ . PrimArray t a => CumulDims -> Int# -> ByteArray# -> a+fromElems = fromElems#+#if __GLASGOW_HASKELL__ == 806+{-# NOINLINE fromElems #-}+#else+{-# INLINE[1] fromElems #-}+#endif++-- | Broadcast element into array+broadcast :: forall (t :: Type) (a :: Type)+ . PrimArray t a => t -> a+broadcast = broadcast#+{-# INLINE[1] broadcast #-}++{-# RULES++"withArrayContent/id"+ withArrayContent broadcast fromElems = id++"withArrayContent+fromElems" forall f g cd off ba .+ withArrayContent f g (fromElems cd off ba) = g cd off ba++"withArrayContent+broadcast" forall f g e .+ withArrayContent f g (broadcast e) = f e+ #-}
src/Numeric/DataFrame/ST.hs view
@@ -2,14 +2,18 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ViewPatterns #-} ----------------------------------------------------------------------------- -- | -- Module : Numeric.DataFrame.ST@@ -22,203 +26,344 @@ module Numeric.DataFrame.ST ( STDataFrame (XSTFrame), SomeSTDataFrame (..)- , newDataFrame, newPinnedDataFrame+ , castDataFrame+ , newDataFrame, newPinnedDataFrame, oneMoreDataFrame+ , subDataFrameView, subDataFrameView' , copyDataFrame, copyMutableDataFrame , copyDataFrame', copyMutableDataFrame'+ , copyDataFrameOff, copyMutableDataFrameOff , freezeDataFrame, unsafeFreezeDataFrame- , thawDataFrame, thawPinDataFrame, unsafeThawDataFrame+ , thawDataFrame, thawPinDataFrame, unsafeThawDataFrame, withThawDataFrame , writeDataFrame, writeDataFrameOff , readDataFrame, readDataFrameOff- , isDataFramePinned+ , isDataFramePinned, getDataFrameSteps ) where --import GHC.Base-import GHC.ST (ST (..))+import GHC.ST (ST (..)) +import Data.Coerce+import Data.Kind import Numeric.DataFrame.Internal.Mutable import Numeric.DataFrame.Internal.PrimArray import Numeric.DataFrame.Type import Numeric.Dimensions+import Unsafe.Coerce -- | Mutable DataFrame that lives in ST. -- Internal representation is always a MutableByteArray.-data family STDataFrame s (t :: Type) (ns :: [k])---- | Pure wrapper on a mutable byte array-newtype instance STDataFrame s t (ns :: [Nat]) = STDataFrame (MDataFrame s t (ns :: [Nat]))+newtype STDataFrame s (t :: Type) (ns :: [k]) = STDataFrame (MDataFrame s t (ns :: [k])) -- | Data frame with some dimensions missing at compile time. -- Pattern-match against its constructor to get a Nat-indexed mutable data frame.-data instance STDataFrame s t (xs :: [XNat])- = forall (ns :: [Nat]) . Dimensions ns- => XSTFrame (STDataFrame s t ns)+pattern XSTFrame :: forall s (t :: Type) (xns :: [XNat]) . ()+ => forall (ns :: [Nat]) . (FixedDims xns ns, Dimensions ns)+ => STDataFrame s t ns -> STDataFrame s t xns+pattern XSTFrame df <- (mkXSTFramePat -> XSTFramePat df)+ where+ XSTFrame = castDataFrame+{-# COMPLETE XSTFrame #-} +data XSTFramePat s (t :: Type) (xns :: [XNat])+ = forall (ns :: [Nat]) . (FixedDims xns ns, Dimensions ns)+ => XSTFramePat (STDataFrame s t ns)++mkXSTFramePat :: forall s (t :: Type) (xns :: [XNat])+ . STDataFrame s t xns -> XSTFramePat s t xns+mkXSTFramePat df+ | SomeDims ds <- fromSteps (getDataFrameSteps df)+ , XDims (Dims :: Dims ns) <- (unsafeCoerce ds) :: Dims xns+ = XSTFramePat @s @t @xns @ns (unsafeCoerce df)+ | otherwise+ = error "XSTFrame pattern: impossible args"+ -- | Mutable DataFrame of unknown dimensionality data SomeSTDataFrame s (t :: Type) = forall (ns :: [Nat]) . Dimensions ns => SomeSTDataFrame (STDataFrame s t ns) +-- | Allow coercing between @XNat@-indexed and @Nat@-indexed Mutable DataFrames.+castDataFrame ::+ forall (t :: Type) (xns :: [XNat]) (ns :: [Nat]) s+ . FixedDims xns ns+ => STDataFrame s t ns -> STDataFrame s t xns+castDataFrame = coerce (castDataFrame# @t @xns @ns)+{-# INLINE castDataFrame #-}+ -- | Create a new mutable DataFrame.-newDataFrame :: forall t (ns :: [Nat]) s- . ( PrimBytes t, Dimensions ns)- => ST s (STDataFrame s t ns)-newDataFrame = STDataFrame <$> ST (newDataFrame# @t @ns)+newDataFrame ::+ forall (t :: Type) ns s+ . (PrimBytes t, Dimensions ns) => ST s (STDataFrame s t ns)+newDataFrame = coerce (newDataFrame# @t @_ @ns) {-# INLINE newDataFrame #-} - -- | Create a new mutable DataFrame.-newPinnedDataFrame :: forall t (ns :: [Nat]) s- . ( PrimBytes t, Dimensions ns)- => ST s (STDataFrame s t ns)-newPinnedDataFrame = STDataFrame <$> ST (newPinnedDataFrame# @t @ns)+newPinnedDataFrame ::+ forall (t :: Type) ns s+ . (PrimBytes t, Dimensions ns) => ST s (STDataFrame s t ns)+newPinnedDataFrame = coerce (newPinnedDataFrame# @t @_ @ns) {-# INLINE newPinnedDataFrame #-} +-- | Create a new mutable DataFrame of the same size.+oneMoreDataFrame ::+ forall (t :: Type) ns s+ . STDataFrame s t ns -> ST s (STDataFrame s t ns)+oneMoreDataFrame = coerce (oneMoreDataFrame# @t @_ @ns)+{-# INLINE oneMoreDataFrame #-} +-- | View a part of a DataFrame.+--+-- This function does not perform a copy.+-- All changes to a new DataFrame will be reflected in the original DataFrame as well.+--+-- If any of the dims in @as@ or @b@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+subDataFrameView ::+ forall (t :: Type) b bi bd as bs asbs s+ . (SubFrameIndexCtx b bi bd, KnownDim bd, ConcatList as (b :+ bs) asbs)+ => Idxs (as +: bi) -> STDataFrame s t asbs -> STDataFrame s t (bd :+ bs)+subDataFrameView = coerce (subDataFrameView# @t @_ @b @bi @bd @as @bs @asbs)++-- | View a part of a DataFrame.+--+-- This function does not perform a copy.+-- All changes to a new DataFrame will be reflected in the original DataFrame as well.+--+-- This is a simpler version of @subDataFrameView@ that allows+-- to view over one index at a time.+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+subDataFrameView' ::+ forall (t :: Type) as bs asbs s+ . ConcatList as bs asbs+ => Idxs as -> STDataFrame s t asbs -> STDataFrame s t bs+subDataFrameView' = coerce (subDataFrameView'# @t @_ @as @bs @asbs)+ -- | Copy one DataFrame into another mutable DataFrame at specified position. ----- In contrast to @copyMDataFrame'@, this function allows to copy over a range of contiguous--- indices over a single dimension.+-- In contrast to @copyDataFrame'@, this function allows to copy over a range+-- of contiguous indices over a single dimension. -- For example, you can write a 3x4 matrix into a 7x4 matrix, starting at indices 0..3.-copyDataFrame :: forall (t :: Type)- (b :: Nat) (bi :: Nat) (bd :: Nat)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( b ~ (bi + bd - 1)- , PrimBytes t- , PrimBytes (DataFrame t (bd :+ bs))- , ConcatList as (b :+ bs) asbs- )- => Idxs (as +: bi) -> DataFrame t (bd :+ bs)- -> STDataFrame s t asbs -> ST s ()-copyDataFrame ei df (STDataFrame mdf) = ST (copyDataFrame# ei df mdf)+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function copies only as+-- many elements as fits into the dataframe along this dimension (possibly none).+copyDataFrame ::+ forall (t :: Type) b bi bd as bs asbs s+ . ( SubFrameIndexCtx b bi bd, KnownDim bd, ExactDims bs+ , PrimArray t (DataFrame t (bd :+ bs))+ , ConcatList as (b :+ bs) asbs )+ => Idxs (as +: bi) -> DataFrame t (bd :+ bs) -> STDataFrame s t asbs -> ST s ()+copyDataFrame = coerce (copyDataFrame# @t @_ @b @bi @bd @as @bs @asbs) {-# INLINE copyDataFrame #-} -- | Copy one mutable DataFrame into another mutable DataFrame at specified position. ----- In contrast to @copyMDataFrame'@, this function allows to copy over a range of contiguous--- indices over a single dimension.+-- In contrast to @copyMutableDataFrame'@, this function allows to copy over a range+-- of contiguous indices over a single dimension. -- For example, you can write a 3x4 matrix into a 7x4 matrix, starting at indices 0..3.-copyMutableDataFrame :: forall (t :: Type)- (b :: Nat) (bi :: Nat) (bd :: Nat)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( b ~ (bi + bd - 1)- , PrimBytes t- , ConcatList as (b :+ bs) asbs- )- => Idxs (as +: bi) -> STDataFrame s t (bd :+ bs)- -> STDataFrame s t asbs -> ST s ()-copyMutableDataFrame ei (STDataFrame mdfA) (STDataFrame mdfB)- = ST (copyMDataFrame# ei mdfA mdfB)+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function copies only as+-- many elements as fits into the dataframe along this dimension (possibly none).+copyMutableDataFrame ::+ forall (t :: Type) b bi bd as bs asbs s+ . ( SubFrameIndexCtx b bi bd+ , ExactDims bs+ , PrimBytes t+ , ConcatList as (b :+ bs) asbs )+ => Idxs (as +: bi) -> STDataFrame s t (bd :+ bs) -> STDataFrame s t asbs -> ST s ()+copyMutableDataFrame = coerce (copyMDataFrame# @t @_ @b @bi @bd @as @bs @asbs) {-# INLINE copyMutableDataFrame #-} -- | Copy one DataFrame into another mutable DataFrame at specified position. ----- This is a simpler version of @copyDataFrame@ that allows to copy over one index at a time.-copyDataFrame' :: forall (t :: Type)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( PrimBytes t- , PrimBytes (DataFrame t bs)- , ConcatList as bs asbs- )- => Idxs as -> DataFrame t bs- -> STDataFrame s t asbs -> ST s ()-copyDataFrame' ei df (STDataFrame mdf) = ST (copyDataFrame'# ei df mdf)+-- This is a simpler version of @copyDataFrame@ that allows+-- to copy over one index at a time.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+copyDataFrame' ::+ forall (t :: Type) as bs asbs s+ . ( ExactDims bs+ , PrimArray t (DataFrame t bs)+ , ConcatList as bs asbs )+ => Idxs as -> DataFrame t bs -> STDataFrame s t asbs -> ST s ()+copyDataFrame' = coerce (copyDataFrame'# @t @_ @as @bs @asbs) {-# INLINE copyDataFrame' #-} -- | Copy one mutable DataFrame into another mutable DataFrame at specified position. ----- This is a simpler version of @copyDataFrame@ that allows to copy over one index at a time.-copyMutableDataFrame' :: forall (t :: Type)- (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) s- . ( PrimBytes t- , ConcatList as bs asbs- )- => Idxs as -> STDataFrame s t bs- -> STDataFrame s t asbs -> ST s ()-copyMutableDataFrame' ei (STDataFrame mdfA) (STDataFrame mdfB)- = ST (copyMDataFrame'# ei mdfA mdfB)+-- This is a simpler version of @copyMutableDataFrame@ that allows+-- to copy over one index at a time.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+copyMutableDataFrame' ::+ forall (t :: Type) as bs asbs s+ . (ExactDims bs, PrimBytes t, ConcatList as bs asbs)+ => Idxs as -> STDataFrame s t bs -> STDataFrame s t asbs -> ST s ()+copyMutableDataFrame' = coerce (copyMDataFrame'# @t @_ @as @bs @asbs) {-# INLINE copyMutableDataFrame' #-} +-- | Copy one DataFrame into another mutable DataFrame by offset in+-- primitive elements.+--+-- This is a low-level copy function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+copyDataFrameOff ::+ forall (t :: Type) as bs asbs s+ . ( Dimensions bs+ , PrimArray t (DataFrame t bs)+ , ConcatList as bs asbs )+ => Int -> DataFrame t bs -> STDataFrame s t asbs -> ST s ()+copyDataFrameOff = coerce (copyDataFrameOff# @t @_ @as @bs @asbs)+{-# INLINE copyDataFrameOff #-}++-- | Copy one mutable DataFrame into another mutable DataFrame by offset in+-- primitive elements.+--+-- This is a low-level copy function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds+copyMutableDataFrameOff ::+ forall (t :: Type) as bs asbs s+ . (ExactDims bs, PrimBytes t, ConcatList as bs asbs)+ => Int -> STDataFrame s t bs -> STDataFrame s t asbs -> ST s ()+copyMutableDataFrameOff = coerce (copyMDataFrameOff# @t @_ @as @bs @asbs)+{-# INLINE copyMutableDataFrameOff #-}+ -- | Make a mutable DataFrame immutable, without copying.-unsafeFreezeDataFrame :: forall (t :: Type) (ns :: [Nat]) s- . PrimArray t (DataFrame t ns)- => STDataFrame s t ns -> ST s (DataFrame t ns)-unsafeFreezeDataFrame (STDataFrame mdf) = ST (unsafeFreezeDataFrame# mdf)+unsafeFreezeDataFrame ::+ forall (t :: Type) ns s+ . PrimArray t (DataFrame t ns)+ => STDataFrame s t ns -> ST s (DataFrame t ns)+unsafeFreezeDataFrame = coerce (unsafeFreezeDataFrame# @t @_ @ns) {-# INLINE unsafeFreezeDataFrame #-} - -- | Copy content of a mutable DataFrame into a new immutable DataFrame.-freezeDataFrame :: forall (t :: Type) (ns :: [Nat]) s- . PrimArray t (DataFrame t ns)- => STDataFrame s t ns -> ST s (DataFrame t ns)-freezeDataFrame (STDataFrame mdf) = ST (freezeDataFrame# mdf)+freezeDataFrame ::+ forall (t :: Type) ns s+ . PrimArray t (DataFrame t ns)+ => STDataFrame s t ns -> ST s (DataFrame t ns)+freezeDataFrame = coerce (freezeDataFrame# @t @_ @ns) {-# INLINE freezeDataFrame #-} -- | Create a new mutable DataFrame and copy content of immutable one in there.-thawDataFrame :: forall (t :: Type) (ns :: [Nat]) s- . (Dimensions ns, PrimBytes (DataFrame t ns))- => DataFrame t ns -> ST s (STDataFrame s t ns)-thawDataFrame df = STDataFrame <$> ST (thawDataFrame# df)+thawDataFrame ::+ forall (t :: Type) ns s+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns -> ST s (STDataFrame s t ns)+thawDataFrame = coerce (thawDataFrame# @t @_ @ns) {-# INLINE thawDataFrame #-} -- | Create a new mutable DataFrame and copy content of immutable one in there. -- The result array is pinned and aligned.-thawPinDataFrame :: forall (t :: Type) (ns :: [Nat]) s- . (Dimensions ns, PrimBytes (DataFrame t ns))- => DataFrame t ns -> ST s (STDataFrame s t ns)-thawPinDataFrame df = STDataFrame <$> ST (thawPinDataFrame# df)+thawPinDataFrame ::+ forall (t :: Type) ns s+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns -> ST s (STDataFrame s t ns)+thawPinDataFrame = coerce (thawPinDataFrame# @t @_ @ns) {-# INLINE thawPinDataFrame #-} -- | UnsafeCoerces an underlying byte array.-unsafeThawDataFrame :: forall (t :: Type) (ns :: [Nat]) s- . ( Dimensions ns- , PrimBytes (DataFrame t ns), PrimBytes t)- => DataFrame t ns -> ST s (STDataFrame s t ns)-unsafeThawDataFrame df = STDataFrame <$> ST (unsafeThawDataFrame# df)+unsafeThawDataFrame ::+ forall (t :: Type) ns s+ . (Dimensions ns, PrimArray t (DataFrame t ns))+ => DataFrame t ns+ -> ST s (STDataFrame s t ns)+unsafeThawDataFrame = coerce (unsafeThawDataFrame# @t @_ @ns) {-# INLINE unsafeThawDataFrame #-} ---- | Write a single element at the specified index-writeDataFrame :: forall t (ns :: [Nat]) s- . PrimBytes t- => STDataFrame s t ns -> Idxs ns -> DataFrame t ('[] :: [Nat]) -> ST s ()-writeDataFrame (STDataFrame mdf) ei = ST . writeDataFrame# mdf ei . unsafeCoerce#-{-# INLINE writeDataFrame #-}----- | Read a single element at the specified index-readDataFrame :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => STDataFrame s t ns -> Idxs ns -> ST s (DataFrame t ('[] :: [Nat]))-readDataFrame (STDataFrame mdf) = unsafeCoerce# . ST . readDataFrame# mdf-{-# INLINE readDataFrame #-}-+-- | Given two continuations @f@ and @g@.+-- If the input DataFrame is a single broadcast value, use it in @f@.+-- Otherwise, create a new mutable DataFrame and copy content of immutable one+-- in there; then use it in @g@.+--+-- This function is useful when @thawDataFrame@ cannot be used due to+-- @Dimensions ns@ constraint being not available.+withThawDataFrame ::+ forall (t :: Type) ns r s+ . PrimArray t (DataFrame t ns)+ => (t -> ST s r)+ -> (STDataFrame s t ns -> ST s r)+ -> DataFrame t ns -> ST s r+withThawDataFrame = coerce (withThawDataFrame# @t @_ @ns @r) --- | Write a single element at the specified element offset-writeDataFrameOff :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => STDataFrame s t ns -> Int -> DataFrame t ('[] :: [Nat]) -> ST s ()-writeDataFrameOff (STDataFrame mdf) (I# i)- = ST . writeDataFrameOff# mdf i . unsafeCoerce#+-- | Write a single element at the specified element offset.+--+-- This is a low-level write function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you write beyond the DataFrame bounds.+writeDataFrameOff ::+ forall (t :: Type) ns s+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => STDataFrame s t ns -> Int -> DataFrame t ('[] :: KindOf ns) -> ST s ()+writeDataFrameOff = coerce (writeDataFrameOff# @t @_ @ns) {-# INLINE writeDataFrameOff #-} +-- | Write a single element at the specified index.+--+-- This function is safe (no `OutOfDimBounds` exception possible).+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- you may happen to write data beyond dataframe bounds.+-- In this case, this function does nothing.+writeDataFrame ::+ forall (t :: Type) ns s+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => STDataFrame s t ns -> Idxs ns -> DataFrame t ('[] :: KindOf ns) -> ST s ()+writeDataFrame = coerce (writeDataFrame# @t @_ @ns)+{-# INLINE writeDataFrame #-} --- | Read a single element at the specified element offset-readDataFrameOff :: forall (t :: Type) (ns :: [Nat]) s- . PrimBytes t- => STDataFrame s t ns -> Int -> ST s (DataFrame t ('[] :: [Nat]))-readDataFrameOff (STDataFrame mdf) (I# i)- = unsafeCoerce# (ST (readDataFrameOff# mdf i))+-- | Read a single element at the specified element offset.+--+-- This is a low-level read function; you have to keep in mind the row-major+-- layout of Mutable DataFrames. Offset bounds are not checked.+-- You will get an undefined behavior if you read beyond the DataFrame bounds.+readDataFrameOff ::+ forall (t :: Type) ns s+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => STDataFrame s t ns -> Int -> ST s (DataFrame t ('[] :: KindOf ns))+readDataFrameOff = coerce (readDataFrameOff# @t @_ @ns) {-# INLINE readDataFrameOff #-} +-- | Read a single element at the specified index.+--+-- If any of the dims in @ns@ is unknown (@n ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+readDataFrame ::+ forall (t :: Type) ns s+ . PrimBytes (DataFrame t ('[] :: KindOf ns))+ => STDataFrame s t ns -> Idxs ns -> ST s (DataFrame t ('[] :: KindOf ns))+readDataFrame = coerce (readDataFrame# @t @_ @ns)+{-# INLINE readDataFrame #-} -- | Check if the byte array wrapped by this DataFrame is pinned, -- which means cannot be relocated by GC.-isDataFramePinned :: forall (k :: Type) (t :: Type) (ns :: [k]) s- . KnownDimKind k- => STDataFrame s t ns -> Bool-isDataFramePinned df = case dimKind @k of- DimNat -> case df of- STDataFrame x -> isDataFramePinned# x- DimXNat -> case df of- XSTFrame (STDataFrame x) -> isDataFramePinned# x+isDataFramePinned ::+ forall (t :: Type) ns s . STDataFrame s t ns -> Bool+isDataFramePinned = coerce (isDataFramePinned# @t @_ @ns)+{-# INLINE isDataFramePinned #-}++-- | Get cumulative dimensions @ns@ of a @STDataFrame s t ns@+getDataFrameSteps ::+ forall (t :: Type) ns s . STDataFrame s t ns -> CumulDims+getDataFrameSteps = coerce (getDataFrameSteps# @t @_ @ns)+{-# INLINE getDataFrameSteps #-}++_unusedTopBind :: STDataFrame s t ns+_unusedTopBind = STDataFrame undefined
src/Numeric/DataFrame/SubSpace.hs view
@@ -1,503 +1,1265 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE UndecidableSuperClasses #-}--------------------------------------------------------------------------------- |--- Module : Numeric.DataFrame.SubSpace--- Copyright : (c) Artem Chirkin--- License : BSD3--------------------------------------------------------------------------------------module Numeric.DataFrame.SubSpace- ( SubSpace (..), (!), element- , ewfoldMap, iwfoldMap- , ewzip, iwzip- , indexWise_, elementWise_- ) where--import Data.Kind-import GHC.Exts--import Numeric.DataFrame.Internal.PrimArray-import Numeric.DataFrame.Type-import Numeric.Dimensions-import Numeric.PrimBytes----- | Operations on DataFrames------ @as@ is an indexing dimensionality------ @bs@ is an element dimensionality------ @t@ is an underlying data type (i.e. Float, Int, Double)----class ( ConcatList as bs asbs- , Dimensions as- , Dimensions bs- , Dimensions asbs- , PrimArray t (DataFrame t bs)- , PrimArray t (DataFrame t asbs)- , PrimBytes (DataFrame t bs)- , PrimBytes (DataFrame t asbs)- ) => SubSpace (t :: Type) (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- | asbs as -> bs, asbs bs -> as, as bs -> asbs where-- -- | Unsafely get a sub-dataframe by its primitive element offset.- -- The offset is not checked to be aligned to the space structure or for bounds.- -- Arguments are zero-based primitive element offset and subset ("bs" element)- -- size (aka `totalDim` of sub dataframe)- --- -- Normal indexing can be expressed in terms of `indexOffset#`:- --- -- > index i = case (# dimVal (dim @bs), fromEnum i #) of (# I# n, I# j #) -> indexOffset# (n *# j)- indexOffset# :: Int# -- ^ Prim element offset- -> DataFrame t asbs -> DataFrame t bs- indexOffset# off df = case uniqueOrCumulDims df of- Left a -> broadcast a- Right steps -> fromElems (dropPref (dims @as) steps) (offsetElems df +# off) (getBytes df)- {-# INLINE [1] indexOffset# #-}-- -- | Unsafely update a sub-dataframe by its primitive element offset.- -- The offset is not checked to be aligned to the space structure or for bounds.- -- Arguments are zero-based primitive element offset and subset ("bs" element)- -- size (aka `totalDim` of sub dataframe)- --- -- Normal updating can be expressed in terms of `indexOffset#`:- --- -- > update i = case (# dimVal (dim @bs), fromEnum i #) of (# I# n, I# j #) -> updateOffset# (n *# j)- updateOffset# :: Int# -- ^ Prim element offset- -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs- updateOffset# off x df- | steps <- getSteps (dims @asbs) df- , elemBS <- byteSize @t undefined = case runRW#- ( \s0 -> case newByteArray# (cdTotalDim# steps *# elemBS) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- ( writeBytes mba (off *# elemBS) x- ( writeBytes mba 0# df s1 )- )- ) of (# _, r #) -> fromElems steps 0# r- {-# INLINE [1] updateOffset# #-}-- -- | Get an element by its index in the dataframe- index :: Idxs as -> DataFrame t asbs -> DataFrame t bs- index i df = case uniqueOrCumulDims df of- Left a -> broadcast a- Right steps -> case cdIx steps i of- I# off -> fromElems (dropPref (dims @as) steps) (offsetElems df +# off) (getBytes df)- {-# INLINE [1] index #-}-- -- | Get a few contiguous elements.- --- -- In a sense, this is just a more complicated version of `index`.- slice :: forall (bi :: Nat) (bd :: Nat) (b' :: Nat) (bs' :: [Nat])- . ( b' ~ (bi + bd - 1), bs ~ (b' :+ bs'), KnownDim bd)- => Idxs (as +: bi) -> DataFrame t asbs -> DataFrame t (bd :+ bs')- slice i df- | _ :* Dims <- dims @bs- , Dict <- inferKnownBackend @t @(bd ': bs')- = case uniqueOrCumulDims df of- Left a -> broadcast a- Right steps -> case cdIx steps i of- I# off- | bsteps <- repHead (dimVal' @bd) (dropPref (dims @as) steps)- -> fromElems bsteps (offsetElems df +# off) (getBytes df)- | otherwise = error "SubSpace/slice: impossible arguments"- where- repHead y (CumulDims (_:x:xs)) = CumulDims (y*x:x:xs)- repHead _ steps = steps-- -- | Set a new value to an element- update :: Idxs as -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs- update i x df- | steps <- getSteps (dims @asbs) df- , I# off <- cdIx steps i- , elemBS <- byteSize @t undefined = case runRW#- ( \s0 -> case newByteArray# (cdTotalDim# steps *# elemBS) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- ( writeBytes mba (off *# elemBS) x- ( writeBytes mba 0# df s1 )- )- ) of (# _, r #) -> fromElems steps 0# r- {-# INLINE [1] update #-}-- -- | Update a few contiguous elements- --- -- In a sense, this is just a more complicated version of `update`.- updateSlice :: forall (bi :: Nat) (bd :: Nat) (b' :: Nat) (bs' :: [Nat])- . ( b' ~ (bi + bd - 1), bs ~ (b' :+ bs'), KnownDim bd)- => Idxs (as +: bi) -> DataFrame t (bd :+ bs') -> DataFrame t asbs -> DataFrame t asbs- updateSlice i x df- | _ :* Dims <- dims @bs- , Dict <- inferKnownBackend @t @(bd ': bs')- , steps <- getSteps (dims @asbs) df- , I# off <- cdIx steps i- , elemBS <- byteSize @t undefined = case runRW#- ( \s0 -> case newByteArray# (cdTotalDim# steps *# elemBS) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- ( writeBytes mba (off *# elemBS) x- ( writeBytes mba 0# df s1 )- )- ) of (# _, r #) -> fromElems steps 0# r- | otherwise = error "SubSpace/updateSlice: impossible arguments"- {-# INLINE [1] updateSlice #-}-- -- | Map a function over each element of DataFrame- ewmap :: forall s (bs' :: [Nat]) (asbs' :: [Nat])- . SubSpace s as bs' asbs'- => (DataFrame s bs' -> DataFrame t bs)- -> DataFrame s asbs' -> DataFrame t asbs- ewmap f df- | bsizeT <- byteSize @t undefined- , stepsAS <- cumulDims $ dims @as- , stepsBS <- cumulDims $ dims @bs- , stepsBS' <- cumulDims $ dims @bs'- , stepsASBS <- stepsAS <> stepsBS- , lenAS <- cdTotalDim# stepsAS- , lenBS <- cdTotalDim# stepsBS- , lenBS' <- cdTotalDim# stepsBS'- , lenBSB <- lenBS *# bsizeT- , lenASBSB <- lenAS *# lenBSB- = let go mba sourceOffE destOffB s- | isTrue# (destOffB >=# lenASBSB)- = s- | otherwise- = go mba (sourceOffE +# lenBS') (destOffB +# lenBSB)- (writeBytes mba destOffB (f (indexOffset# sourceOffE df)) s)- in case runRW#- ( \s0 -> case newByteArray# lenASBSB s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba ( go mba 0# 0# s1 )- ) of (# _, r #) -> fromElems stepsASBS 0# r- {-# INLINE [1] ewmap #-}-- -- | Map a function over each element with its index of DataFrame- iwmap :: forall s (bs' :: [Nat]) (asbs' :: [Nat])- . SubSpace s as bs' asbs'- => (Idxs as -> DataFrame s bs' -> DataFrame t bs)- -> DataFrame s asbs' -> DataFrame t asbs- iwmap f df- | bsizeT <- byteSize @t undefined- , stepsAS <- cumulDims $ dims @as- , stepsBS <- cumulDims $ dims @bs- , stepsBS' <- cumulDims $ dims @bs'- , stepsASBS <- stepsAS <> stepsBS- , lenAS <- cdTotalDim# stepsAS- , lenBS <- cdTotalDim# stepsBS- , lenBS' <- cdTotalDim# stepsBS'- , lenBSB <- lenBS *# bsizeT- , lenASBSB <- lenAS *# lenBSB- = let go _ [] _ _ s = s- go mba (i:is) sourceOffE destOffB s- = go mba is (sourceOffE +# lenBS') (destOffB +# lenBSB)- (writeBytes mba destOffB (f i (indexOffset# sourceOffE df)) s)- in case runRW#- ( \s0 -> case newByteArray# lenASBSB s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba ( go mba [minBound..maxBound] 0# 0# s1 )- ) of (# _, r #) -> fromElems stepsASBS 0# r- {-# INLINE [1] iwmap #-}-- -- | Generate a DataFrame by repeating an element- ewgen :: DataFrame t bs -> DataFrame t asbs- ewgen df = case uniqueOrCumulDims df of- Left a -> broadcast a- Right stepsBS- | stepsAS <- cumulDims $ dims @as- , stepsASBS <- stepsAS <> stepsBS- , elS <- byteSize @t undefined- , lenBSB <- cdTotalDim# stepsBS *# elS- , lenASBSB <- cdTotalDim# stepsASBS *# elS- -> let go mba destOffB s- | isTrue# (destOffB >=# lenASBSB)- = s- | otherwise- = go mba (destOffB +# lenBSB)- (writeBytes mba destOffB df s)- in case runRW#- ( \s0 -> case newByteArray# lenASBSB s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba ( go mba 0# s1 )- ) of (# _, r #) -> fromElems stepsASBS 0# r- {-# INLINE [1] ewgen #-}-- -- | Generate a DataFrame by iterating a function (index -> element)- iwgen :: (Idxs as -> DataFrame t bs) -> DataFrame t asbs- iwgen f- | stepsAS <- cumulDims $ dims @as- , stepsBS <- cumulDims $ dims @bs- , stepsASBS <- stepsAS <> stepsBS- , elS <- byteSize @t undefined- , lenBSB <- cdTotalDim# stepsBS *# elS- , lenASBSB <- cdTotalDim# stepsASBS *# elS- = let go _ [] _ s = s- go mba (i:is) destOffB s- = go mba is (destOffB +# lenBSB) (writeBytes mba destOffB (f i) s)- in case runRW#- ( \s0 -> case newByteArray# lenASBSB s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba ( go mba [minBound..maxBound] 0# s1 )- ) of (# _, r #) -> fromElems stepsASBS 0# r- {-# INLINE [1] iwgen #-}-- -- | Left-associative fold of a DataFrame.- -- The fold is strict, so accumulater is evaluated to WHNF;- -- but you'd better make sure that the function is strict enough to not- -- produce memory leaks deeply inside the result data type.- ewfoldl :: (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b- ewfoldl f x0 df = case uniqueOrCumulDims df of- Left a ->- let b = broadcast a- go 0 x = x- go n x = go (n-1) $! f x b- in go (totalDim' @as) x0- Right stepsASBS- | stepsBS <- dropPref (dims @as) stepsASBS- , lenBS <- cdTotalDim# stepsBS- , lenASBS <- cdTotalDim# stepsASBS- -> let go sourceOffE x- | isTrue# (sourceOffE >=# lenASBS)- = x- | otherwise- = go (sourceOffE +# lenBS) $! f x (indexOffset# sourceOffE df)- in go 0# x0-- -- | Left-associative fold of a DataFrame with an index- -- The fold is strict, so accumulater is evaluated to WHNF;- -- but you'd better make sure that the function is strict enough to not- -- produce memory leaks deeply inside the result data type.- iwfoldl :: (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b- iwfoldl f x0 df = case uniqueOrCumulDims df of- Left a ->- let b = broadcast a- go [] x = x- go (i:is) x = go is $! f i x b- in go [minBound..maxBound] x0- Right stepsASBS- | stepsBS <- dropPref (dims @as) stepsASBS- , lenBS <- cdTotalDim# stepsBS- -> let go [] _ x = x- go (i:is) sourceOffE x- = go is (sourceOffE +# lenBS) $! f i x (indexOffset# sourceOffE df)- in go [minBound..maxBound] 0# x0-- -- | Right-associative fold of a DataFrame- -- The fold is strict, so accumulater is evaluated to WHNF;- -- but you'd better make sure that the function is strict enough to not- -- produce memory leaks deeply inside the result data type.- ewfoldr :: (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b- ewfoldr f x0 df = case uniqueOrCumulDims df of- Left a ->- let b = broadcast a- go 0 x = x- go n x = go (n-1) $! f b x- in go (totalDim' @as) x0- Right stepsASBS- | stepsBS <- dropPref (dims @as) stepsASBS- , lenBS <- cdTotalDim# stepsBS- , lenASBS <- cdTotalDim# stepsASBS- -> let go sourceOffE x- | isTrue# (sourceOffE <# 0#)- = x- | otherwise- = go (sourceOffE -# lenBS) $! f (indexOffset# sourceOffE df) x- in go (lenASBS -# lenBS) x0-- -- | Right-associative fold of a DataFrame with an index- -- The fold is strict, so accumulater is evaluated to WHNF;- -- but you'd better make sure that the function is strict enough to not- -- produce memory leaks deeply inside the result data type.- iwfoldr :: (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b- iwfoldr f x0 df = case uniqueOrCumulDims df of- Left a ->- let b = broadcast a- go [] x = x- go (i:is) x = go is $! f i b x- in go [maxBound, pred maxBound .. minBound] x0- Right stepsASBS- | stepsBS <- dropPref (dims @as) stepsASBS- , lenBS <- cdTotalDim# stepsBS- , lenASBS <- cdTotalDim# stepsASBS- -> let go [] _ x = x- go (i:is) sourceOffE x- = go is (sourceOffE -# lenBS) $! f i (indexOffset# sourceOffE df) x- in go [maxBound, pred maxBound .. minBound] (lenASBS -# lenBS) x0-- -- | Apply an applicative functor on each element (Lens-like traversal)- elementWise :: forall (s :: Type) (bs' :: [Nat]) (asbs' :: [Nat]) (f :: Type -> Type)- . ( Applicative f- , SubSpace s as bs' asbs'- )- => (DataFrame s bs' -> f (DataFrame t bs))- -> DataFrame s asbs' -> f (DataFrame t asbs)- elementWise = indexWise . const- {-# INLINE [1] elementWise #-}-- -- | Apply an applicative functor on each element with its index- -- (Lens-like indexed traversal)- indexWise :: forall (s :: Type) (bs' :: [Nat]) (asbs' :: [Nat]) (f :: Type -> Type)- . ( Applicative f- , SubSpace s as bs' asbs'- )- => (Idxs as -> DataFrame s bs' -> f (DataFrame t bs))- -> DataFrame s asbs' -> f (DataFrame t asbs)- indexWise f df = runWithState <$> iwfoldl applyF (pure initialState) df- where- steps = cumulDims $ dims @asbs- -- run a state-based continuation within RW- runWithState :: ( State# RealWorld -> (# State# RealWorld, (# MutableByteArray# RealWorld, Int# #) #))- -> DataFrame t asbs- runWithState g = case runRW#- ( \s0 -> case g s0 of- (# s1, (# marr, _ #) #) -> unsafeFreezeByteArray# marr s1- ) of (# _, arr #) -> fromElems steps 0# arr-- -- Prepare empty byte array for the result DataFrame and keep a current position counter- -- Input: state- -- Output: state +- -- ( current mutable byte array + current write position )- initialState :: State# RealWorld -> (# State# RealWorld, (# MutableByteArray# RealWorld, Int# #) #)- initialState s0 = case newByteArray# (rezLength# *# rezElBSize#) s0 of- (# s1, marr #) -> (# s1, (# marr, 0# #) #)-- -- Given the result chunk, write it into a mutable array- updateChunk :: (State# RealWorld -> (# State# RealWorld, (# MutableByteArray# RealWorld, Int# #) #))- -> DataFrame t bs- -> (State# RealWorld -> (# State# RealWorld, (# MutableByteArray# RealWorld, Int# #) #))- updateChunk g dfChunk = case (# byteOffset dfChunk, getBytes dfChunk #) of- (# off#, arr# #) -> \s -> case g s of- (# s1, (# marr#, pos# #) #) -> case- copyByteArray# arr# (off# *# rezElBSize#)- marr# (pos# *# rezElBSize#)- (rezStepN# *# rezElBSize#) s1 of- s2 -> (# s2, (# marr#, pos# +# rezStepN# #) #)-- -- Apply applicative functor on each chunk and update a state.- applyF :: Idxs as- -> f (State# RealWorld -> (# State# RealWorld, (# MutableByteArray# RealWorld, Int# #) #))- -> DataFrame s bs'- -> f (State# RealWorld -> (# State# RealWorld, (# MutableByteArray# RealWorld, Int# #) #))- applyF idx s dfChunk = idx `seq` dfChunk `seq` updateChunk <$> s <*> f idx dfChunk-- -- Element byte size of the result DataFrame (byte size of s)- rezElBSize# = byteSize @t undefined- -- Number of primitive elements in the result DataFrame chunk- !(I# rezStepN#) = fromIntegral $ totalDim' @bs- -- Number of primitive elements in the result DataFrame- rezLength# = cdTotalDim# steps------ | Apply an applicative functor on each element with its index--- (Lens-like indexed traversal)-indexWise_ :: forall t as bs asbs f b- . (SubSpace t as bs asbs, Applicative f)- => (Idxs as -> DataFrame t bs -> f b)- -> DataFrame t asbs -> f ()-indexWise_ f = iwfoldr (\i -> (*>) . f i) (pure ())---- | Apply an applicative functor on each element (Lens-like traversal)-elementWise_ :: forall t as bs asbs f b- . (SubSpace t as bs asbs, Applicative f)- => (DataFrame t bs -> f b)- -> DataFrame t asbs -> f ()-elementWise_ f = ewfoldr ((*>) . f) (pure ())----- | Apply a functor over a single element (simple lens)-element :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) f- . (SubSpace t as bs asbs, Applicative f)- => Idxs as- -> (DataFrame t bs -> f (DataFrame t bs))- -> DataFrame t asbs -> f (DataFrame t asbs)-element i f df = flip (update i) df <$> f (index i df)-{-# INLINE element #-}---- | Index an element (reverse arguments of `index`)-(!) :: SubSpace t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- => DataFrame t asbs -> Idxs as -> DataFrame t bs-(!) = flip index-{-# INLINE (!) #-}-infixl 4 !----ewfoldMap :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) m- . (Monoid m, SubSpace t as bs asbs)- => (DataFrame t bs -> m) -> DataFrame t asbs -> m-ewfoldMap f = ewfoldl (\m b -> m `seq` (mappend m $! f b)) mempty-{-# INLINE ewfoldMap #-}--iwfoldMap :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) m- . ( Monoid m, SubSpace t as bs asbs)- => (Idxs as -> DataFrame t bs -> m) -> DataFrame t asbs -> m-iwfoldMap f = iwfoldl (\i m b -> m `seq` (mappend m $! f i b)) mempty-{-# INLINE iwfoldMap #-}------ | Zip two spaces on a specified subspace index-wise (with index)-iwzip :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- s (bs' :: [Nat]) (asbs' :: [Nat])- r (bs'' :: [Nat]) (asbs'' :: [Nat])- . ( SubSpace t as bs asbs- , SubSpace s as bs' asbs'- , SubSpace r as bs'' asbs''- )- => (Idxs as -> DataFrame t bs -> DataFrame s bs' -> DataFrame r bs'')- -> DataFrame t asbs- -> DataFrame s asbs'- -> DataFrame r asbs''-iwzip f dft dfs = iwmap g dft- where- g i dft' = f i dft' (index i dfs)-{-# INLINE iwzip #-}---- | Zip two spaces on a specified subspace element-wise (without index)-ewzip :: forall t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat])- s (bs' :: [Nat]) (asbs' :: [Nat])- r (bs'' :: [Nat]) (asbs'' :: [Nat])- . ( SubSpace t as bs asbs- , SubSpace s as bs' asbs'- , SubSpace r as bs'' asbs''- )- => (DataFrame t bs -> DataFrame s bs' -> DataFrame r bs'')- -> DataFrame t asbs- -> DataFrame s asbs'- -> DataFrame r asbs''-ewzip = iwzip . const-{-# INLINE ewzip #-}---instance ( ConcatList as bs asbs- , Dimensions as- , Dimensions bs- , Dimensions asbs- , PrimArray t (DataFrame t bs)- , PrimArray t (DataFrame t asbs)- , PrimBytes (DataFrame t bs)- , PrimBytes (DataFrame t asbs)- ) => SubSpace t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) where----dropPref :: Dims (ns :: [Nat]) -> CumulDims -> CumulDims-dropPref ds = CumulDims . drop (length $ listDims ds) . unCumulDims---unSc :: DataFrame (t :: Type) ('[] :: [Nat]) -> t-unSc = unsafeCoerce#--{-# RULES-"ewgen/broadcast" ewgen = broadcast . unSc-- #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses #-}+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+{- |+Module : Numeric.DataFrame.SubSpace+Copyright : (c) Artem Chirkin+License : BSD3++This module provides a flexible interface to manipulate parts of a DataFrame.++==== A note on indexing and slicing++When you index or slice dataframes, the left part of the dimension list+(@as@ for indexing, plus @b@ for slicing) determines the mechanics of accessing+sub-dataframes.+If compiler knows all dimensions at compile time, it can guarantee that the+operation is safe (provided with valid indices).+Otherwise, you can get an `OutOfDimBounds` exception at runtime.++When all dimensions in the indexing subspace satisfy @d :: Nat@ or @d ~ N n@,+slicing functions are safe to use, but you need some type-level proof for GHC+that the indices align.++When any of the dimensions are unknown (@d ~ XN m@), these functions are unsafe+-- they can yield an `OutOfDimBounds` exception if you give a bad index.+But they are easy to use (no type-level proof needed).++ -}++module Numeric.DataFrame.SubSpace+ ( -- $indexingSafety+ -- * Class definition+ SubSpace (SubSpaceCtx), CanSlice+ -- * Simple interface+ --+ -- All functions in this section are named @sxxx@, where @s@ stands for+ -- "simple". These allow using all functions of `SubSpace` with indexing+ -- dimensionality @as@ fixed to a single dim @(as ~ '[a])@.+ -- Use these functions if you are tired of @TypeApplications@ or find the+ -- error messages too cryptic.+ , sindexOffset, supdateOffset+ , (.!), slookup, supdate, sslice, ssliceMaybe, supdateSlice+ , sewgen, siwgen, sewmap, siwmap, sewzip, siwzip+ , selement, selementWise, selementWise_, sindexWise, sindexWise_+ , sewfoldl, sewfoldl', sewfoldr, sewfoldr', sewfoldMap+ , siwfoldl, siwfoldl', siwfoldr, siwfoldr', siwfoldMap+ -- * Flexible interface+ --+ -- Functions in this section allow you pick, fold, iterate, or do whatever+ -- you want with arbitrary sub-dataframe dimensionalities:+ -- e.g. a DataFrame of rank 3 can be processed as an 1D array of matrices+ -- or a matrix of vectors, or a 3D array of scalars.+ -- Often, you would need @TypeApplications@ to specify explicitly at least+ -- the indexing subspace (parameter @as@).+ , joinDataFrame, indexOffset, updateOffset+ , index, Numeric.DataFrame.SubSpace.lookup+ , update, slice, sliceMaybe, updateSlice+ , ewgen, iwgen, ewmap, iwmap, ewzip, iwzip+ , element, elementWise, elementWise_, indexWise, indexWise_+ , ewfoldl, ewfoldl', ewfoldr, ewfoldr', ewfoldMap+ , iwfoldl, iwfoldl', iwfoldr, iwfoldr', iwfoldMap+ ) where++import Control.Arrow (first)+import Control.Monad+import Control.Monad.ST+import Data.Kind+import GHC.Base (Int (..), (+#))+import Numeric.DataFrame.Internal.PrimArray+import Numeric.DataFrame.ST+import Numeric.DataFrame.Type+import Numeric.Dimensions+import qualified Numeric.TypedList as TL+import Unsafe.Coerce++-- | Unsafely get a sub-dataframe by its primitive element offset.+-- The offset is not checked to be aligned to the space structure or for bounds.+--+-- Warning: this function is utterly unsafe -- it does not even throw an exception if+-- the offset is too big; you just get an undefined behavior.+sindexOffset ::+ forall t a bs+ . SubSpace t '[a] bs (a :+ bs)+ => Int -- ^ Prim element offset+ -> DataFrame t (a :+ bs) -> DataFrame t bs+sindexOffset = indexOffset @t @'[a] @bs @(a :+ bs)+{-# INLINE sindexOffset #-}++-- | Unsafely update a sub-dataframe by its primitive element offset.+-- The offset is not checked to be aligned to the space structure or for bounds.+--+-- Warning: this function is utterly unsafe -- it does not even throw an exception if+-- the offset is too big; you just get an undefined behavior.+supdateOffset ::+ forall t a bs+ . SubSpace t '[a] bs (a :+ bs)+ => Int -- ^ Prim element offset+ -> DataFrame t bs -> DataFrame t (a :+ bs) -> DataFrame t (a :+ bs)+supdateOffset = updateOffset @t @'[a] @bs @(a :+ bs)+{-# INLINE supdateOffset #-}++-- | Get an element by its index in the dataframe.+--+-- If (@a ~ XN m@) then this function is unsafe and can throw+-- an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+(.!) ::+ forall t a bs+ . SubSpace t '[a] bs (a :+ bs)+ => DataFrame t (a :+ bs) -> Idx a -> DataFrame t bs+(.!) x i = index @t @'[a] @bs @(a :+ bs) (i :* U) x+{-# INLINE (.!) #-}+infixl 4 .!++-- | Set a new value to an element.+--+-- If (@a ~ XN m@) and the index falls outside of the DataFrame dim,+-- then this function returns the original DataFrame.+supdate ::+ forall t a bs+ . SubSpace t '[a] bs (a :+ bs)+ => Idx a -> DataFrame t bs -> DataFrame t (a :+ bs) -> DataFrame t (a :+ bs)+supdate = update @t @'[a] @bs @(a :+ bs) . (:*U)+{-# INLINE[1] supdate #-}++-- | Map a function over each element of DataFrame.+sewmap ::+ forall t a bs s bs'+ . (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs'))+ => (DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s (a :+ bs') -> DataFrame t (a :+ bs)+sewmap = ewmap @t @'[a] @bs @(a :+ bs) @s @bs' @(a :+ bs')+{-# INLINE sewmap #-}++-- | Map a function over each element with its index of DataFrame.+siwmap ::+ forall t a bs s bs'+ . (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs'))+ => (Idx a -> DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s (a :+ bs') -> DataFrame t (a :+ bs)+siwmap f = iwmap @t @'[a] @bs @(a :+ bs) @s @bs' @(a :+ bs') (\(i :* U) -> f i)+{-# INLINE[1] siwmap #-}++-- | Generate a DataFrame by repeating an element.+sewgen ::+ forall t a bs+ . (SubSpace t '[a] bs (a :+ bs), Dimensions '[a])+ => DataFrame t bs -> DataFrame t (a :+ bs)+sewgen = ewgen @t @'[a] @bs @(a :+ bs)+{-# INLINE sewgen #-}++-- | Generate a DataFrame by iterating a function (index -> element).+siwgen ::+ forall t a bs+ . (SubSpace t '[a] bs (a :+ bs), Dimensions '[a])+ => (Idx a -> DataFrame t bs) -> DataFrame t (a :+ bs)+siwgen f = iwgen @t @'[a] @bs @(a :+ bs) (\(i :* U) -> f i)+{-# INLINE[1] siwgen #-}++-- | Left-associative lazy fold of a DataFrame.+-- Same rules apply as for `foldl`.+sewfoldl ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b+sewfoldl = ewfoldl @t @'[a] @bs @(a :+ bs) @b+{-# INLINE sewfoldl #-}++-- | Left-associative strict fold of a DataFrame.+-- Same rules apply as for `foldl'`.+sewfoldl' ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b+sewfoldl' = ewfoldl' @t @'[a] @bs @(a :+ bs) @b+{-# INLINE sewfoldl' #-}++-- | Left-associative lazy fold of a DataFrame with an index.+-- Same rules apply as for `foldl`.+siwfoldl ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (Idx a -> b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b+siwfoldl f = iwfoldl @t @'[a] @bs @(a :+ bs) @b (\(i :* U) -> f i)+{-# INLINE[1] siwfoldl #-}++-- | Left-associative strict fold of a DataFrame with an index.+-- Same rules apply as for `foldl'`.+siwfoldl' ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (Idx a -> b -> DataFrame t bs -> b) -> b -> DataFrame t (a :+ bs) -> b+siwfoldl' f = iwfoldl' @t @'[a] @bs @(a :+ bs) @b (\(i :* U) -> f i)+{-# INLINE[1] siwfoldl' #-}++-- | Right-associative lazy fold of a DataFrame.+-- Same rules apply as for `foldr`.+sewfoldr ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b+sewfoldr = ewfoldr @t @'[a] @bs @(a :+ bs) @b+{-# INLINE sewfoldr #-}++-- | Right-associative strict fold of a DataFrame.+-- Same rules apply as for `foldr'`.+sewfoldr' ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b+sewfoldr' = ewfoldr' @t @'[a] @bs @(a :+ bs) @b+{-# INLINE sewfoldr' #-}++-- | Right-associative lazy fold of a DataFrame with an index.+-- Same rules apply as for `foldr`.+siwfoldr ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (Idx a -> DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b+siwfoldr f = iwfoldr @t @'[a] @bs @(a :+ bs) @b (\(i :* U) -> f i)+{-# INLINE[1] siwfoldr #-}++-- | Right-associative strict fold of a DataFrame with an index.+-- Same rules apply as for `foldr'`.+siwfoldr' ::+ forall t a bs b+ . SubSpace t '[a] bs (a :+ bs)+ => (Idx a -> DataFrame t bs -> b -> b) -> b -> DataFrame t (a :+ bs) -> b+siwfoldr' f = iwfoldr' @t @'[a] @bs @(a :+ bs) @b (\(i :* U) -> f i)+{-# INLINE[1] siwfoldr' #-}++-- | Apply an applicative functor on each element (Lens-like traversal).+selementWise ::+ forall t a bs s bs' f+ . (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs'), Applicative f)+ => (DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s (a :+ bs') -> f (DataFrame t (a :+ bs))+selementWise = elementWise @t @'[a] @bs @(a :+ bs) @s @bs' @(a :+ bs') @f+{-# INLINE selementWise #-}++-- | Apply an applicative functor on each element with its index+-- (Lens-like indexed traversal).+sindexWise ::+ forall t a bs s bs' f+ . (SubSpace t '[a] bs (a :+ bs), SubSpace s '[a] bs' (a :+ bs'), Applicative f)+ => (Idx a -> DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s (a :+ bs') -> f (DataFrame t (a :+ bs))+sindexWise f = indexWise @t @'[a] @bs @(a :+ bs) @s @bs' @(a :+ bs') @f (\(i :* U) -> f i)+{-# INLINE[1] sindexWise #-}++-- | Apply an applicative functor on each element (Lens-like traversal)+selementWise_ ::+ forall t a bs f b+ . (SubSpace t '[a] bs (a :+ bs), Applicative f)+ => (DataFrame t bs -> f b) -> DataFrame t (a :+ bs) -> f ()+selementWise_ = elementWise_ @t @'[a] @bs @(a :+ bs) @f @b+{-# INLINE selementWise_ #-}++-- | Apply an applicative functor on each element with its index+-- (Lens-like indexed traversal)+sindexWise_ ::+ forall t a bs f b+ . (SubSpace t '[a] bs (a :+ bs), Applicative f)+ => (Idx a -> DataFrame t bs -> f b) -> DataFrame t (a :+ bs) -> f ()+sindexWise_ f = indexWise_ @t @'[a] @bs @(a :+ bs) @f @b (\(i :* U) -> f i)+{-# INLINE sindexWise_ #-}++-- | Apply a functor over a single element (simple lens)+--+-- If (@a ~ XN m@) and the index falls outside of the DataFrame Dim, the+-- argument Functor is not called and the result is @pure@ original DataFrame.+selement ::+ forall t a bs f+ . (SubSpace t '[a] bs (a :+ bs), Applicative f)+ => Idx a+ -> (DataFrame t bs -> f (DataFrame t bs))+ -> DataFrame t (a :+ bs) -> f (DataFrame t (a :+ bs))+selement = element @t @'[a] @bs @(a :+ bs) @f . (:* U)+{-# INLINE selement #-}++-- | Map each element of the DataFrame to a monoid,+-- and combine the results.+sewfoldMap ::+ forall t a bs m+ . (SubSpace t '[a] bs (a :+ bs), Monoid m)+ => (DataFrame t bs -> m) -> DataFrame t (a :+ bs) -> m+sewfoldMap = ewfoldMap @t @'[a] @bs @(a :+ bs) @m+{-# INLINE sewfoldMap #-}++-- | Map each element of the DataFrame and its index to a monoid,+-- and combine the results.+siwfoldMap ::+ forall t a bs m+ . (SubSpace t '[a] bs (a :+ bs), Monoid m)+ => (Idx a -> DataFrame t bs -> m) -> DataFrame t (a :+ bs) -> m+siwfoldMap f = iwfoldMap @t @'[a] @bs @(a :+ bs) @m (\(i :* U) -> f i)+{-# INLINE[1] siwfoldMap #-}++-- | Zip two spaces on a specified subspace element-wise (without index)+sewzip ::+ forall t a bs l bsL r bsR+ . (SubSpace t '[a] bs (a :+ bs), SubSpace l '[a] bsL (a :+ bsL), SubSpace r '[a] bsR (a :+ bsR))+ => (DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs)+ -> DataFrame l (a :+ bsL) -> DataFrame r (a :+ bsR) -> DataFrame t (a :+ bs)+sewzip = ewzip @t @'[a] @bs @(a :+ bs) @l @bsL @(a :+ bsL) @r @bsR @(a :+ bsR)+{-# INLINE sewzip #-}++-- | Zip two spaces on a specified subspace index-wise (with index)+siwzip ::+ forall t a bs l bsL r bsR+ . (SubSpace t '[a] bs (a :+ bs), SubSpace l '[a] bsL (a :+ bsL), SubSpace r '[a] bsR (a :+ bsR))+ => (Idx a -> DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs)+ -> DataFrame l (a :+ bsL) -> DataFrame r (a :+ bsR) -> DataFrame t (a :+ bs)+siwzip f = iwzip @t @'[a] @bs @(a :+ bs) @l @bsL @(a :+ bsL) @r @bsR @(a :+ bsR) (\(i :* U) -> f i)+{-# INLINE siwzip #-}++-- | Flatten a nested DataFrame, analogous to `Control.Monad.join`.+joinDataFrame ::+ forall t as bs asbs+ . (SubSpace t as bs asbs, PrimBytes (DataFrame t bs))+ => DataFrame (DataFrame t bs) as -> DataFrame t asbs+joinDataFrame = joinDataFrameI @_ @t @as @bs @asbs+{-# INLINE[1] joinDataFrame #-}++-- | Unsafely get a sub-dataframe by its primitive element offset.+-- The offset is not checked to be aligned to the space structure or for bounds.+--+-- Warning: this function is utterly unsafe -- it does not even throw an exception if+-- the offset is too big; you just get an undefined behavior.+indexOffset ::+ forall t as bs asbs+ . SubSpace t as bs asbs+ => Int -- ^ Prim element offset+ -> DataFrame t asbs -> DataFrame t bs+indexOffset = indexOffsetI @_ @t @as @bs @asbs+{-# INLINE[1] indexOffset #-}++-- | Unsafely update a sub-dataframe by its primitive element offset.+-- The offset is not checked to be aligned to the space structure or for bounds.+--+-- Warning: this function is utterly unsafe -- it does not even throw an exception if+-- the offset is too big; you just get an undefined behavior.+updateOffset ::+ forall t as bs asbs+ . SubSpace t as bs asbs+ => Int -- ^ Prim element offset+ -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs+updateOffset = updateOffsetI @_ @t @as @bs @asbs+{-# INLINE[1] updateOffset #-}++-- | Get an element by its index in the dataframe.+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+index ::+ forall t as bs asbs+ . SubSpace t as bs asbs+ => Idxs as -> DataFrame t asbs -> DataFrame t bs+index = indexI @_ @t @as @bs @asbs+{-# INLINE[1] index #-}++-- | Set a new value to an element.+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to update data beyond dataframe bounds.+-- In this case, the original DataFrame is returned.+update ::+ forall t as bs asbs+ . SubSpace t as bs asbs+ => Idxs as -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs+update = updateI @_ @t @as @bs @asbs+{-# INLINE[1] update #-}++-- | Map a function over each element of DataFrame.+ewmap ::+ forall t as bs asbs s bs' asbs'+ . (SubSpace t as bs asbs, SubSpace s as bs' asbs')+ => (DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s asbs' -> DataFrame t asbs+ewmap = ewmapI @_ @t @as @bs @asbs @s @bs' @asbs'+{-# INLINE[1] ewmap #-}++-- | Map a function over each element with its index of DataFrame.+iwmap ::+ forall t as bs asbs s bs' asbs'+ . (SubSpace t as bs asbs, SubSpace s as bs' asbs')+ => (Idxs as -> DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s asbs' -> DataFrame t asbs+iwmap = iwmapI @_ @t @as @bs @asbs @s @bs' @asbs'+{-# INLINE[1] iwmap #-}++-- | Generate a DataFrame by repeating an element.+ewgen ::+ forall t as bs asbs+ . (SubSpace t as bs asbs, Dimensions as)+ => DataFrame t bs -> DataFrame t asbs+ewgen = ewgenI @_ @t @as @bs @asbs+{-# INLINE[1] ewgen #-}++-- | Generate a DataFrame by iterating a function (index -> element).+iwgen ::+ forall t as bs asbs+ . (SubSpace t as bs asbs, Dimensions as)+ => (Idxs as -> DataFrame t bs) -> DataFrame t asbs+iwgen = iwgenI @_ @t @as @bs @asbs+{-# INLINE[1] iwgen #-}++-- | Left-associative lazy fold of a DataFrame.+-- Same rules apply as for `foldl`.+ewfoldl ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b+ewfoldl = ewfoldlI @_ @t @as @bs @asbs @b+{-# INLINE[1] ewfoldl #-}++-- | Left-associative strict fold of a DataFrame.+-- Same rules apply as for `foldl'`.+ewfoldl' ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b+ewfoldl' f z0 xs = ewfoldr f' id xs z0+ where+ f' x k z = k $! f z x+{-# INLINE ewfoldl' #-}++-- | Left-associative lazy fold of a DataFrame with an index.+-- Same rules apply as for `foldl`.+iwfoldl ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b+iwfoldl = iwfoldlI @_ @t @as @bs @asbs @b+{-# INLINE[1] iwfoldl #-}++-- | Left-associative strict fold of a DataFrame with an index.+-- Same rules apply as for `foldl'`.+iwfoldl' ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b+iwfoldl' f z0 xs = iwfoldr f' id xs z0+ where+ f' i x k z = k $! f i z x+{-# INLINE[1] iwfoldl' #-}++-- | Right-associative lazy fold of a DataFrame.+-- Same rules apply as for `foldr`.+ewfoldr ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b+ewfoldr = ewfoldrI @_ @t @as @bs @asbs @b+{-# INLINE[1] ewfoldr #-}++-- | Right-associative strict fold of a DataFrame.+-- Same rules apply as for `foldr'`.+ewfoldr' ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b+ewfoldr' f z0 xs = ewfoldl f' id xs z0+ where+ f' k x z = k $! f x z+{-# INLINE ewfoldr' #-}++-- | Right-associative lazy fold of a DataFrame with an index.+-- Same rules apply as for `foldr`.+iwfoldr ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b+iwfoldr = iwfoldrI @_ @t @as @bs @asbs @b+{-# INLINE[1] iwfoldr #-}++-- | Right-associative strict fold of a DataFrame with an index.+-- Same rules apply as for `foldr'`.+iwfoldr' ::+ forall t as bs asbs b+ . SubSpace t as bs asbs+ => (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b+iwfoldr' f z0 xs = iwfoldl f' id xs z0+ where+ f' i k x z = k $! f i x z+{-# INLINE[1] iwfoldr' #-}++-- | Apply an applicative functor on each element (Lens-like traversal).+elementWise ::+ forall t as bs asbs s bs' asbs' f+ . (SubSpace t as bs asbs, SubSpace s as bs' asbs', Applicative f)+ => (DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s asbs' -> f (DataFrame t asbs)+elementWise = elementWiseI @_ @t @as @bs @asbs @s @bs' @asbs' @f+{-# INLINE[1] elementWise #-}++-- | Apply an applicative functor on each element with its index+-- (Lens-like indexed traversal).+indexWise ::+ forall t as bs asbs s bs' asbs' f+ . (SubSpace t as bs asbs, SubSpace s as bs' asbs', Applicative f)+ => (Idxs as -> DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s asbs' -> f (DataFrame t asbs)+indexWise = indexWiseI @_ @t @as @bs @asbs @s @bs' @asbs' @f+{-# INLINE[1] indexWise #-}++-- | Apply an applicative functor on each element (Lens-like traversal)+elementWise_ ::+ forall t as bs asbs f b+ . (SubSpace t as bs asbs, Applicative f)+ => (DataFrame t bs -> f b) -> DataFrame t asbs -> f ()+elementWise_ f = ewfoldr ((*>) . f) (pure ())+{-# INLINE elementWise_ #-}++-- | Apply an applicative functor on each element with its index+-- (Lens-like indexed traversal)+indexWise_ ::+ forall t as bs asbs f b+ . (SubSpace t as bs asbs, Applicative f)+ => (Idxs as -> DataFrame t bs -> f b) -> DataFrame t asbs -> f ()+indexWise_ f = iwfoldr (\i -> (*>) . f i) (pure ())+{-# INLINE indexWise_ #-}++-- | Apply a functor over a single element (simple lens)+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@) and any of the+-- corresponding indices fall outside of the DataFrame Dims, then the+-- argument Functor is not called and the result is @pure@ original DataFrame.+element ::+ forall t as bs asbs f+ . (SubSpace t as bs asbs, Applicative f)+ => Idxs as+ -> (DataFrame t bs -> f (DataFrame t bs))+ -> DataFrame t asbs -> f (DataFrame t asbs)+element i f df = case dimKind @(KindOfEl asbs) of+ DimKNat -> flip (update i) df <$> f (index i df)+ DimKXNat+ | XFrame (dfN :: DataFrame t asbsN) <- df+ , Just off <- cdIxM (getSteps (dims @asbsN) dfN) i+ -> flip (updateOffset off) df <$> f (indexOffset off df)+ | otherwise+ -> pure df+{-# INLINE element #-}+++-- | Map each element of the DataFrame to a monoid,+-- and combine the results.+ewfoldMap ::+ forall t as bs asbs m+ . (SubSpace t as bs asbs, Monoid m)+ => (DataFrame t bs -> m) -> DataFrame t asbs -> m+ewfoldMap f = ewfoldr (mappend . f) mempty+{-# INLINE ewfoldMap #-}++-- | Map each element of the DataFrame and its index to a monoid,+-- and combine the results.+iwfoldMap ::+ forall t as bs asbs m+ . (SubSpace t as bs asbs, Monoid m)+ => (Idxs as -> DataFrame t bs -> m) -> DataFrame t asbs -> m+iwfoldMap f = iwfoldr (\i -> mappend . f i) mempty+{-# INLINE iwfoldMap #-}++-- | Zip two spaces on a specified subspace element-wise (without index)+ewzip :: forall t as bs asbs l bsL asbsL r bsR asbsR+ . (SubSpace t as bs asbs, SubSpace l as bsL asbsL, SubSpace r as bsR asbsR)+ => (DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs)+ -> DataFrame l asbsL -> DataFrame r asbsR -> DataFrame t asbs+ewzip = iwzip . const+{-# INLINE ewzip #-}++-- | Zip two spaces on a specified subspace index-wise (with index).+iwzip :: forall t as bs asbs l bsL asbsL r bsR asbsR+ . (SubSpace t as bs asbs, SubSpace l as bsL asbsL, SubSpace r as bsR asbsR)+ => (Idxs as -> DataFrame l bsL -> DataFrame r bsR -> DataFrame t bs)+ -> DataFrame l asbsL -> DataFrame r asbsR -> DataFrame t asbs+iwzip f dfl dfr = case dimKind @(KindOfEl asbs) of+ DimKNat -> iwmap (\i x -> f i x (index i dfr)) dfl+ DimKXNat+ | XFrame (_ :: DataFrame l asbsLN) <- dfl+ , XFrame (_ :: DataFrame r asbsRN) <- dfr+ , asbs <- unsafeCoerce -- minimum spans+ $ zipWith min (listDims $ dims @asbsLN) (listDims $ dims @asbsRN) :: Dims asbs+ , bs <- (dims :: Dims bs)+ , as <- (dropSufDims bs asbs :: Dims as)+ -> withLiftedConcatList @'Nothing @('Just (DimsBound bs)) @'Nothing as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) -> case () of+ _ | Dict <- inferKnownBackend @t @asbsn+ , Dict <- inferKnownBackend @t @bsn+ -> XFrame (iwgen @t @asn @bsn @asbsn+ (\iN -> let i = liftIdxs iN+ in unliftXFrame (f i (index i dfl) (index i dfr))+ )+ )+{-# INLINE iwzip #-}++-- | DataFrames indexed by Nats and XNats require slightly different sets of+-- constraints to be sliced.+-- This family hides the difference, so that I could write one function for+-- both kinds.+type family CanSlice (t :: Type) (asbs :: [k]) :: Constraint where+ CanSlice t (asbs :: [Nat])+ = ( PrimArray t (DataFrame t asbs), Dimensions asbs )+ CanSlice t (asbs :: [XNat])+ = ( )++-- | Get a few contiguous elements.+--+-- In a sense, this is just a more complicated version of `sindex`.+--+-- If (@b ~ XN m@) then this function is unsafe and can throw+-- an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+sslice ::+ forall t b bi bd bs+ . ( KnownDimKind (KindOfEl bs)+ , CanSlice t (b :+ bs)+ , SubFrameIndexCtx b bi bd, KnownDim bd+ , PrimArray t (DataFrame t (bd :+ bs)))+ => Idx bi -> DataFrame t (b :+ bs) -> DataFrame t (bd :+ bs)+sslice = slice @t @b @bi @bd @'[] @bs @(b :+ bs) . (:* U)+{-# INLINE[1] sslice #-}++-- | Update a few contiguous elements.+--+-- In a sense, this is just a more complicated version of `supdate`.+--+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function updates only as+-- many elements as fits into the dataframe along this dimension (possibly none).+supdateSlice ::+ forall t b bi bd bs+ . ( KnownDimKind (KindOfEl bs)+ , CanSlice t (b :+ bs)+ , SubFrameIndexCtx b bi bd+ , KnownDim bd, ExactDims bs+ , PrimArray t (DataFrame t (bd :+ bs)))+ => Idx bi -> DataFrame t (bd :+ bs)+ -> DataFrame t (b :+ bs) -> DataFrame t (b :+ bs)+supdateSlice = updateSlice @t @b @bi @bd @'[] @bs @(b :+ bs) . (:* U)+{-# INLINE[1] supdateSlice #-}++-- | Get a few contiguous elements.+--+-- In a sense, this is just a more complicated version of `index`.+--+-- If any of the dims in @as@ or @b@ is unknown (@a ~ XN m@),+-- then this function is unsafe and can throw an `OutOfDimBounds` exception.+-- Otherwise, its safety is guaranteed by the type system.+slice ::+ forall (t :: Type) b bi bd as bs asbs+ . ( KnownDimKind (KindOfEl asbs)+ , CanSlice t asbs+ , SubFrameIndexCtx b bi bd, KnownDim bd+ , ConcatList as (b :+ bs) asbs+ , PrimArray t (DataFrame t (bd :+ bs)))+ => Idxs (as +: bi) -> DataFrame t asbs -> DataFrame t (bd :+ bs)+slice i = case dimKind @(KindOfEl asbs) of+ DimKNat+ -> withArrayContent broadcast+ (\steps off0 ba ->+ let (off, bsteps) = getOffAndStepsSub (I# off0) steps i (dim @bd)+ in fromElems bsteps (case off of I# o -> o) ba+ )+ DimKXNat+ -> \(XFrame (df :: DataFrame t asbsN)) -> withArrayContent broadcast+ (\steps off0 ba ->+ let (off, bsteps) = getOffAndStepsSub (I# off0) steps i (dim @bd)+ in fromElems bsteps (case off of I# o -> o) ba+ ) df+{-# INLINE[1] slice #-}++-- | Update a few contiguous elements.+--+-- In a sense, this is just a more complicated version of `update`.+--+-- If any of the dims in @as@ is unknown (@a ~ XN m@),+-- you may happen to update data beyond dataframe bounds.+-- In this case, the original DataFrame is returned.+-- If (@b ~ XN m@) and (@Idx bi + Dim bd > Dim b@), this function updates only as+-- many elements as fits into the dataframe along this dimension (possibly none).+updateSlice ::+ forall (t :: Type) b bi bd as bs asbs+ . ( KnownDimKind (KindOfEl asbs)+ , CanSlice t asbs+ , SubFrameIndexCtx b bi bd+ , KnownDim bd, ExactDims bs+ , ConcatList as (b :+ bs) asbs+ , PrimArray t (DataFrame t (bd :+ bs)))+ => Idxs (as +: bi) -> DataFrame t (bd :+ bs)+ -> DataFrame t asbs -> DataFrame t asbs+updateSlice i bdbsDf asbsDf = case dimKind @(KindOfEl asbs) of+ DimKNat -> runST $ do+ asbsM <- thawDataFrame asbsDf+ copyDataFrame i bdbsDf asbsM+ unsafeFreezeDataFrame asbsM+ DimKXNat+ | (XFrame (asbsDfN :: DataFrame t asbsN)) <- asbsDf -> runST $ do+ asbsM <- thawDataFrame @t @asbsN asbsDfN+ copyDataFrame i bdbsDf (castDataFrame @t @asbs asbsM)+ XFrame <$> unsafeFreezeDataFrame asbsM+{-# INLINE[1] updateSlice #-}+++-- | Get an element by its index in the dataframe.+-- This is a safe alternative to `(.!)` function when the index dimension+-- is not known at compile time (@a ~ XN m@).+slookup ::+ forall t (a :: XNat) (bs :: [XNat])+ . (All KnownDimType bs, PrimBytes t)+ => Idx a -> DataFrame t (a :+ bs) -> Maybe (DataFrame t bs)+slookup = Numeric.DataFrame.SubSpace.lookup @t @'[a] @bs @(a :+ bs) . (:*U)+{-# INLINE slookup #-}++-- | Get an element by its index in the dataframe.+-- This is a safe alternative to `index` function when some of the dimensions+-- are not known at compile time (@d ~ XN m@).+lookup ::+ forall t (as :: [XNat]) (bs :: [XNat]) (asbs :: [XNat])+ . (ConcatList as bs asbs, All KnownDimType bs, PrimBytes t)+ => Idxs as -> DataFrame t asbs -> Maybe (DataFrame t bs)+lookup i (XFrame (df :: DataFrame t asbsN))+ | asbsN <- dims @asbsN+ , Just off <- cdIxM (getSteps asbsN df) i+ , asbs <- XDims asbsN :: Dims asbs+ , (as, bs) <- prefSufDims i asbs+ = withLiftedConcatList @'Nothing @'Nothing @('Just asbsN) as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (_ :: Dims asbsn)+ -> case inferKnownBackend @t @bsn of+ Dict -> Just (XFrame (indexOffset @t @asn @bsn @asbsn off df))+ | otherwise = Nothing+{-# INLINE lookup #-}++-- | Get a few contiguous elements.+--+-- In a sense, this is just a more complicated version of `slookup`.+--+-- This is a safe alternative to `sslice` function when the slice dimension+-- is not known at compile time (@b ~ XN m@).+ssliceMaybe ::+ forall (t :: Type) (b :: XNat) (bi :: XNat) (bd :: XNat) (bs :: [XNat])+ . ( SubFrameIndexCtx b bi bd, KnownDim bd+ , All KnownDimType bs, PrimBytes t)+ => Idx bi -> DataFrame t (b :+ bs) -> Maybe (DataFrame t (bd :+ bs))+ssliceMaybe = sliceMaybe @t @b @bi @bd @'[] @bs @(b :+ bs) . (:* U)+{-# INLINE ssliceMaybe #-}+++-- | Get a few contiguous elements.+--+-- In a sense, this is just a more complicated version of `lookup`.+--+-- This is a safe alternative to `slice` function when some of the dimensions+-- are not known at compile time (@d ~ XN m@).+sliceMaybe ::+ forall (t :: Type) (b :: XNat) bi bd as bs asbs+ . ( SubFrameIndexCtx b bi bd, KnownDim bd+ , ConcatList as (b :+ bs) asbs+ , All KnownDimType bs, PrimBytes t)+ => Idxs (as +: bi) -> DataFrame t asbs -> Maybe (DataFrame t (bd :+ bs))+sliceMaybe i (XFrame (df :: DataFrame t asbsN))+ | asbsN <- dims @asbsN+ , bd <- dim @bd+ , Dict <- ( withKnownXDim @bd Dict+ :: Dict (KnownDim (DimBound bd), KnownDimType bd, FixedDim bd (DimBound bd)))+ , Just (I# off0, bsteps@(CumulDims (bbN:bN:_)))+ <- getOffAndStepsSubM 0 (getSteps asbsN df) i bd+ , bbN == bN * dimVal bd -- is there enough elements?+ , asbs <- XDims asbsN :: Dims asbs+ , XDims (Dims :: Dims bsN) <-+ unsafeCoerce (dropSome (listIdxs i) (listDims asbs)) :: Dims bs+ , Dict <- inferKnownBackend @t @(DimBound bd ': bsN)+ = Just $ XFrame @_ @t @(bd :+ bs) @(DimBound bd ': bsN)+ (withArrayContent broadcast+ (\_ off -> fromElems bsteps (off0 +# off)) df)+ | otherwise = Nothing+{-# INLINE sliceMaybe #-}+++-- | Operations on DataFrames+--+-- @as@ is an indexing dimensionality+--+-- @bs@ is an element dimensionality+--+-- @t@ is an underlying data type (i.e. Float, Int, Double)+--+class ( ConcatList as bs asbs+ , SubSpaceCtx t as bs asbs+ , PrimBytes t+ , KnownDimKind k+ ) => SubSpace (t :: Type) (as :: [k]) (bs :: [k]) (asbs :: [k])+ | asbs as -> bs, asbs bs -> as, as bs -> asbs where+ type SubSpaceCtx t as bs asbs :: Constraint++ joinDataFrameI :: PrimBytes (DataFrame t bs)+ => DataFrame (DataFrame t bs) as -> DataFrame t asbs+ indexOffsetI :: Int -> DataFrame t asbs -> DataFrame t bs+ updateOffsetI :: Int -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs+ indexI :: Idxs as -> DataFrame t asbs -> DataFrame t bs+ updateI :: Idxs as -> DataFrame t bs -> DataFrame t asbs -> DataFrame t asbs+ ewmapI :: forall s (bs' :: [k]) (asbs' :: [k])+ . SubSpace s as bs' asbs'+ => (DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s asbs' -> DataFrame t asbs+ iwmapI :: forall s (bs' :: [k]) (asbs' :: [k])+ . SubSpace s as bs' asbs'+ => (Idxs as -> DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s asbs' -> DataFrame t asbs+ ewgenI :: Dimensions as => DataFrame t bs -> DataFrame t asbs+ iwgenI :: Dimensions as => (Idxs as -> DataFrame t bs) -> DataFrame t asbs+ ewfoldlI :: (b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b+ iwfoldlI :: (Idxs as -> b -> DataFrame t bs -> b) -> b -> DataFrame t asbs -> b+ ewfoldrI :: (DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b+ iwfoldrI :: (Idxs as -> DataFrame t bs -> b -> b) -> b -> DataFrame t asbs -> b+ elementWiseI :: forall (s :: Type) (bs' :: [k]) (asbs' :: [k]) (f :: Type -> Type)+ . (Applicative f, SubSpace s as bs' asbs')+ => (DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s asbs' -> f (DataFrame t asbs)+ elementWiseI = indexWiseI . const+ {-# INLINE elementWiseI #-}+ indexWiseI :: forall (s :: Type) (bs' :: [k]) (asbs' :: [k]) (f :: Type -> Type)+ . (Applicative f, SubSpace s as bs' asbs')+ => (Idxs as -> DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s asbs' -> f (DataFrame t asbs)++instance ( ConcatList as bs asbs+ , SubSpaceCtx t as bs asbs+ ) => SubSpace t (as :: [Nat]) (bs :: [Nat]) (asbs :: [Nat]) where+ type SubSpaceCtx t as bs asbs+ = ( Dimensions as, Dimensions bs, Dimensions asbs+ , PrimArray t (DataFrame t asbs), PrimArray t (DataFrame t bs))++ joinDataFrameI+ | Dict <- inferKnownBackend @(DataFrame t bs) @as+ = withArrayContent ewgenI+ (\steps -> fromElems (steps `mappend` cumulDims (dims @bs)))++ indexOffsetI (I# off) = withArrayContent broadcast+ (\steps off0 -> fromElems (dropPref (dims @as) steps) (off0 +# off))+ {-# INLINE indexOffsetI #-}++ updateOffsetI off bs asbs = runST $ do+ asbsM <- thawDataFrame asbs+ copyDataFrameOff off bs asbsM+ unsafeFreezeDataFrame asbsM+ {-# INLINE updateOffsetI #-}++ indexI i = withArrayContent broadcast+ (\steps off0 -> fromElems (dropPref (dims @as) steps)+ (case cdIx steps i of I# off -> off0 +# off))+ {-# INLINE indexI #-}++ updateI i bs asbs = runST $ do+ asbsM <- thawDataFrame asbs+ copyDataFrame' i bs asbsM+ unsafeFreezeDataFrame asbsM+ {-# INLINE updateI #-}++ ewmapI (f :: DataFrame s bs' -> DataFrame t bs) (asbs' :: DataFrame s asbs')+ = case uniqueOrCumulDims asbs' of+ Left e -> ewgen (f (broadcast e))+ Right _ -> runST $ do+ let asTD = fromIntegral (totalDim' @as ) :: Int+ bsTD = fromIntegral (totalDim' @bs ) :: Int+ bs'TD = fromIntegral (totalDim' @bs') :: Int+ asbsM <- newDataFrame+ forM_ [0..asTD-1] $ \i ->+ copyDataFrameOff (i*bsTD) (f (indexOffset (i*bs'TD) asbs')) asbsM+ unsafeFreezeDataFrame asbsM+ {-# INLINE ewmapI #-}++ iwmapI f (asbs' :: DataFrame s asbs')+ = case uniqueOrCumulDims asbs' of+ Left e -> iwgen (flip f (broadcast e))+ Right _ -> runST $ do+ asbsM <- newDataFrame+ forM_ [minBound..maxBound] $ \i ->+ copyDataFrame' i (f i (index i asbs')) asbsM+ unsafeFreezeDataFrame asbsM+ {-# INLINE iwmapI #-}++ ewgenI bs = case uniqueOrCumulDims bs of+ Left a -> broadcast a+ Right bsSteps -> runST $ do+ let asTD = fromIntegral (totalDim' @as) :: Int+ bsTD = fromIntegral (cdTotalDim bsSteps)+ asbsM <- newDataFrame+ forM_ [0, bsTD .. asTD * bsTD - 1] $ \o -> copyDataFrameOff o bs asbsM+ unsafeFreezeDataFrame asbsM+ {-# INLINE ewgenI #-}++ iwgenI f = runST $ do+ asbsM <- newDataFrame+ forM_ [minBound..maxBound] $ \i -> copyDataFrame' i (f i) asbsM+ unsafeFreezeDataFrame asbsM+ {-# INLINE iwgenI #-}++ ewfoldlI f x0 asbs = case uniqueOrCumulDims asbs of+ Left a ->+ let b = broadcast a+ go i | i < 0 = x0+ | otherwise = f (go (i-1)) b+ in go (fromIntegral (totalDim' @as) - 1 :: Int)+ Right asbsSteps ->+ let bsSteps = dropPref (dims @as) asbsSteps+ bsLen = max 1 (fromIntegral (cdTotalDim bsSteps)) :: Int+ asbsLen = fromIntegral (cdTotalDim asbsSteps) :: Int+ go i | i < 0 = x0+ | otherwise = f (go (i - bsLen)) (indexOffset i asbs)+ in go (asbsLen - bsLen)+ {-# INLINE ewfoldlI #-}++ iwfoldlI f x0 asbs+ | not (nonVoidDims (dims @as)) = x0+ | otherwise = case uniqueOrCumulDims asbs of+ Left a ->+ let b = broadcast a+ go i | i == minBound = f i x0 b+ | otherwise = f i (go (pred i)) b+ in go maxBound+ Right _ ->+ let go i | i == minBound = f i x0 (index i asbs)+ | otherwise = f i (go (pred i)) (index i asbs)+ in go maxBound+ {-# INLINE iwfoldlI #-}++ ewfoldrI f x0 asbs = case uniqueOrCumulDims asbs of+ Left a ->+ let b = broadcast a+ go i | i >= totalDim' @as = x0+ | otherwise = f b (go (i+1))+ in go 0+ Right asbsSteps ->+ let bsSteps = dropPref (dims @as) asbsSteps+ bsLen = max 1 (fromIntegral (cdTotalDim bsSteps)) :: Int+ asbsLen = fromIntegral (cdTotalDim asbsSteps) :: Int+ go i | i >= asbsLen = x0+ | otherwise = f (indexOffset i asbs) (go (i + bsLen))+ in go 0+ {-# INLINE ewfoldrI #-}++ iwfoldrI f x0 asbs+ | not (nonVoidDims (dims @as)) = x0+ | otherwise = case uniqueOrCumulDims asbs of+ Left a ->+ let b = broadcast a+ go i | i == maxBound = f i b x0+ | otherwise = f i b (go (succ i))+ in go minBound+ Right _ ->+ let go i | i == maxBound = f i (index i asbs) x0+ | otherwise = f i (index i asbs) (go (succ i))+ in go minBound+ {-# INLINE iwfoldrI #-}++ indexWiseI (f :: Idxs as -> DataFrame s bs' -> f (DataFrame t bs))+ = fmap mkSTDF . iwfoldr fST (pure $ MakingDF (const (pure ())))+ where+ mkSTDF :: MakingDF t asbs -> DataFrame t asbs+ mkSTDF st = runST $ do+ df <- newDataFrame+ getMDF st df+ unsafeFreezeDataFrame df+ fST :: Idxs as -> DataFrame s bs'+ -> f (MakingDF t asbs) -> f (MakingDF t asbs)+ fST i bs' asbsSTF+ = (\r st -> MakingDF (\x -> copyDataFrame' i r x *> getMDF st x))+ <$> f i bs' <*> asbsSTF+ {-# INLINE indexWiseI #-}++-- | Hide ST monad state with a DataFrame in works inside a plain type.+newtype MakingDF t asbs+ = MakingDF { getMDF :: forall s . STDataFrame s t asbs -> ST s () }++-- | Checks if all of the dimensions are non-zero.+nonVoidDims :: Dims ns -> Bool+nonVoidDims = all (0 <) . listDims++dropPref :: Dims (ns :: [Nat]) -> CumulDims -> CumulDims+dropPref ds = CumulDims . dropSome (listDims ds) . unCumulDims++dropSome :: [Word] -> [Word] -> [Word]+dropSome [] xs = xs+dropSome (_:as) xs = dropSome as (tail xs)++instance ( ConcatList as bs asbs+ , SubSpaceCtx t as bs asbs+ ) => SubSpace t (as :: [XNat]) (bs :: [XNat]) (asbs :: [XNat]) where+ type SubSpaceCtx t as bs asbs+ = ( Dimensions bs, PrimBytes t, ExactDims bs+ , All KnownDimType as, All KnownDimType bs, All KnownDimType asbs)++ joinDataFrameI (XFrame (df :: DataFrame (DataFrame t bs) asN)) =+ let as = XDims (dims @asN) :: Dims as+ bs = dims :: Dims bs+ asbs = concatDims as bs :: Dims asbs+ in withLiftedConcatList @('Just asN) @('Just (DimsBound bs)) @'Nothing+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) -> case () of+ _ | Dict <- inferKnownBackend @t @bsn+ , Dict <- inferKnownBackend @t @asbsn+ -> XFrame @_ @t @asbs @asbsn $ withArrayContent+ -- know for sure its DataFrame t bsn+ (ewgen @t @asn @bsn @asbsn . unliftXFrame)+ (\steps -> fromElems (steps `mappend` cumulDims (dims @bs)))+ df+ {-# INLINE joinDataFrameI #-}++ indexOffsetI off (XFrame (df :: DataFrame t asbsN)) =+ let bs = dims :: Dims bs+ as = dropSufDims bs asbs :: Dims as+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @('Just (DimsBound bs)) @('Just asbsN)+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn)+ -> case inferKnownBackend @t @bsn of+ Dict -> XFrame (indexOffset @t @asn @bsn @asbsn off df)+ {-# INLINE indexOffsetI #-}++ updateOffsetI off (XFrame (bsDf :: DataFrame t bsN))+ (XFrame (asbsDf :: DataFrame t asbsN)) =+ let bs = dims :: Dims bs+ as = dropSufDims bs asbs :: Dims as+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @('Just bsN) @('Just asbsN)+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn)+ -> XFrame (updateOffset @t @asn @bsn @asbsn off bsDf asbsDf)+ {-# INLINE updateOffsetI #-}++ indexI i (XFrame (df :: DataFrame t asbsN)) =+ let bs = dims :: Dims bs+ as = dropSufDims bs asbs :: Dims as+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN)+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn)+ -> case inferKnownBackend @t @bsn of+ Dict -> XFrame (index @t @asn @bsn @asbsn (unsafeUnliftIdxs i) df)+ {-# INLINE indexI #-}++ updateI i (XFrame (bsDf :: DataFrame t bsN))+ (XFrame (asbsDf :: DataFrame t asbsN)) =+ let bs = dims :: Dims bs+ as = dropSufDims bs asbs :: Dims as+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @('Just bsN) @('Just asbsN)+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn)+ -> XFrame (update @t @asn @bsn @asbsn (unsafeUnliftIdxs i) bsDf asbsDf)+ {-# INLINE updateI #-}++ ewmapI :: forall s (bs' :: [XNat]) (asbs' :: [XNat])+ . SubSpace s as bs' asbs'+ => (DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s asbs' -> DataFrame t asbs+ ewmapI f (XFrame (df :: DataFrame s asbsN')) =+ let bs = dims :: Dims bs+ bs' = dims :: Dims bs'+ as = dropSufDims bs' asbs' :: Dims as+ asbs' = XDims (dims @asbsN') :: Dims asbs'+ asbs = concatDims as bs :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN')+ as bs' asbs' $+ \(Dims :: Dims asn) (Dims :: Dims bsn') (Dims :: Dims asbsn') ->+ withLiftedConcatList @('Just asn) @'Nothing @'Nothing+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) ->+ case inferKnownBackend @t @asbsn of+ Dict+ | Dict <- inferKnownBackend @t @bsn+ , Dict <- inferKnownBackend @s @bsn'+ -> XFrame (ewmap @t @asn @bsn @asbsn @s @bsn' @asbsn'+ (unliftXFrame . f . XFrame) df)+ {-# INLINE ewmapI #-}++ iwmapI :: forall s (bs' :: [XNat]) (asbs' :: [XNat])+ . SubSpace s as bs' asbs'+ => (Idxs as -> DataFrame s bs' -> DataFrame t bs)+ -> DataFrame s asbs' -> DataFrame t asbs+ iwmapI f (XFrame (df :: DataFrame s asbsN')) =+ let bs = dims :: Dims bs+ bs' = dims :: Dims bs'+ as = dropSufDims bs' asbs' :: Dims as+ asbs' = XDims (dims @asbsN') :: Dims asbs'+ asbs = concatDims as bs :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN')+ as bs' asbs' $+ \(Dims :: Dims asn) (Dims :: Dims bsn') (Dims :: Dims asbsn') ->+ withLiftedConcatList @('Just asn) @'Nothing @'Nothing+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) -> case () of+ _ | Dict <- inferKnownBackend @t @asbsn+ , Dict <- inferKnownBackend @t @bsn+ , Dict <- inferKnownBackend @s @bsn'+ -> XFrame (iwmap @t @asn @bsn @asbsn @s @bsn' @asbsn'+ (\i x -> unliftXFrame (f (liftIdxs i) (XFrame x))) df)+ {-# INLINE iwmapI #-}+++ ewgenI (XFrame (df :: DataFrame t bsN)) =+ let as = dims :: Dims as+ bs = dims :: Dims bs+ asbs = concatDims as bs :: Dims asbs+ in withLiftedConcatList @'Nothing @('Just bsN) @'Nothing as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) ->+ case inferKnownBackend @t @asbsn of+ Dict -> XFrame (ewgen @t @asn @bsn @asbsn df)+ {-# INLINE ewgenI #-}++ iwgenI f =+ let as = dims :: Dims as+ bs = dims :: Dims bs+ asbs = concatDims as bs :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @'Nothing as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) -> case () of+ _ | Dict <- inferKnownBackend @t @asbsn+ , Dict <- inferKnownBackend @t @bsn+ -> XFrame (iwgen @t @asn @bsn @asbsn (unliftXFrame . f . liftIdxs))+ {-# INLINE iwgenI #-}++ ewfoldlI f x0 (XFrame (df :: DataFrame s asbsN)) =+ let as = dropSufDims bs asbs :: Dims as+ bs = dims :: Dims bs+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN) as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) ->+ case inferKnownBackend @t @bsn of+ Dict -> ewfoldl @t @asn @bsn @asbsn (\b x -> f b (XFrame x)) x0 df+ {-# INLINE ewfoldlI #-}++ iwfoldlI f x0 (XFrame (df :: DataFrame s asbsN)) =+ let as = dropSufDims bs asbs :: Dims as+ bs = dims :: Dims bs+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN) as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) ->+ case inferKnownBackend @t @bsn of+ Dict -> iwfoldl @t @asn @bsn @asbsn+ (\i b x -> f (liftIdxs i) b (XFrame x)) x0 df+ {-# INLINE iwfoldlI #-}++ ewfoldrI f x0 (XFrame (df :: DataFrame s asbsN)) =+ let as = dropSufDims bs asbs :: Dims as+ bs = dims :: Dims bs+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN) as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) ->+ case inferKnownBackend @t @bsn of+ Dict -> ewfoldr @t @asn @bsn @asbsn (\x -> f (XFrame x)) x0 df+ {-# INLINE ewfoldrI #-}++ iwfoldrI f x0 (XFrame (df :: DataFrame s asbsN)) =+ let as = dropSufDims bs asbs :: Dims as+ bs = dims :: Dims bs+ asbs = XDims (dims @asbsN) :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN) as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) ->+ case inferKnownBackend @t @bsn of+ Dict -> iwfoldr @t @asn @bsn @asbsn+ (\i x -> f (liftIdxs i) (XFrame x)) x0 df+ {-# INLINE iwfoldrI #-}++ indexWiseI :: forall s (bs' :: [XNat]) (asbs' :: [XNat]) f+ . (Applicative f, SubSpace s as bs' asbs')+ => (Idxs as -> DataFrame s bs' -> f (DataFrame t bs))+ -> DataFrame s asbs' -> f (DataFrame t asbs)+ indexWiseI f (XFrame (df :: DataFrame s asbsN')) =+ let bs = dims :: Dims bs+ bs' = dims :: Dims bs'+ as = dropSufDims bs' asbs' :: Dims as+ asbs' = XDims (dims @asbsN') :: Dims asbs'+ asbs = concatDims as bs :: Dims asbs+ in withLiftedConcatList @'Nothing @'Nothing @('Just asbsN')+ as bs' asbs' $+ \(Dims :: Dims asn) (Dims :: Dims bsn') (Dims :: Dims asbsn') ->+ withLiftedConcatList @('Just asn) @'Nothing @'Nothing+ as bs asbs $+ \(Dims :: Dims asn) (Dims :: Dims bsn) (Dims :: Dims asbsn) -> case () of+ _ | Dict <- inferKnownBackend @t @asbsn+ , Dict <- inferKnownBackend @t @bsn+ , Dict <- inferKnownBackend @s @bsn'+ -> XFrame <$>+ (indexWise @t @asn @bsn @asbsn @s @bsn' @asbsn'+ (\i x -> unliftXFrame <$> f (liftIdxs i) (XFrame x)) df+ )+ {-# INLINE indexWiseI #-}++dropSufDims :: ConcatList as bs asbs+ => Dims bs -> Dims asbs -> Dims as+dropSufDims = unsafeCoerce dropSuf+ where+ dropSuf :: [Word] -> [Word] -> [Word]+ dropSuf bs asbs = fst $ foldr f ([], bs) asbs+ f :: Word -> ([Word], [Word]) -> ([Word], [Word])+ f x (as, []) = (x:as,[])+ f _ (_, _:rs) = ([], rs)++prefSufDims :: ConcatList as bs asbs+ => TypedList f as -> Dims asbs -> (Dims as, Dims bs)+prefSufDims = unsafeCoerce f+ where+ f :: [Word] -> [Word] -> ([Word], [Word])+ f [] asbs = ([], asbs)+ f (_:is) ~(a:sbs) = first (a:) (f is sbs)++concatDims :: ConcatList as bs asbs+ => Dims as -> Dims bs -> Dims asbs+concatDims = TL.concat++{-+A trick here is that both alternatives of 'MayEq' have the same representation+of a single equality constraint.+Thus, I can unsafeCoerce an equality later into MayEq to significantly+reduce amount of boilerplate.+ -}+type family MayEq (mns :: (Maybe [Nat])) (ns :: [Nat]) :: Constraint where+ MayEq 'Nothing bs = bs ~ bs+ MayEq ('Just as) bs = as ~ bs++-- Very unsafe function, because it does not check the content of XDims at all.+withLiftedConcatList ::+ forall (asm :: Maybe [Nat]) (bsm :: Maybe [Nat]) (asbsm :: Maybe [Nat])+ (asx :: [XNat]) (bsx :: [XNat]) (asbsx :: [XNat]) r+ . -- (ConcatList asx bsx asbsx) =>+ Dims asx -> Dims bsx -> Dims asbsx+ -> ( forall (asn :: [Nat]) (bsn :: [Nat]) (asbsn :: [Nat])+ . ( ConcatList asn bsn asbsn+ , FixedDims asx asn, FixedDims bsx bsn, FixedDims asbsx asbsn+ , MayEq asm asn, MayEq bsm bsn, MayEq asbsm asbsn)+ => Dims asn -> Dims bsn -> Dims asbsn -> r+ ) -> r+withLiftedConcatList (XDims (asn :: Dims asn))+ (XDims (bsn :: Dims bsn))+ (XDims (asbsn :: Dims asbsn)) k+ | Dict <- unsafeEqTypes @_ @asbsn @(Concat asn bsn)+ , Dict <- unsafeCoerce (Dict @(asn ~ asn)) :: Dict (MayEq asm asn)+ , Dict <- unsafeCoerce (Dict @(bsn ~ bsn)) :: Dict (MayEq bsm bsn)+ , Dict <- unsafeCoerce (Dict @(asbsn ~ asbsn)) :: Dict (MayEq asbsm asbsn)+ , Dict <- inferConcat @asn @bsn @asbsn = k asn bsn asbsn++unliftXFrame :: forall (t :: Type) (xns :: [XNat]) (ns :: [Nat])+ . (ExactDims xns, FixedDims xns ns)+ => DataFrame t xns -> DataFrame t ns+unliftXFrame (XFrame x) = unsafeCoerce x+{-# INLINE unliftXFrame #-}++unsafeEqTypes :: forall k (a :: k) (b :: k)+ . Dict (a ~ b)+unsafeEqTypes = unsafeCoerce (Dict :: Dict (a ~ a))
src/Numeric/DataFrame/Type.hs view
@@ -12,6 +12,7 @@ {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-}@@ -22,7 +23,9 @@ {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-}-+#ifdef UNSAFE_INDICES+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}+#endif {- | The core @easytensor@ types. -}@@ -38,24 +41,29 @@ , DataFrame ( SingleFrame, MultiFrame, XFrame, (:*:), Z , S, DF2, DF3, DF4, DF5, DF6, DF7, DF8, DF9) #endif+ , scalar, unScalar+ , IndexFrame (..), SubFrameIndexCtx -- * Flexible assembling and disassembling , PackDF, packDF, unpackDF , appendDF, consDF, snocDF , fromFlatList, fromListWithDefault, fromList- , constrainDF+ , constrainDF, asDiag -- * Infer type class instances , KnownBackend (), DFBackend, KnownBackends , InferKnownBackend (..), inferPrimElem -- * Re-exports- , Dim (..), Idx (), XNat (..), N, XN, Dims, Idxs, TypedList (..)+ , Dim (..), Idx (), Nat, XNat (..), N, XN, Dims, Idxs, TypedList (..) , PrimBytes (), bSizeOf, bAlignOf, bFieldOffsetOf , PrimArray (), ixOff ) where +#if !(MIN_VERSION_base(4,13,0))+import Data.Proxy (Proxy)+#endif+import Control.Arrow (second, (***)) import Data.Data-import Data.Proxy (Proxy)-import Data.Semigroup hiding (All)+import Data.Semigroup hiding (All, Min) import Data.Type.Lits import Data.Void import Foreign.Storable (Storable (..))@@ -66,7 +74,9 @@ import qualified Text.ParserCombinators.ReadPrec as Read import qualified Text.Read as Read import qualified Text.Read.Lex as Read+import Unsafe.Coerce +import Numeric.Basics import Numeric.DataFrame.Internal.PrimArray import Numeric.Dimensions import Numeric.PrimBytes@@ -75,12 +85,11 @@ import qualified Numeric.ProductOrd.Partial as Partial import Numeric.TypedList (typeables) -import {-# SOURCE #-} Numeric.DataFrame.Internal.Backend (DFBackend,- KnownBackend)-import {-# SOURCE #-} qualified Numeric.DataFrame.Internal.Backend as Backend-import Numeric.DataFrame.Internal.BackendI ()+import Numeric.DataFrame.Internal.BackendI (DFBackend, KnownBackend)+import qualified Numeric.DataFrame.Internal.BackendI as Backend + -- | Keep data in a primitive data frame -- and maintain information about Dimensions in the type system data family DataFrame (t :: l) (xs :: [k])@@ -97,7 +106,7 @@ -- Pattern-match against its constructor to get a Nat-indexed data frame. data instance DataFrame (ts :: l) (xns :: [XNat]) = forall (ns :: [Nat])- . (KnownXNatTypes xns, FixedDims xns ns, Dimensions ns, KnownBackends ts ns)+ . (All KnownDimType xns, FixedDims xns ns, Dimensions ns, KnownBackends ts ns) => XFrame (DataFrame ts ns) -- | Data frame that has an unknown dimensionality at compile time.@@ -113,21 +122,36 @@ {-# COMPLETE Z, (:*:) #-} +#define PLEASE_STYLISH_HASKELL \+ forall (xs :: [Type]) (ns :: [Nat]) . () => \+ forall (y :: Type) (ys :: [Type]) . (xs ~ (y ': ys)) => \+ DataFrame y ns -> DataFrame ys ns -> DataFrame xs ns -- | Constructing a @MultiFrame@ using DataFrame columns-pattern (:*:) :: forall (xs :: [Type]) (ns :: [Nat]) . ()- => forall (y :: Type) (ys :: [Type]) . (xs ~ (y ': ys))- => DataFrame y ns -> DataFrame ys ns -> DataFrame xs ns+pattern (:*:) :: PLEASE_STYLISH_HASKELL pattern (:*:) x xs <- (MultiFrame (DataFrame' x :* (MultiFrame -> xs))) where (:*:) x (MultiFrame xs) = MultiFrame (DataFrame' x :* xs) infixr 6 :*:+#undef PLEASE_STYLISH_HASKELL -- | Empty MultiFrame pattern Z :: forall (xs :: [Type]) (ns :: [Nat]) . () => (xs ~ '[]) => DataFrame xs ns pattern Z = MultiFrame U +-- | Convert a scalar back to an ordinary type+unScalar :: DataFrame t ('[] :: [Nat]) -> t+-- rely on that Scalar is just two times newtype alias to t+unScalar = unsafeCoerce+{-# INLINE unScalar #-}++-- | Convert any type to a scalar wrapper+scalar :: t -> DataFrame t ('[] :: [Nat])+-- rely on that Scalar is just two times newtype alias to t+scalar = unsafeCoerce+{-# INLINE scalar #-}+ -- | I use this kind-polymorphic constraint to generalize @XFrame@ and @SomeDataFrame@ -- over @SingleFrame@ and @MultiFrame@. type family KnownBackends (ts :: l) (ns :: [Nat]) :: Constraint where@@ -137,8 +161,8 @@ (KnownBackend t ns, KnownBackends ts ns) -- | Allow inferring @KnownBackends@ if you know the dimensions and the element types.-class InferKnownBackend (t :: k) ds where- -- Infer @KnownBackends@ if you know the dimensions and the element types.+class InferKnownBackend t (ds :: [Nat]) where+ -- | Infer @KnownBackends@ if you know the dimensions and the element types. inferKnownBackend :: Dict (KnownBackends t ds) instance (PrimBytes t, Dimensions ds) => InferKnownBackend (t :: Type) ds where@@ -146,15 +170,17 @@ instance (RepresentableList ts, All PrimBytes ts, Dimensions ds) => InferKnownBackend (ts :: [Type]) ds where- inferKnownBackend = go (tList @_ @ts)+ inferKnownBackend = go (tList @ts) where- go :: forall ss . All PrimBytes ss => TypeList ss -> Dict (KnownBackends ss ds)+ go :: forall ss . All PrimBytes ss+ => TypeList ss -> Dict (KnownBackends ss ds) go U = Dict go ((_ :: Proxy t) :* ts) = case Backend.inferKnownBackend @t @ds of Dict -> case go ts of Dict -> Dict + -- | All component data frames must satisfy a given constraint. type family AllFrames (f :: Type -> Constraint) (ts :: [Type]) (ds :: [Nat]) :: Constraint where@@ -162,6 +188,107 @@ AllFrames f (t ': ts) ds = (f (DataFrame t ds), AllFrames f ts ds) +-- | Index one dimension deep into a @DataFrame@.+class IndexFrame t d ds where+ -- | Index one dimension deep into a @DataFrame@.+ --+ -- Note, this function does not provide indexing safety at the type level;+ -- it throws an `OutOfDimBounds` exception if an index is out of bounds+ -- (unless @unsafeindices@ package flag is enabled, which is even more dangerous).+ (!) :: DataFrame t (d ': ds) -> Word -> DataFrame t ds++instance (PrimArray t (DataFrame t '[d]), KnownDim d)+ => IndexFrame (t :: Type) (d :: Nat) '[] where+ {-# INLINE (!) #-}+ (!) df i+#ifndef UNSAFE_INDICES+ | i >= dimVal' @d = outOfDimBoundsNoCallStack "IndexFrame.(!)"+ i (dimVal' @d) Nothing Nothing+ | otherwise+#endif+ = S (ixOff (fromIntegral i) df)++instance {-# INCOHERENT #-}+ ( PrimArray t (DataFrame t (d ': ds))+ , KnownDim d, KnownBackend t ds)+ => IndexFrame (t :: Type) (d :: Nat) ds where+ {-# INLINE (!) #-}+ (!) df i+#ifndef UNSAFE_INDICES+ | i >= dimVal' @d = outOfDimBoundsNoCallStack "IndexFrame.(!)"+ i (dimVal' @d) Nothing Nothing+ | otherwise+#endif+ = withArrayContent broadcast+ (\(CumulDims ~(_:ss@(s:_))) off0 ->+ fromElems (CumulDims ss) (case s*i of W# off -> off0 +# word2Int# off)+ ) df+++instance IndexFrame ('[] :: [Type]) (d :: Nat) ds where+ {-# INLINE (!) #-}+ (!) Z _ = Z++instance (IndexFrame t d ds, IndexFrame ts d ds)+ => IndexFrame ((t ': ts) :: [Type]) (d :: Nat) ds where+ {-# INLINE (!) #-}+ (!) (df :*: dfs) i = df ! i :*: dfs ! i++instance PrimBytes t+ => IndexFrame (t :: Type) (xd :: XNat) xds where+ {-# INLINE (!) #-}+ (!) (XFrame df) i+ | D :* (Dims :: Dims ns) <- dims `inSpaceOf` df+ , Dict <- inferKnownBackend @t @ns+ = XFrame (df ! i)+ (!) _ _ = error "IndexFrame: impossible pattern"++instance (RepresentableList ts, All PrimBytes ts)+ => IndexFrame (ts :: [Type]) (xd :: XNat) xds where+ {-# INLINE (!) #-}+ (!) (XFrame df) i+ | (D :: Dim n) :* (Dims :: Dims ns) <- dims `inSpaceOf` df+ , Dict <- getTsEvs @ts @n @ns tList+ = XFrame (df ! i)+ where+ getTsEvs :: forall (as :: [Type]) (d :: Nat) (ds :: [Nat])+ . ( All PrimBytes as, KnownBackends as (d ': ds)+ , Dimensions ds, KnownDim d)+ => TypeList as -> Dict (IndexFrame as d ds, KnownBackends as ds)+ getTsEvs U = Dict+ getTsEvs ((_ :: Proxy a) :* as')+ | Dict <- getTsEvs @_ @d @ds as'+ , Dict <- inferKnownBackend @a @ds = Dict+ (!) _ _ = error "IndexFrame: impossible pattern"++{- |+This type family describes two strategies for dealing with dimensions when+slicing a @DataFrame@.++If the original space dimension (@d@) is fixed at compile time+(@d :: Nat@ or @d ~ N n@), then we enforce the safety of a slicing operation+with types:+ the index space is bounded by the size of the original space+ minus the subframe size.++If the original space dimension (@d@) is not fixed (@d ~ XN m@), then we give up.+Just let the user do the slicing as easy as possible, and throw an `OutOfDimBounds`+exception at runtime if the index plus the sliced size is greater than the+original size of the DataFrame.+ -}+type family SubFrameIndexCtx (d :: k) (idxN :: k) (subN :: k) :: Constraint where+ SubFrameIndexCtx (n :: Nat) idxN subN+ = (n + 1) ~ (idxN + subN)+ SubFrameIndexCtx (N n) idxN subN+ = ( (n + 1) ~ (DimBound idxN + DimBound subN)+ , idxN ~ N (DimBound idxN)+ , subN ~ N (DimBound subN)+ )+ SubFrameIndexCtx (XN m) idxN subN+ = ( idxN ~ XN m+ , subN ~ N (DimBound subN)+ )+ deriving instance Typeable (DataFrame (t :: l) (xs :: [k])) deriving instance ( Data (DataFrame t xs) , Typeable t, Typeable xs, Typeable k, Typeable l)@@ -184,17 +311,24 @@ => Fractional (DataFrame t ds) deriving instance Floating (DFBackend t ds) => Floating (DataFrame t ds)+deriving instance Epsilon (DFBackend t ds)+ => Epsilon (DataFrame t ds) deriving instance Real (DFBackend t ds) => Real (DataFrame t ds)+deriving instance RealExtras (DFBackend t ds)+ => RealExtras (DataFrame t ds) deriving instance RealFrac (DFBackend t ds) => RealFrac (DataFrame t ds) deriving instance RealFloat (DFBackend t ds) => RealFloat (DataFrame t ds)+deriving instance RealFloatExtras (DFBackend t ds)+ => RealFloatExtras (DataFrame t ds) deriving instance PrimBytes (DFBackend t ds) => PrimBytes (DataFrame t ds) deriving instance (PrimArray t (DFBackend t ds), PrimBytes t) => PrimArray t (DataFrame t ds) + instance Ord (NonTransitive.ProductOrd (DFBackend t ds)) => Ord (NonTransitive.ProductOrd (DataFrame t ds)) where (>) = coerce ((>) @(NonTransitive.ProductOrd (DFBackend t ds)))@@ -263,7 +397,7 @@ ) => Show (DataFrame (t :: Type) (ds :: [Nat])) where showsPrec p x = case dims @ds of U -> showParen (p >= 10)- $ showString "S " . showsPrec 10 (unsafeCoerce# x :: t)+ $ showString "S " . showsPrec 10 (unScalar x) D0 :* _ -> showString "DF0" d :* _ -> showParen (p >= 10) $ unpackDF' ( \Dict f ->@@ -310,24 +444,24 @@ instance (All Read ts, All PrimBytes ts, RepresentableList ts, Dimensions ds) => Read (DataFrame (ts :: [Type]) (ds :: [Nat])) where- readPrec = readFixedMultiDF (tList @Type @ts) (dims @ds)+ readPrec = readFixedMultiDF (tList @ts) (dims @ds) readList = Read.readListDefault readListPrec = Read.readListPrecDefault -instance (Read t, PrimBytes t, BoundedDims ds, All KnownXNatType ds)+instance (Read t, PrimBytes t, BoundedDims ds, All KnownDimType ds) => Read (DataFrame (t :: Type) (ds :: [XNat])) where readPrec = Read.parens . Read.prec 10 $ do Read.lift . Read.expect $ Read.Ident "XFrame"- Read.step $ readPrecBoundedDF (minDims @XNat @ds)+ Read.step $ readPrecBoundedDF (minimalDims @ds) readList = Read.readListDefault readListPrec = Read.readListPrecDefault instance ( All Read ts, All PrimBytes ts, RepresentableList ts- , BoundedDims ds, All KnownXNatType ds)+ , BoundedDims ds, All KnownDimType ds) => Read (DataFrame (ts :: [Type]) (ds :: [XNat])) where readPrec = Read.parens . Read.prec 10 $ do Read.lift . Read.expect $ Read.Ident "XFrame"- Read.step $ readBoundedMultiDF (tList @Type @ts) (minDims @XNat @ds)+ Read.step $ readBoundedMultiDF (tList @ts) (minimalDims @ds) readList = Read.readListDefault readListPrec = Read.readListPrecDefault @@ -343,7 +477,7 @@ => Read (SomeDataFrame (ts :: [Type])) where readPrec = Read.parens . Read.prec 10 $ do Read.lift . Read.expect $ Read.Ident "SomeDataFrame"- Read.step $ readSomeMultiDF (tList @Type @ts)+ Read.step $ readSomeMultiDF (tList @ts) readList = Read.readListDefault readListPrec = Read.readListPrecDefault @@ -381,7 +515,7 @@ -} readPrecBoundedDF :: forall (t :: Type) (ds :: [XNat])- . (Read t, PrimBytes t, KnownXNatTypes ds)+ . (Read t, PrimBytes t, All KnownDimType ds) => Dims ds -> Read.ReadPrec (DataFrame t ds) -- reading scalar readPrecBoundedDF U@@ -414,7 +548,7 @@ = Read.parens $ lookLex >>= \case Read.Ident ('D':'F':s) | Just (Dx (n :: Dim n)) <- (Read.readMaybe ('D':s) :: Maybe SomeDim)- , Just Dict <- indeedLE m n -- check if the DF dim is not less than m+ , Just Dict <- lessOrEqDim m n -> case n of D0 -> do -- need to repack it under different constraint dims XFrame x <- readPrecBoundedDF @t (Dn D0 :* xns)@@ -428,12 +562,6 @@ (followedBy x) _ -> Read.pfail- where- indeedLE :: forall (a ::Nat) (b :: Nat) . Dim a -> Dim b -> Maybe (Dict (a <= b))- indeedLE a b = case compareDim a b of- SLT -> Just Dict- SEQ -> Just Dict- SGT -> Nothing {- In this case we know Nothing about the dimensionality of a DataFrame.@@ -451,7 +579,7 @@ Read.+++ (lookLex >>= \case Read.Ident ('D':'F':s)- | Just (Dx (d :: Dim d)) <- (Read.readMaybe ('D':s) :: Maybe SomeDim)+ | Just (Dx (d@D :: Dim d)) <- (Read.readMaybe ('D':s) :: Maybe SomeDim) -> case d of D0 | Dict <- inferKnownBackend @t @'[0] -> SomeDataFrame <$> readPrecFixedDF @t (D0 :* U)@@ -479,7 +607,7 @@ return (x :*: xs) readBoundedMultiDF :: forall (ts :: [Type]) (ds :: [XNat])- . (All Read ts, All PrimBytes ts, KnownXNatTypes ds)+ . (All Read ts, All PrimBytes ts, All KnownDimType ds) => TypeList ts -> Dims ds -> Read.ReadPrec (DataFrame ts ds)@@ -562,9 +690,9 @@ -- | A scalar DataFrame is just a newtype wrapper on a value. pattern S :: forall (t :: Type) . t -> DataFrame t ('[] :: [Nat]) -- rely on that Scalar is just two times newtype alias to t-pattern S x <- (unsafeCoerce# -> x)+pattern S x <- (unScalar -> x) where- S = unsafeCoerce#+ S = scalar {-# COMPLETE S #-} @@ -800,25 +928,27 @@ -> Int -> r) -> DataFrame t (d ': ds) -> r-unpackDF' k df+unpackDF' k | d :* Dims <- dims @(d ': ds) , Dict <- inferKnownBackend @t @(d ': ds) , Dict <- inferKnownBackend @t @ds- = case arrayContent# df of- (# x | #)- | e <- broadcast x- -> let f :: forall (zr :: RuntimeRep) (z :: TYPE zr)- . (Int -> DataFrame t ds -> z) -> Int -> z- f consume = (`consume` e)- in k Dict f 0- (# | (# cdims, off, arr #) #)- | cd <- CumulDims . tail $ unCumulDims cdims- , td <- cdTotalDim# cd- , n <- case dimVal d of W# w -> word2Int# w- -> let f :: forall (zr :: RuntimeRep) (z :: TYPE zr)- . (Int -> DataFrame t ds -> z) -> Int -> z- f consume (I# o) = consume (I# (o -# td)) (fromElems cd o arr)- in k Dict f (I# (off +# td *# (n -# 1#)))+ = withArrayContent+ ( \x ->+ let e = broadcast x+ f :: forall (zr :: RuntimeRep) (z :: TYPE zr)+ . (Int -> DataFrame t ds -> z) -> Int -> z+ f consume = (`consume` e)+ in k Dict f 0+ )+ ( \cdims off arr ->+ let cd = CumulDims . tail $ unCumulDims cdims+ td = cdTotalDim# cd+ n = case dimVal d of W# w -> word2Int# w+ f :: forall (zr :: RuntimeRep) (z :: TYPE zr)+ . (Int -> DataFrame t ds -> z) -> Int -> z+ f consume (I# o) = consume (I# (o -# td)) (fromElems cd o arr)+ in k Dict f (I# (off +# td *# (n -# 1#)))+ ) | otherwise = error "Numeric.DataFrame.Type.unpackDF: impossible args" @@ -878,17 +1008,20 @@ -- | Unsafely copy two PrimBytes values into a third one.-unsafeAppendPB :: (PrimBytes x, PrimBytes y, PrimBytes z)+unsafeAppendPB :: forall x y z+ . (PrimBytes x, PrimBytes y, PrimBytes z) => x -> y -> z-unsafeAppendPB x y- | sx <- byteSize x- = case runRW#- ( \s0 -> case newByteArray# (sx +# byteSize y) s0 of- (# s1, mba #) -> unsafeFreezeByteArray# mba- ( writeBytes mba sx y- ( writeBytes mba 0# x s1))- ) of (# _, r #) -> fromBytes 0# r-+unsafeAppendPB x y = go (byteSize x) (byteSize y)+ where+ go :: Int# -> Int# -> z+ go 0# _ = fromBytes (byteOffset y) (getBytes y)+ go _ 0# = fromBytes (byteOffset x) (getBytes x)+ go sx sy = case runRW#+ ( \s0 -> case newByteArray# (sx +# sy) s0 of+ (# s1, mba #) -> unsafeFreezeByteArray# mba+ ( writeBytes mba sx y+ ( writeBytes mba 0# x s1))+ ) of (# _, r #) -> fromBytes 0# r -- | Construct a DataFrame from a list of smaller DataFrames. --@@ -911,35 +1044,76 @@ -- Pattern-match against the resulting @XFrame@ to find out its dimensionality. -- -- You must not provide an infinite list as an argument.-fromList :: forall (t :: Type) (ds :: [Nat])- . (PrimBytes t, Dimensions ds)- => [DataFrame t ds] -> DataFrame t (XN 0 ': AsXDims ds)+fromList :: forall (t :: Type) (ds :: [Nat]) (xds :: [XNat])+ . (PrimBytes t, Dimensions ds, xds ~ Map N ds, ds ~ UnMap N xds)+ => [DataFrame t ds] -> DataFrame t (XN 0 ': xds) fromList xs | Dx (D :: Dim n) <- someDimVal . fromIntegral $ length xs , Dict <- inferKnownBackend @t @(n ': ds)- , ds@(AsXDims (XDims ds')) <- dims @ds- , Just Dict <- sameDims ds ds'+ , Dict <- unsafeEqTypes @_ @ds @(DimsBound xds)+ , Dict <- inferExactFixedDims (dims @ds) = XFrame (fromListWithDefault @t @n @ds undefined xs) | otherwise- = error "Numeri.DataFrame.Type/fromList: impossible arguments"-+ = case Dict @(ds ~ UnMap N xds) of+ Dict -> -- just make GHC not complain about unused constraints.+ error "Numeri.DataFrame.Type/fromList: impossible arguments" -- | Try to convert between @XNat@-indexed DataFrames. -- -- This is useful for imposing restrictions on unknown DataFrames, -- e.g. increasing the minimum number of elements.-constrainDF :: forall (ds :: [XNat]) (ys :: [XNat]) (l :: Type) (ts :: l)- . (BoundedDims ds, All KnownXNatType ds)- => DataFrame ts ys -> Maybe (DataFrame ts ds)-constrainDF (XFrame (df :: DataFrame ts ns))+constrainDF :: forall (ds :: [XNat]) (ys :: [XNat]) t+ . (BoundedDims ds, All KnownDimType ds)+ => DataFrame t ys -> Maybe (DataFrame t ds)+constrainDF (XFrame (df :: DataFrame t ns)) | ns <- dims @ns- = case constrainDims @XNat @ds ns of+ = case constrainDims @ds ns of Just (XDims (Dims :: Dims ms)) | Dict <- unsafeEqTypes @[Nat] @ns @ms -> Just $ XFrame df _ -> Nothing +-- | Place elements of a vector on the main diagonal of a matrix;+-- fill the rest of the matrix with zeroes.+--+-- Note, this function is naturally generalized onto higher dimensions+-- (which can be seen from the type signature).+--+-- Note, the argument of this function does not fully determine its result type.+-- This may cause some obscure type errors.+-- Specify type parameters @n@ and @m@ explicitly or make sure the result type is fixed.+asDiag :: forall (n :: Nat) (m :: Nat) (ds :: [Nat]) (t :: Type)+ . ( Dimensions ds, KnownDim n, KnownDim m+ , KnownBackend t (Min n m :+ ds)+ , KnownBackend t (n :+ m :+ ds)+ , PrimBytes t)+ => DataFrame t (Min n m :+ ds)+ -> DataFrame t (n :+ m :+ ds)+asDiag x+ | elemSize <- byteSize @t undefined+ , dMinNM@D <- minDim (D @n) (D @m)+ , xba <- getBytes x+ , ds <- dims @ds+ , steps@(CumulDims (elemsNR:_:elemsNE:_)) <- cumulDims (D @n :* D @m :* ds)+ , m <- dimVal' @m+ , bsE <- case elemsNE of W# w -> word2Int# w *# elemSize+ , bsR <- case elemsNR of W# w -> word2Int# w *# elemSize+ , bsShift <- case m + 1 of W# w -> word2Int# w *# bsE+ , bsLim <- case m * dimVal dMinNM of W# w -> word2Int# w *# bsE+ = case runRW#+ ( \s0 -> case newByteArray# bsR s0 of+ (# s1, mba #) ->+ let go offSrc offDst s+ | isTrue# (offDst >=# bsLim) = s+ | otherwise = go (offSrc +# bsE) (offDst +# bsShift)+ (copyByteArray# xba offSrc mba offDst bsE s)+ in unsafeFreezeByteArray# mba+ (go (byteOffset x) 0# (setByteArray# mba 0# bsR 0# s1)) + ) of (# _, r #) -> fromElems steps 0# r+ | otherwise+ = error "Numeri.DataFrame.Type/asDiag: impossible arguments"+{-# INLINE asDiag #-} -- Need this for @packDF'@ to make @Int -> c z@ a proper second-order type -- parameterized by the result type.@@ -955,7 +1129,7 @@ U | S x <- v -> z S `k` x D :* (Dims :: Dims ns)- -> case inferTypeableCons @_ @ds of+ -> case inferTypeableCons @ds of Dict -> -- PLZ don't ask me how does this work unpackDF' (\_ f -> runOff $ packDF'@@ -965,7 +1139,7 @@ gunfold k z _ = case typeableDims @ds of U -> k (z S) D :* (Dims :: Dims ns)- -> case inferTypeableCons @_ @ds of Dict -> packDF' k z+ -> case inferTypeableCons @ds of Dict -> packDF' k z toConstr _ = case typeableDims @ds of U -> scalarFrameConstr d :* _ -> singleFrameConstr $ dimVal d@@ -980,11 +1154,11 @@ instance (AllFrames Data ts ds, Typeable ts, Typeable ds) => Data (DataFrame (ts :: [Type]) (ds :: [Nat])) where gfoldl _ z Z = z Z- gfoldl k z (x :*: xs) = case inferTypeableCons @Type @ts of+ gfoldl k z (x :*: xs) = case inferTypeableCons @ts of Dict -> z (:*:) `k` x `k` xs- gunfold k z _ = case typeables @Type @ts of+ gunfold k z _ = case typeables @ts of U -> z Z- _ :* _ -> case inferTypeableCons @_ @ts of Dict -> k (k (z (:*:)))+ _ :* _ -> case inferTypeableCons @ts of Dict -> k (k (z (:*:))) toConstr Z = multiFrameZConstr toConstr (_ :*: _) = multiFrameConsConstr dataTypeOf _ = dataFrameDataType [multiFrameZConstr, multiFrameConsConstr]@@ -1043,13 +1217,14 @@ fromSingleFrame (dd@D :* (Dims :: Dims ds')) x | Dict <- inferKnownBackend @t @ds , Dict <- inferKnownBackend @t @ds'- = G.M1 $ case arrayContent# x of- (# e | #) -> fillRep @_ @ds' (const $ broadcast e) 0 dd- (# | (# cdims, off, arr #) #)- | cd <- CumulDims . tail $ unCumulDims cdims- , td <- cdTotalDim# cd- -> fillRep @_ @ds'- (\(W# i) -> fromElems cd (off +# td *# word2Int# i) arr) 0 dd+ = G.M1 $ withArrayContent+ (\e -> fillRep @_ @ds' (const $ broadcast e) 0 dd)+ (\cdims off arr ->+ let cd = CumulDims . tail $ unCumulDims cdims+ td = cdTotalDim# cd+ in fillRep @_ @ds'+ (\(W# i) -> fromElems cd (off +# td *# word2Int# i) arr) 0 dd+ ) x where fillRep :: forall (n :: Nat) (ns :: [Nat]) . (Word -> DataFrame t ns)@@ -1105,7 +1280,7 @@ where d2 = divDim d D2 d2' = d2 `plusDim` modDim d D2-+{-# INLINE toSingleFrame #-} type family MultiFrameRepNil (ts :: [Type]) :: (Type -> Type) where MultiFrameRepNil '[] = G.C1 ('G.MetaCons "Z" 'G.PrefixI 'False) G.U1@@ -1136,5 +1311,245 @@ +instance (AllFrames Eq ts ds, AllFrames Ord ts ds)+ => Ord (DataFrame (ts :: [Type]) ds) where+ compare Z Z = EQ+ compare (a :*: as) (b :*: bs) = compare a b <> compare as bs+++withFixedDF1 :: forall (l :: Type) (ts :: l) (xns :: [XNat])+ (rep :: RuntimeRep) (r :: TYPE rep)+ . xns ~ Map 'N (DimsBound xns)+ => ( forall (ns :: [Nat])+ . ( All KnownDimType xns, FixedDims xns ns+ , Dimensions ns+ , KnownBackends ts ns+ , ns ~ DimsBound xns+ , xns ~ Map 'N ns+ ) => DataFrame ts ns -> r+ ) -> DataFrame ts xns -> r+withFixedDF1 f (XFrame (df :: DataFrame ts ds))+ | Dict <- unsafeEqTypes @_ @ds @(DimsBound xns)+ = f df+{-# INLINE withFixedDF1 #-}++withFixedDF2 :: forall (l :: Type) (ts :: l) (xns :: [XNat])+ (rep :: RuntimeRep) (r :: TYPE rep)+ . xns ~ Map 'N (DimsBound xns)+ => ( forall (ns :: [Nat])+ . ( All KnownDimType xns, FixedDims xns ns+ , Dimensions ns+ , KnownBackends ts ns+ , ns ~ DimsBound xns+ , xns ~ Map 'N ns+ ) => DataFrame ts ns -> DataFrame ts ns -> r+ ) -> DataFrame ts xns -> DataFrame ts xns -> r+withFixedDF2 f (XFrame (a :: DataFrame ts as)) (XFrame (b :: DataFrame ts bs))+ | Dict <- unsafeEqTypes @_ @as @(DimsBound xns)+ , Dict <- unsafeEqTypes @_ @bs @(DimsBound xns)+ = f a b+{-# INLINE withFixedDF2 #-}++instance ( xns ~ Map 'N (DimsBound xns)+ , Eq (DataFrame ts xns)+ , Ord (DataFrame ts (DimsBound xns))+ ) => Ord (DataFrame (ts :: l) (xns :: [XNat])) where+ compare = withFixedDF2 compare+ (>=) = withFixedDF2 (>=)+ (<=) = withFixedDF2 (<=)+ (>) = withFixedDF2 (>)+ (<) = withFixedDF2 (<)+ min = withFixedDF2 ((.) XFrame . min)+ max = withFixedDF2 ((.) XFrame . max)++instance ( Dimensions xns+ , KnownBackends ts (DimsBound xns)+ , Num (DataFrame ts (DimsBound xns))+ ) => Num (DataFrame (ts :: l) (xns :: [XNat])) where+ (+) = withKnownXDims @xns $ withFixedDF2 ((.) XFrame . (+))+ (-) = withKnownXDims @xns $ withFixedDF2 ((.) XFrame . (-))+ (*) = withKnownXDims @xns $ withFixedDF2 ((.) XFrame . (*))+ negate = withKnownXDims @xns $ withFixedDF1 (XFrame . negate)+ abs = withKnownXDims @xns $ withFixedDF1 (XFrame . abs)+ signum = withKnownXDims @xns $ withFixedDF1 (XFrame . signum)+ fromInteger = withKnownXDims @xns+ (XFrame . fromInteger @(DataFrame ts (DimsBound xns)))++instance ( Dimensions xns+ , KnownBackends ts (DimsBound xns)+ , Fractional (DataFrame ts (DimsBound xns))+ ) => Fractional (DataFrame (ts :: l) (xns :: [XNat])) where+ (/) = withKnownXDims @xns $ withFixedDF2 ((.) XFrame . (/))+ recip = withKnownXDims @xns $ withFixedDF1 (XFrame . recip)+ fromRational = withKnownXDims @xns+ (XFrame . fromRational @(DataFrame ts (DimsBound xns)))++instance ( Dimensions xns+ , KnownBackends ts (DimsBound xns)+ , Floating (DataFrame ts (DimsBound xns))+ ) => Floating (DataFrame (ts :: l) (xns :: [XNat])) where+ pi = withKnownXDims @xns $ XFrame (pi :: DataFrame ts (DimsBound xns))+ exp = withKnownXDims @xns $ withFixedDF1 (XFrame . exp)+ log = withKnownXDims @xns $ withFixedDF1 (XFrame . log)+ sqrt = withKnownXDims @xns $ withFixedDF1 (XFrame . sqrt)+ sin = withKnownXDims @xns $ withFixedDF1 (XFrame . sin)+ cos = withKnownXDims @xns $ withFixedDF1 (XFrame . cos)+ tan = withKnownXDims @xns $ withFixedDF1 (XFrame . tan)+ asin = withKnownXDims @xns $ withFixedDF1 (XFrame . asin)+ acos = withKnownXDims @xns $ withFixedDF1 (XFrame . acos)+ atan = withKnownXDims @xns $ withFixedDF1 (XFrame . atan)+ sinh = withKnownXDims @xns $ withFixedDF1 (XFrame . sinh)+ cosh = withKnownXDims @xns $ withFixedDF1 (XFrame . cosh)+ tanh = withKnownXDims @xns $ withFixedDF1 (XFrame . tanh)+ asinh = withKnownXDims @xns $ withFixedDF1 (XFrame . asinh)+ acosh = withKnownXDims @xns $ withFixedDF1 (XFrame . acosh)+ atanh = withKnownXDims @xns $ withFixedDF1 (XFrame . atanh)+ (**) = withKnownXDims @xns $ withFixedDF2 ((.) XFrame . (**))+ logBase = withKnownXDims @xns $ withFixedDF2 ((.) XFrame . logBase)++instance ( xns ~ Map 'N (DimsBound xns)+ , ProductOrder (DataFrame ts (DimsBound xns))+ ) => ProductOrder (DataFrame (ts :: l) (xns :: [XNat])) where+ cmp = withFixedDF2 cmp++instance ( Dimensions xns+ , KnownBackends ts (DimsBound xns)+ , Bounded (DataFrame ts (DimsBound xns))+ ) => Bounded (DataFrame (ts :: l) (xns :: [XNat])) where+ minBound = withKnownXDims @xns (XFrame (minBound :: DataFrame ts (DimsBound xns)))+ maxBound = withKnownXDims @xns (XFrame (maxBound :: DataFrame ts (DimsBound xns)))++instance ( Dimensions xns+ , KnownBackends ts (DimsBound xns)+ , PrimBytes (DataFrame ts (DimsBound xns))+ ) => PrimBytes (DataFrame (ts :: l) (xns :: [XNat])) where+ type PrimFields (DataFrame ts xns)+ = PrimFields (DataFrame ts (DimsBound xns))+ getBytes = withKnownXDims @xns $ withFixedDF1 getBytes+ getBytesPinned = withKnownXDims @xns $ withFixedDF1 getBytesPinned+ fromBytes i ba+ = withKnownXDims @xns (XFrame (fromBytes i ba :: DataFrame ts (DimsBound xns)))+ readBytes mba i s0+ | (# s1, (a :: DataFrame ts (DimsBound xns)) #) <- readBytes mba i s0+ = (# s1, withKnownXDims @xns (XFrame a) #)+ writeBytes mba i = withKnownXDims @xns $ withFixedDF1 (writeBytes mba i)+ readAddr addr s0+ | (# s1, (a :: DataFrame ts (DimsBound xns)) #) <- readAddr addr s0+ = (# s1, withKnownXDims @xns (XFrame a) #)+ writeAddr = withKnownXDims @xns $ withFixedDF1 writeAddr+ byteSize _ = withKnownXDims @xns (byteSize @(DataFrame ts (DimsBound xns)) undefined)+ byteAlign _ = withKnownXDims @xns (byteAlign @(DataFrame ts (DimsBound xns)) undefined)+ byteOffset = withKnownXDims @xns (withFixedDF1 byteOffset)+ byteFieldOffset n _+ = withKnownXDims @xns (byteFieldOffset @(DataFrame ts (DimsBound xns)) n undefined)+ indexArray ba i+ = withKnownXDims @xns (XFrame (indexArray ba i :: DataFrame ts (DimsBound xns)))+ readArray mba i s0+ | (# s1, (a :: DataFrame ts (DimsBound xns)) #) <- readArray mba i s0+ = (# s1, withKnownXDims @xns (XFrame a) #)+ writeArray mba i = withKnownXDims @xns (withFixedDF1 (writeArray mba i))++instance ( Dimensions xns+ , KnownBackend t (DimsBound xns)+ , PrimArray t (DataFrame t (DimsBound xns))+ , PrimBytes t+ ) => PrimArray t (DataFrame t (xns :: [XNat])) where+ broadcast#+ = withKnownXDims @xns (XFrame . broadcast# @t @(DataFrame t (DimsBound xns)))+ ix# i = withKnownXDims @xns (withFixedDF1 (ix# i))+ gen# cd f s0+ | (# s1, (a :: DataFrame t (DimsBound xns)) #) <- gen# cd f s0+ = (# s1, withKnownXDims @xns (XFrame a) #)+ upd# cd i t = withKnownXDims @xns (withFixedDF1 (XFrame . upd# cd i t))+ withArrayContent# f g = withKnownXDims @xns (withFixedDF1 (withArrayContent f g))+ offsetElems = withKnownXDims @xns (withFixedDF1 offsetElems)+ uniqueOrCumulDims = withKnownXDims @xns (withFixedDF1 uniqueOrCumulDims)+ fromElems# cd i ba+ = withKnownXDims @xns (XFrame (fromElems cd i ba :: DataFrame t (DimsBound xns)))+++-- The following instance only make sense on scalars.++instance ( Enum (DataFrame ts ('[] :: [Nat]))+ , KnownBackends ts ('[] :: [Nat])+ ) => Enum (DataFrame (ts :: l) ('[] :: [XNat])) where+ succ (XFrame x) = XFrame (succ x)+ pred (XFrame x) = XFrame (pred x)+ toEnum = XFrame . toEnum+ fromEnum (XFrame x) = fromEnum x+ enumFrom (XFrame x) = map XFrame $ enumFrom x+ enumFromThen (XFrame x) (XFrame y) = map XFrame $ enumFromThen x y+ enumFromTo (XFrame x) (XFrame y) = map XFrame $ enumFromTo x y+ enumFromThenTo (XFrame x) (XFrame y) (XFrame z)+ = map XFrame $ enumFromThenTo x y z++instance ( Epsilon (DataFrame ts ('[] :: [Nat]))+ , KnownBackends ts ('[] :: [Nat])+ , Eq (DataFrame ts ('[] :: [XNat]))+ ) => Epsilon (DataFrame (ts :: l) ('[] :: [XNat])) where+ epsilon = XFrame epsilon++instance ( Real (DataFrame ts ('[] :: [Nat]))+ , KnownBackends ts ('[] :: [Nat])+ , Eq (DataFrame ts ('[] :: [XNat]))+ ) => Real (DataFrame (ts :: l) ('[] :: [XNat])) where+ toRational (XFrame x) = toRational x++instance ( Integral (DataFrame ts ('[] :: [Nat]))+ , KnownBackends ts ('[] :: [Nat])+ , Eq (DataFrame ts ('[] :: [XNat]))+ ) => Integral (DataFrame (ts :: l) ('[] :: [XNat])) where+ quot (XFrame x) (XFrame y) = XFrame (quot x y)+ rem (XFrame x) (XFrame y) = XFrame (rem x y)+ div (XFrame x) (XFrame y) = XFrame (div x y)+ mod (XFrame x) (XFrame y) = XFrame (mod x y)+ quotRem (XFrame x) (XFrame y) = (XFrame *** XFrame) (quotRem x y)+ divMod (XFrame x) (XFrame y) = (XFrame *** XFrame) (divMod x y)+ toInteger (XFrame x) = toInteger x++instance ( RealExtras (DataFrame t ('[] :: [Nat]))+ , KnownBackend t ('[] :: [Nat])+ , Eq t+ ) => RealExtras (DataFrame (t :: Type) ('[] :: [XNat])) where+ copysign (XFrame x) (XFrame y) = XFrame (copysign x y)++instance ( RealFrac (DataFrame t ('[] :: [Nat]))+ , KnownBackend t ('[] :: [Nat])+ , Eq t+ ) => RealFrac (DataFrame (t :: Type) ('[] :: [XNat])) where+ properFraction (XFrame x) = second XFrame (properFraction x)+ truncate (XFrame x) = truncate x+ round (XFrame x) = round x+ ceiling (XFrame x) = ceiling x+ floor (XFrame x) = floor x++instance ( RealFloat (DataFrame t ('[] :: [Nat]))+ , KnownBackend t ('[] :: [Nat])+ , Eq t+ ) => RealFloat (DataFrame (t :: Type) ('[] :: [XNat])) where+ floatRadix = const (floatRadix @(DataFrame t ('[] :: [Nat])) undefined)+ floatDigits = const (floatDigits @(DataFrame t ('[] :: [Nat])) undefined)+ floatRange = const (floatRange @(DataFrame t ('[] :: [Nat])) undefined)+ decodeFloat (XFrame x) = decodeFloat x+ encodeFloat i = XFrame . encodeFloat i+ exponent (XFrame x) = exponent x+ significand (XFrame x) = XFrame (significand x)+ scaleFloat i (XFrame x) = XFrame (scaleFloat i x)+ isNaN (XFrame x) = isNaN x+ isInfinite (XFrame x) = isInfinite x+ isDenormalized (XFrame x) = isDenormalized x+ isNegativeZero (XFrame x) = isNegativeZero x+ isIEEE (XFrame x) = isIEEE x+ atan2 (XFrame y) (XFrame x) = XFrame (atan2 y x)++instance ( RealFloatExtras (DataFrame t ('[] :: [Nat]))+ , KnownBackend t ('[] :: [Nat])+ , Eq t+ ) => RealFloatExtras (DataFrame (t :: Type) ('[] :: [XNat])) where+ hypot (XFrame x) (XFrame y) = XFrame (hypot x y)+ maxFinite = XFrame maxFinite+++ unsafeEqTypes :: forall k (a :: k) (b :: k) . Dict (a ~ b)-unsafeEqTypes = unsafeCoerce# (Dict :: Dict (a ~ a))+unsafeEqTypes = unsafeCoerce (Dict :: Dict (a ~ a))
src/Numeric/Matrix.hs view
@@ -3,7 +3,6 @@ , SquareMatrix (..) , MatrixDeterminant (..) , MatrixInverse (..)- , MatrixLU (..), LUFact (..) , Matrix , HomTransform4 (..) , Mat22f, Mat23f, Mat24f@@ -14,10 +13,9 @@ , Mat42d, Mat43d, Mat44d , mat22, mat33, mat44 , (%*)- , pivotMat, luSolve ) where import Numeric.DataFrame.Internal.Backend () import Numeric.Matrix.Internal-import Numeric.Matrix.Internal.Mat44d ()-import Numeric.Matrix.Internal.Mat44f ()+import Numeric.Matrix.Internal.Double ()+import Numeric.Matrix.Internal.Float ()
+ src/Numeric/Matrix/Bidiagonal.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++module Numeric.Matrix.Bidiagonal+ ( BiDiag (..), biDiag, bidiagonalHouseholder+ ) where++import Control.Monad+import Control.Monad.ST+import Data.Kind+import Numeric.Basics+import Numeric.DataFrame.ST+import Numeric.DataFrame.SubSpace+import Numeric.DataFrame.Type+import Numeric.Dimensions+import Numeric.Matrix.Internal+import Numeric.Scalar.Internal+import Numeric.Subroutine.Householder+import Numeric.Vector.Internal+++-- | Put two vectors on the main and first upper diagonal.+biDiag :: forall (t :: Type) (n :: Nat) (m :: Nat)+ . (PrimBytes t, Num t)+ => Dims '[n,m]+ -> Vector t (Min n m)+ -> Vector t (Min n m)+ -> Matrix t n m+biDiag (dn@D :* dm@D :* U) a b = runST $ do+ dnm@D <- pure $ minDim dn dm+ rPtr <- thawDataFrame 0+ forM_ [0 .. dimVal dnm - 1] $ \i -> do+ writeDataFrame rPtr (Idx i :* Idx i :* U) $ a ! i+ when (i+1 < dimVal dm) $+ writeDataFrame rPtr (Idx i :* Idx (i+1) :* U) $ b ! i+ unsafeFreezeDataFrame rPtr++-- | Decomposition of a matrix \( A = U B V^\intercal \) such that+-- \( U \) and \( V \) are orthogonal and \( B \) is bidiagonal.+data BiDiag (t :: Type) (n :: Nat) (m :: Nat)+ = BiDiag+ { bdU :: Matrix t n n+ -- ^ \( U \) left orthogonal matrix+ , bdUDet :: Scalar t+ -- ^ A shortcut for evaluating a determinant of \( |U| = \pm 1 \)+ , bdAlpha :: Vector t (Min n m)+ -- ^ Main diagonal of \( B \)+ , bdBeta :: Vector t (Min n m)+ -- ^ First upper diagonal of \( B \);+ -- its last element equals zero if \( n \geq m \)+ , bdV :: Matrix t m m+ -- ^ \( B \) left orthogonal matrix+ , bdVDet :: Scalar t+ -- ^ A shortcut for evaluating a determinant of \( |V| = \pm 1 \)+ }++deriving instance ( Show t, PrimBytes t+ , KnownDim n, KnownDim m, KnownDim (Min n m))+ => Show (BiDiag t n m)+deriving instance ( Eq t, PrimBytes t+ , KnownDim n, KnownDim m, KnownDim (Min n m)+ , KnownBackend t '[Min n m])+ => Eq (BiDiag t n m)++{- |+Decompose a matrix \( A = U B V^\intercal \) such that+\( U \) and \( V \) are orthogonal and \( B \) is bidiagonal.++The first returned number+ -}+bidiagonalHouseholder ::+ forall (t :: Type) (n :: Nat) (m :: Nat)+ . (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m)+ => Matrix t n m+ -> BiDiag t n m+bidiagonalHouseholder a = runST $ do+ D <- pure $ minDim (dim @n) (dim @m)+ tmpNPtr <- newDataFrame+ tmpMPtr <- newDataFrame+ uPtr <- thawDataFrame eye+ bPtr <- thawDataFrame a+ vPtr <- thawDataFrame eye+ (ud, vd) <-+ let f (ud, vd) i = do+ ud' <- householderReflectionInplaceL tmpNPtr uPtr bPtr+ (Idx (i - 1) :* Idx (i - 1) :* U)+ vd' <- householderReflectionInplaceR tmpMPtr vPtr bPtr+ (Idx (i - 1) :* Idx i :* U)+ return (ud /= ud', vd /= vd')+ in foldM f (False, False) [1 .. lim - 1]++ udn <- householderReflectionInplaceL tmpNPtr uPtr bPtr+ (Idx (lim - 1) :* Idx (lim - 1) :* U)+ vdn <- if (m > lim)+ then householderReflectionInplaceR tmpMPtr vPtr bPtr+ (Idx (lim - 1) :* Idx lim :* U)+ else pure False+ bdU <- unsafeFreezeDataFrame uPtr+ bdV <- unsafeFreezeDataFrame vPtr+ b <- unsafeFreezeDataFrame bPtr+ let bdAlpha = iwgen @t @'[Min n m]+ (\(Idx i :* U) -> index (Idx i :* Idx i :* U) b)+ bdBeta = iwgen @t @'[Min n m]+ (\(Idx i :* U) -> if i+1 < m then index (Idx i :* Idx (i+1) :* U) b else 0)+ bdUDet = if ud /= udn then -1 else 1+ bdVDet = if vd /= vdn then -1 else 1+ return BiDiag {..}+ where+ n = dimVal' @n+ m = dimVal' @m+ lim = max 1 (min n m)
src/Numeric/Matrix/Internal.hs view
@@ -17,7 +17,6 @@ , SquareMatrix (..) , MatrixDeterminant (..) , MatrixInverse (..)- , MatrixLU (..), LUFact (..) , Matrix , HomTransform4 (..) , Mat22f, Mat23f, Mat24f@@ -28,21 +27,13 @@ , Mat42d, Mat43d, Mat44d , mat22, mat33, mat44 , (%*)- , pivotMat, luSolve ) where -import Control.Monad (foldM)-import Control.Monad.ST-import Data.Foldable (foldl', forM_)-import Data.List (delete) import GHC.Base import Numeric.DataFrame.Contraction ((%*)) import Numeric.DataFrame.Internal.PrimArray-import Numeric.DataFrame.ST-import Numeric.DataFrame.SubSpace import Numeric.DataFrame.Type import Numeric.Dimensions-import Numeric.PrimBytes import Numeric.Scalar.Internal import Numeric.Vector.Internal @@ -70,29 +61,6 @@ inverse :: Matrix t n n -> Matrix t n n --- | Result of LU factorization with Partial Pivoting--- @ PA = LU @.-data LUFact t n- = LUFact- { luLower :: Matrix t n n- -- ^ Lower triangular matrix @L@.- -- All elements on the diagonal of @L@ equal @1@.- , luUpper :: Matrix t n n- -- ^ Upper triangular matrix @U@- , luPerm :: Matrix t n n- -- ^ Row permutation matrix @P@- , luPermSign :: Scalar t- -- ^ Sign of permutation @luPermSign == det . luPerm@- }--deriving instance (Show t, PrimBytes t, KnownDim n) => Show (LUFact t n)-deriving instance (Eq (Matrix t n n), Eq t) => Eq (LUFact t n)--class MatrixLU t (n :: Nat) where- -- | Compute LU factorization with Partial Pivoting- lu :: Matrix t n n -> LUFact t n-- -- | Operations on 4x4 transformation matrices and vectors in homogeneous coordinates. -- All angles are specified in radians. --@@ -260,214 +228,3 @@ = let f 0# = ix# 0# df f k = ix# k df + f (k -# n1) in scalar $ f (n *# n -# 1#)---instance ( KnownDim n, Ord t, Fractional t- , PrimBytes t- , PrimArray t (Matrix t n n)- , PrimArray t (Vector t n)- , PrimBytes (Vector t n)- , PrimBytes (Matrix t n n)- )- => MatrixInverse t n where- inverse m = runST $ do- m' <- newDataFrame- indexWise_ @t @'[n] @'[n] @'[n, n] @_ @()- (\is ->- indexWise_ @t @'[n] @'[] @'[n] @_ @()- (\(j:*U) -> writeDataFrame m' (j:*is)) . luSolve luM- ) eye- unsafeFreezeDataFrame m'- where- luM = lu m-- -- much nicer, but probably slower version:- -- (it is not particularly fast to do an extra transpose- -- before calculating the inverse)- -- ewmap (luSolve . lu $ transpose m) eye----instance ( KnownDim n, Ord t, Fractional t- , PrimBytes t, PrimArray t (Matrix t n n))- => MatrixDeterminant t n where- det m = prodF (luUpper f) * prodF (luLower f) * luPermSign f- where- f = lu m- n = case dimVal' @n of W# w -> word2Int# w- n1 = n +# 1#- nn1 = n *# n -# 1#- prodF a = scalar $ prodF' nn1 a- prodF' 0# a = ix# 0# a- prodF' k a = ix# k a * prodF' (k -# n1) a---instance ( KnownDim n, Ord t, Fractional t- , PrimBytes t, PrimArray t (Matrix t n n))- => MatrixLU t n where- lu m' = case runRW# go of- (# _, (# bu, bl #) #) -> LUFact- { luLower = fromElems steps 0# bl- , luUpper = fromElems steps 0# bu- , luPerm = p- , luPermSign = si- }- where- dn = dimVal' @n- dnn = dn * dn- steps = CumulDims [dnn, dn, 1]- (m, p, si) = pivotMat m'- n = case dn of W# w -> word2Int# w- nn = case dnn of W# w -> word2Int# w- tbs = byteSize @t undefined- bsize = nn *# tbs- ixm i j = ix# (i *# n +# j) m- loop :: (Int# -> a -> State# s -> (# State# s, a #))- -> Int# -> Int# -> a -> State# s -> (# State# s, a #)- loop f i k x s- | isTrue# (i ==# k) = (# s, x #)- | otherwise = case f i x s of- (# s', y #) -> loop f ( i +# 1# ) k y s'- go s0- | (# s1, mbl #) <- newByteArray# bsize s0- , (# s2, mbu #) <- newByteArray# bsize s1- , s3 <- setByteArray# mbl 0# bsize 0# s2- , s4 <- setByteArray# mbu 0# bsize 0# s3- , readL <- \i j -> readArray @t mbl (i *# n +# j)- , readU <- \i j -> readArray @t mbu (i *# n +# j)- , writeL <- \i j -> writeArray @t mbl (i *# n +# j)- , writeU <- \i j -> writeArray @t mbu (i *# n +# j)- , computeU <- \i j ->- let f k x s- | (# s' , ukj #) <- readU k j s- , (# s'', lik #) <- readL i k s'- = (# s'', x - ukj * lik #)- in loop f 0# i (ixm i j)- , computeL' <- \i j ->- let f k x s- | (# s' , ukj #) <- readU k j s- , (# s'', lik #) <- readL i k s'- = (# s'', x - ukj * lik #)- in loop f 0# j (ixm i j)- , (# sr, () #) <-- loop- ( \j _ sj -> case sj of- sj0- | sj1 <- writeL j j 1 sj0- , (# sj2, () #) <- loop- ( \i _ sij0 -> case computeU i j sij0 of- (# sij1, uij #) -> (# writeU i j uij sij1, () #)- ) 0# j () sj1- , (# sj3, ujj #) <- computeU j j sj2- , sj4 <- writeU j j ujj sj3- -> case ujj of- 0 -> loop- ( \i _ sij -> (# writeL i j 0 sij, () #)- ) (j +# 1#) n () sj4- x -> loop- ( \i _ sij0 -> case computeL' i j sij0 of- (# sij1, lij #) -> (# writeL i j (lij / x) sij1, () #)- ) (j +# 1#) n () sj4- ) 0# n () s4- , (# sf0, bl #) <- unsafeFreezeByteArray# mbl sr- , (# sf1, bu #) <- unsafeFreezeByteArray# mbu sf0- = (# sf1, (# bu, bl #) #)----- | Solve @Ax = b@ problem given LU decomposition of A.-luSolve :: forall (t :: Type) (n :: Nat)- . ( KnownDim n, Fractional t- , PrimArray t (Matrix t n n), PrimArray t (Vector t n))- => LUFact t n -> Vector t n -> Vector t n-luSolve LUFact {..} b = x- where- -- Pb = LUx- pb = luPerm %* b- n# = case dimVal' @n of W# w -> word2Int# w- n = I# n#- -- Ly = Pb- y :: Vector t n- y = runST $ do- my <- newDataFrame- let ixA (I# i) (I# j) = scalar $ ix# (i *# n# +# j) luLower- ixB (I# i) = scalar $ ix# i pb- forM_ [0..n-1] $ \i -> do- v <- foldM ( \v j -> do- dj <- readDataFrameOff my j- return $ v - dj * ixA i j- ) (ixB i) [0..i-1]- writeDataFrameOff my i v- unsafeFreezeDataFrame my- -- Ux = y- x = runST $ do- mx <- newDataFrame- let ixA (I# i) (I# j) = scalar $ ix# (i *# n# +# j) luUpper- ixB (I# i) = scalar $ ix# i y- forM_ [n-1, n-2 .. 0] $ \i -> do- v <- foldM ( \v j -> do- dj <- readDataFrameOff mx j- return $ v - dj * ixA i j- ) (ixB i) [i+1..n-1]- writeDataFrameOff mx i (v / ixA i i)- unsafeFreezeDataFrame mx----- | Permute rows that the largest magnitude elements in columns are on diagonals.------ Invariants of result matrix:--- * forall j >= i: |M[i,i]| >= M[j,i]--- * if M[i,i] == 0 then forall j >= i: |M[i+1,i+1]| >= M[j,i+1]-pivotMat :: forall (t :: Type) (n :: Nat)- . (KnownDim n, PrimArray t (Matrix t n n), Ord t, Num t)- => Matrix t n n -> ( Matrix t n n -- permutated matrix- , Matrix t n n -- permutation matrix- , Scalar t -- sign of permutation matrix- )-pivotMat mat- = ( let f ( _, [] ) = undefined- f ( j, i:is )- | j == n = f (0, is)- | otherwise = (# (j+1, i:is), ix i j #)- in case gen# steps f (0,rowOrder) of- (# _, r #) -> r- , let f ( _, [] ) = undefined- f ( j, x:xs )- | j == n = f (0, xs)- | otherwise = (# ( j + 1, x:xs), if j == x then 1 else 0 #)- in case gen# steps f (0,rowOrder) of- (# _, r #) -> r- , if countMisordered rowOrder `rem` 2 == 1- then -1 else 1- )- where- dn = dimVal' @n- steps = CumulDims [dn * dn, dn, 1]- -- permuted row ordering- rowOrder = uncurry fillPass $ searchPass 0 [0..n-1]- -- matrix size- n = fromIntegral dn- n# = case n of I# x -> x- -- sign of permutations- countMisordered :: [Int] -> Int- countMisordered [] = 0- countMisordered (i:is) = foldl' (\c j -> if i > j then succ c else c) 0 is- + countMisordered is- ix (I# i) (I# j) = ix# (i *# n# +# j) mat- findMax :: Int -> [Int] -> (t, Int)- findMax j = foldl' (\(ox, oi) i -> let x = abs (ix i j)- in if x > ox then (x, i)- else (ox, oi)- ) (0, 0)- -- search maximums, leaving Nothing where all rows are 0- searchPass :: Int -> [Int] -> ([Int], [Maybe Int])- searchPass j is- | j == n = (is, [])- | otherwise = case findMax j is of- (0, _) -> (Nothing:) <$> searchPass (j+1) is- (_, i) -> (Just i:) <$> searchPass (j+1) (delete i is)- -- replace Nothings with remaining row numbers- fillPass :: [Int] -> [Maybe Int] -> [Int]- fillPass _ [] = []- fillPass js (Just i : is) = i : fillPass js is- fillPass (j:js) (Nothing : is) = j : fillPass js is- fillPass [] (Nothing : is) = 0 : fillPass [] is
+ src/Numeric/Matrix/Internal/Double.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Numeric.Matrix.Internal.Double () where++import qualified Control.Monad.ST as ST+import qualified Numeric.DataFrame.ST as ST+import Numeric.Dimensions (KnownDim)+import Numeric.Matrix.Internal+import Numeric.Matrix.LU+import Numeric.Scalar.Internal+import Numeric.Vector.Internal++{-# INLINE mkMat #-}+mkMat ::+ Double -> Double -> Double -> Double ->+ Double -> Double -> Double -> Double ->+ Double -> Double -> Double -> Double ->+ Double -> Double -> Double -> Double ->+ Mat44d+mkMat+ _11 _12 _13 _14+ _21 _22 _23 _24+ _31 _32 _33 _34+ _41 _42 _43 _44+ = ST.runST $ do+ df <- ST.newDataFrame+ ST.writeDataFrameOff df 0 $ scalar _11+ ST.writeDataFrameOff df 1 $ scalar _12+ ST.writeDataFrameOff df 2 $ scalar _13+ ST.writeDataFrameOff df 3 $ scalar _14+ ST.writeDataFrameOff df 4 $ scalar _21+ ST.writeDataFrameOff df 5 $ scalar _22+ ST.writeDataFrameOff df 6 $ scalar _23+ ST.writeDataFrameOff df 7 $ scalar _24+ ST.writeDataFrameOff df 8 $ scalar _31+ ST.writeDataFrameOff df 9 $ scalar _32+ ST.writeDataFrameOff df 10 $ scalar _33+ ST.writeDataFrameOff df 11 $ scalar _34+ ST.writeDataFrameOff df 12 $ scalar _41+ ST.writeDataFrameOff df 13 $ scalar _42+ ST.writeDataFrameOff df 14 $ scalar _43+ ST.writeDataFrameOff df 15 $ scalar _44+ ST.unsafeFreezeDataFrame df++instance HomTransform4 Double where+ {-# INLINE translate4 #-}+ translate4 (unpackV4# -> (# x, y, z, _ #)) = mkMat+ 1 0 0 0+ 0 1 0 0+ 0 0 1 0+ x y z 1++ {-# INLINE translate3 #-}+ translate3 (unpackV3# -> (# x, y, z #)) = mkMat+ 1 0 0 0+ 0 1 0 0+ 0 0 1 0+ x y z 1++ {-# INLINE rotateX #-}+ rotateX a = mkMat+ 1 0 0 0+ 0 c s 0+ 0 n c 0+ 0 0 0 1+ where+ c = cos a+ s = sin a+ n = -s++ {-# INLINE rotateY #-}+ rotateY a = mkMat+ c 0 n 0+ 0 1 0 0+ s 0 c 0+ 0 0 0 1+ where+ c = cos a+ s = sin a+ n = -s++ {-# INLINE rotateZ #-}+ rotateZ a = mkMat+ c s 0 0+ n c 0 0+ 0 0 1 0+ 0 0 0 1+ where+ c = cos a+ s = sin a+ n = -s++ {-# INLINE rotate #-}+ rotate (unpackV3# -> (# x, y, z #)) a = mkMat+ (c+xxv) (yxv+zs) (zxv-ys) 0+ (xyv-zs) (c+yyv) (zyv+xs) 0+ (xzv+ys) (yzv-xs) (c+zzv) 0+ 0 0 0 1+ where+ c = cos a+ v = 1 - c -- v for versine+ s = sin a+ xxv = x * x * v+ xyv = x * y * v+ xzv = x * z * v+ yxv = xyv+ yyv = y * y * v+ yzv = y * z * v+ zxv = xzv+ zyv = yzv+ zzv = z * z * v+ xs = x * s+ ys = y * s+ zs = z * s++ {-# INLINE rotateEuler #-}+ rotateEuler x y z = mkMat+ (cy*cz) (cx*sz+sx*sy*cz) (sx*sz-cx*sy*cz) 0+ (-cy*sz) (cx*cz-sx*sy*sz) (sx*cz+cx*sy*sz) 0+ sy (-sx*cy) (cx*cy) 0+ 0 0 0 1+ where+ cx = cos x+ sx = sin x+ cy = cos y+ sy = sin y+ cz = cos z+ sz = sin z++ {-# INLINE lookAt #-}+ lookAt up cam foc = mkMat+ xb1 yb1 zb1 0+ xb2 yb2 zb2 0+ xb3 yb3 zb3 0+ tx ty tz 1+ where+ (# xb1, xb2, xb3 #) = unpackV3# xb+ (# yb1, yb2, yb3 #) = unpackV3# yb+ (# zb1, zb2, zb3 #) = unpackV3# zb+ zb = normalized $ cam - foc -- Basis vector for "backward", since +Z is behind the camera+ xb = normalized $ up `cross` zb -- Basis vector for "right"+ yb = zb `cross` xb -- Basis vector for "up"+ ncam = -cam+ tx = unScalar $ xb `dot` ncam+ ty = unScalar $ yb `dot` ncam+ tz = unScalar $ zb `dot` ncam++ {-# INLINE perspective #-}+ perspective n f fovy aspect = mkMat+ dpw 0 0 0+ 0 dph 0 0+ 0 0 a (-1)+ 0 0 b 0+ where+ hpd = tan (fovy * 0.5) -- height/distance+ wpd = aspect * hpd; -- width/distance+ dph = recip hpd -- distance/height+ dpw = recip wpd -- distance/width+ nmf = n - f+ a = (n + f) / nmf+ b = 2 * n * f / nmf++ {-# INLINE orthogonal #-}+ orthogonal n f w h = mkMat+ iw 0 0 0+ 0 ih 0 0+ 0 0 a 0+ 0 0 b 1+ where+ ih = 2 / h+ iw = 2 / w+ nmf = n - f+ a = 2 / nmf+ b = (n + f) / nmf++ {-# INLINE toHomPoint #-}+ toHomPoint (unpackV3# -> (# x, y, z #)) = vec4 x y z 1++ {-# INLINE toHomVector #-}+ toHomVector (unpackV3# -> (# x, y, z #)) = vec4 x y z 0++ {-# INLINE fromHom #-}+ fromHom (unpackV4# -> (# x, y, z, w #))+ | w == 0 = vec3 x y z+ | otherwise = vec3 (x/w) (y/w) (z/w)++instance KnownDim n => MatrixInverse Double n where+ inverse = inverseViaLU++instance KnownDim n => MatrixDeterminant Double n where+ det = detViaLU
+ src/Numeric/Matrix/Internal/Float.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Numeric.Matrix.Internal.Float () where++import qualified Control.Monad.ST as ST+import qualified Numeric.DataFrame.ST as ST+import Numeric.Dimensions (KnownDim)+import Numeric.Matrix.Internal+import Numeric.Matrix.LU+import Numeric.Scalar.Internal+import Numeric.Vector.Internal++{-# INLINE mkMat #-}+mkMat ::+ Float -> Float -> Float -> Float ->+ Float -> Float -> Float -> Float ->+ Float -> Float -> Float -> Float ->+ Float -> Float -> Float -> Float ->+ Mat44f+mkMat+ _11 _12 _13 _14+ _21 _22 _23 _24+ _31 _32 _33 _34+ _41 _42 _43 _44+ = ST.runST $ do+ df <- ST.newDataFrame+ ST.writeDataFrameOff df 0 $ scalar _11+ ST.writeDataFrameOff df 1 $ scalar _12+ ST.writeDataFrameOff df 2 $ scalar _13+ ST.writeDataFrameOff df 3 $ scalar _14+ ST.writeDataFrameOff df 4 $ scalar _21+ ST.writeDataFrameOff df 5 $ scalar _22+ ST.writeDataFrameOff df 6 $ scalar _23+ ST.writeDataFrameOff df 7 $ scalar _24+ ST.writeDataFrameOff df 8 $ scalar _31+ ST.writeDataFrameOff df 9 $ scalar _32+ ST.writeDataFrameOff df 10 $ scalar _33+ ST.writeDataFrameOff df 11 $ scalar _34+ ST.writeDataFrameOff df 12 $ scalar _41+ ST.writeDataFrameOff df 13 $ scalar _42+ ST.writeDataFrameOff df 14 $ scalar _43+ ST.writeDataFrameOff df 15 $ scalar _44+ ST.unsafeFreezeDataFrame df++instance HomTransform4 Float where+ {-# INLINE translate4 #-}+ translate4 (unpackV4# -> (# x, y, z, _ #)) = mkMat+ 1 0 0 0+ 0 1 0 0+ 0 0 1 0+ x y z 1++ {-# INLINE translate3 #-}+ translate3 (unpackV3# -> (# x, y, z #)) = mkMat+ 1 0 0 0+ 0 1 0 0+ 0 0 1 0+ x y z 1++ {-# INLINE rotateX #-}+ rotateX a = mkMat+ 1 0 0 0+ 0 c s 0+ 0 n c 0+ 0 0 0 1+ where+ c = cos a+ s = sin a+ n = -s++ {-# INLINE rotateY #-}+ rotateY a = mkMat+ c 0 n 0+ 0 1 0 0+ s 0 c 0+ 0 0 0 1+ where+ c = cos a+ s = sin a+ n = -s++ {-# INLINE rotateZ #-}+ rotateZ a = mkMat+ c s 0 0+ n c 0 0+ 0 0 1 0+ 0 0 0 1+ where+ c = cos a+ s = sin a+ n = -s++ {-# INLINE rotate #-}+ rotate (unpackV3# -> (# x, y, z #)) a = mkMat+ (c+xxv) (yxv+zs) (zxv-ys) 0+ (xyv-zs) (c+yyv) (zyv+xs) 0+ (xzv+ys) (yzv-xs) (c+zzv) 0+ 0 0 0 1+ where+ c = cos a+ v = 1 - c -- v for versine+ s = sin a+ xxv = x * x * v+ xyv = x * y * v+ xzv = x * z * v+ yxv = xyv+ yyv = y * y * v+ yzv = y * z * v+ zxv = xzv+ zyv = yzv+ zzv = z * z * v+ xs = x * s+ ys = y * s+ zs = z * s++ {-# INLINE rotateEuler #-}+ rotateEuler x y z = mkMat+ (cy*cz) (cx*sz+sx*sy*cz) (sx*sz-cx*sy*cz) 0+ (-cy*sz) (cx*cz-sx*sy*sz) (sx*cz+cx*sy*sz) 0+ sy (-sx*cy) (cx*cy) 0+ 0 0 0 1+ where+ cx = cos x+ sx = sin x+ cy = cos y+ sy = sin y+ cz = cos z+ sz = sin z++ {-# INLINE lookAt #-}+ lookAt up cam foc = mkMat+ xb1 yb1 zb1 0+ xb2 yb2 zb2 0+ xb3 yb3 zb3 0+ tx ty tz 1+ where+ (# xb1, xb2, xb3 #) = unpackV3# xb+ (# yb1, yb2, yb3 #) = unpackV3# yb+ (# zb1, zb2, zb3 #) = unpackV3# zb+ zb = normalized $ cam - foc -- Basis vector for "backward", since +Z is behind the camera+ xb = normalized $ up `cross` zb -- Basis vector for "right"+ yb = zb `cross` xb -- Basis vector for "up"+ ncam = -cam+ tx = unScalar $ xb `dot` ncam+ ty = unScalar $ yb `dot` ncam+ tz = unScalar $ zb `dot` ncam++ {-# INLINE perspective #-}+ perspective n f fovy aspect = mkMat+ dpw 0 0 0+ 0 dph 0 0+ 0 0 a (-1)+ 0 0 b 0+ where+ hpd = tan (fovy * 0.5) -- height/distance+ wpd = aspect * hpd; -- width/distance+ dph = recip hpd -- distance/height+ dpw = recip wpd -- distance/width+ nmf = n - f+ a = (n + f) / nmf+ b = 2 * n * f / nmf++ {-# INLINE orthogonal #-}+ orthogonal n f w h = mkMat+ iw 0 0 0+ 0 ih 0 0+ 0 0 a 0+ 0 0 b 1+ where+ ih = 2 / h+ iw = 2 / w+ nmf = n - f+ a = 2 / nmf+ b = (n + f) / nmf++ {-# INLINE toHomPoint #-}+ toHomPoint (unpackV3# -> (# x, y, z #)) = vec4 x y z 1++ {-# INLINE toHomVector #-}+ toHomVector (unpackV3# -> (# x, y, z #)) = vec4 x y z 0++ {-# INLINE fromHom #-}+ fromHom (unpackV4# -> (# x, y, z, w #))+ | w == 0 = vec3 x y z+ | otherwise = vec3 (x/w) (y/w) (z/w)++instance KnownDim n => MatrixInverse Float n where+ inverse = inverseViaLU++instance KnownDim n => MatrixDeterminant Float n where+ det = detViaLU
− src/Numeric/Matrix/Internal/Mat44d.hs
@@ -1,186 +0,0 @@-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Numeric.Matrix.Internal.Mat44d () where--import qualified Control.Monad.ST as ST-import qualified Numeric.DataFrame.ST as ST-import Numeric.Matrix.Internal-import Numeric.Scalar.Internal-import Numeric.Vector.Internal--{-# INLINE mkMat #-}-mkMat ::- Double -> Double -> Double -> Double ->- Double -> Double -> Double -> Double ->- Double -> Double -> Double -> Double ->- Double -> Double -> Double -> Double ->- Mat44d-mkMat- _11 _12 _13 _14- _21 _22 _23 _24- _31 _32 _33 _34- _41 _42 _43 _44- = ST.runST $ do- df <- ST.newDataFrame- ST.writeDataFrameOff df 0 $ scalar _11- ST.writeDataFrameOff df 1 $ scalar _12- ST.writeDataFrameOff df 2 $ scalar _13- ST.writeDataFrameOff df 3 $ scalar _14- ST.writeDataFrameOff df 4 $ scalar _21- ST.writeDataFrameOff df 5 $ scalar _22- ST.writeDataFrameOff df 6 $ scalar _23- ST.writeDataFrameOff df 7 $ scalar _24- ST.writeDataFrameOff df 8 $ scalar _31- ST.writeDataFrameOff df 9 $ scalar _32- ST.writeDataFrameOff df 10 $ scalar _33- ST.writeDataFrameOff df 11 $ scalar _34- ST.writeDataFrameOff df 12 $ scalar _41- ST.writeDataFrameOff df 13 $ scalar _42- ST.writeDataFrameOff df 14 $ scalar _43- ST.writeDataFrameOff df 15 $ scalar _44- ST.unsafeFreezeDataFrame df--instance HomTransform4 Double where- {-# INLINE translate4 #-}- translate4 (unpackV4# -> (# x, y, z, _ #)) = mkMat- 1 0 0 0- 0 1 0 0- 0 0 1 0- x y z 1-- {-# INLINE translate3 #-}- translate3 (unpackV3# -> (# x, y, z #)) = mkMat- 1 0 0 0- 0 1 0 0- 0 0 1 0- x y z 1-- {-# INLINE rotateX #-}- rotateX a = mkMat- 1 0 0 0- 0 c s 0- 0 n c 0- 0 0 0 1- where- c = cos a- s = sin a- n = -s-- {-# INLINE rotateY #-}- rotateY a = mkMat- c 0 n 0- 0 1 0 0- s 0 c 0- 0 0 0 1- where- c = cos a- s = sin a- n = -s-- {-# INLINE rotateZ #-}- rotateZ a = mkMat- c s 0 0- n c 0 0- 0 0 1 0- 0 0 0 1- where- c = cos a- s = sin a- n = -s-- {-# INLINE rotate #-}- rotate (unpackV3# -> (# x, y, z #)) a = mkMat- (c+xxv) (yxv+zs) (zxv-ys) 0- (xyv-zs) (c+yyv) (zyv+xs) 0- (xzv+ys) (yzv-xs) (c+zzv) 0- 0 0 0 1- where- c = cos a- v = 1 - c -- v for versine- s = sin a- xxv = x * x * v- xyv = x * y * v- xzv = x * z * v- yxv = xyv- yyv = y * y * v- yzv = y * z * v- zxv = xzv- zyv = yzv- zzv = z * z * v- xs = x * s- ys = y * s- zs = z * s-- {-# INLINE rotateEuler #-}- rotateEuler x y z = mkMat- (cy*cz) (cx*sz+sx*sy*cz) (sx*sz-cx*sy*cz) 0- (-cy*sz) (cx*cz-sx*sy*sz) (sx*cz+cx*sy*sz) 0- sy (-sx*cy) (cx*cy) 0- 0 0 0 1- where- cx = cos x- sx = sin x- cy = cos y- sy = sin y- cz = cos z- sz = sin z-- {-# INLINE lookAt #-}- lookAt up cam foc = mkMat- xb1 yb1 zb1 0- xb2 yb2 zb2 0- xb3 yb3 zb3 0- tx ty tz 1- where- (# xb1, xb2, xb3 #) = unpackV3# xb- (# yb1, yb2, yb3 #) = unpackV3# yb- (# zb1, zb2, zb3 #) = unpackV3# zb- zb = normalized $ cam - foc -- Basis vector for "backward", since +Z is behind the camera- xb = normalized $ up `cross` zb -- Basis vector for "right"- yb = zb `cross` xb -- Basis vector for "up"- ncam = -cam- tx = unScalar $ xb `dot` ncam- ty = unScalar $ yb `dot` ncam- tz = unScalar $ zb `dot` ncam-- {-# INLINE perspective #-}- perspective n f fovy aspect = mkMat- dpw 0 0 0- 0 dph 0 0- 0 0 a (-1)- 0 0 b 0- where- hpd = tan (fovy * 0.5) -- height/distance- wpd = aspect * hpd; -- width/distance- dph = recip hpd -- distance/height- dpw = recip wpd -- distance/width- nmf = n - f- a = (n + f) / nmf- b = 2 * n * f / nmf-- {-# INLINE orthogonal #-}- orthogonal n f w h = mkMat- iw 0 0 0- 0 ih 0 0- 0 0 a 0- 0 0 b 1- where- ih = 2 / h- iw = 2 / w- nmf = n - f- a = 2 / nmf- b = (n + f) / nmf-- {-# INLINE toHomPoint #-}- toHomPoint (unpackV3# -> (# x, y, z #)) = vec4 x y z 1-- {-# INLINE toHomVector #-}- toHomVector (unpackV3# -> (# x, y, z #)) = vec4 x y z 0-- {-# INLINE fromHom #-}- fromHom (unpackV4# -> (# x, y, z, w #))- | w == 0 = vec3 x y z- | otherwise = vec3 (x/w) (y/w) (z/w)
− src/Numeric/Matrix/Internal/Mat44f.hs
@@ -1,186 +0,0 @@-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Numeric.Matrix.Internal.Mat44f () where--import qualified Control.Monad.ST as ST-import qualified Numeric.DataFrame.ST as ST-import Numeric.Matrix.Internal-import Numeric.Scalar.Internal-import Numeric.Vector.Internal--{-# INLINE mkMat #-}-mkMat ::- Float -> Float -> Float -> Float ->- Float -> Float -> Float -> Float ->- Float -> Float -> Float -> Float ->- Float -> Float -> Float -> Float ->- Mat44f-mkMat- _11 _12 _13 _14- _21 _22 _23 _24- _31 _32 _33 _34- _41 _42 _43 _44- = ST.runST $ do- df <- ST.newDataFrame- ST.writeDataFrameOff df 0 $ scalar _11- ST.writeDataFrameOff df 1 $ scalar _12- ST.writeDataFrameOff df 2 $ scalar _13- ST.writeDataFrameOff df 3 $ scalar _14- ST.writeDataFrameOff df 4 $ scalar _21- ST.writeDataFrameOff df 5 $ scalar _22- ST.writeDataFrameOff df 6 $ scalar _23- ST.writeDataFrameOff df 7 $ scalar _24- ST.writeDataFrameOff df 8 $ scalar _31- ST.writeDataFrameOff df 9 $ scalar _32- ST.writeDataFrameOff df 10 $ scalar _33- ST.writeDataFrameOff df 11 $ scalar _34- ST.writeDataFrameOff df 12 $ scalar _41- ST.writeDataFrameOff df 13 $ scalar _42- ST.writeDataFrameOff df 14 $ scalar _43- ST.writeDataFrameOff df 15 $ scalar _44- ST.unsafeFreezeDataFrame df--instance HomTransform4 Float where- {-# INLINE translate4 #-}- translate4 (unpackV4# -> (# x, y, z, _ #)) = mkMat- 1 0 0 0- 0 1 0 0- 0 0 1 0- x y z 1-- {-# INLINE translate3 #-}- translate3 (unpackV3# -> (# x, y, z #)) = mkMat- 1 0 0 0- 0 1 0 0- 0 0 1 0- x y z 1-- {-# INLINE rotateX #-}- rotateX a = mkMat- 1 0 0 0- 0 c s 0- 0 n c 0- 0 0 0 1- where- c = cos a- s = sin a- n = -s-- {-# INLINE rotateY #-}- rotateY a = mkMat- c 0 n 0- 0 1 0 0- s 0 c 0- 0 0 0 1- where- c = cos a- s = sin a- n = -s-- {-# INLINE rotateZ #-}- rotateZ a = mkMat- c s 0 0- n c 0 0- 0 0 1 0- 0 0 0 1- where- c = cos a- s = sin a- n = -s-- {-# INLINE rotate #-}- rotate (unpackV3# -> (# x, y, z #)) a = mkMat- (c+xxv) (yxv+zs) (zxv-ys) 0- (xyv-zs) (c+yyv) (zyv+xs) 0- (xzv+ys) (yzv-xs) (c+zzv) 0- 0 0 0 1- where- c = cos a- v = 1 - c -- v for versine- s = sin a- xxv = x * x * v- xyv = x * y * v- xzv = x * z * v- yxv = xyv- yyv = y * y * v- yzv = y * z * v- zxv = xzv- zyv = yzv- zzv = z * z * v- xs = x * s- ys = y * s- zs = z * s-- {-# INLINE rotateEuler #-}- rotateEuler x y z = mkMat- (cy*cz) (cx*sz+sx*sy*cz) (sx*sz-cx*sy*cz) 0- (-cy*sz) (cx*cz-sx*sy*sz) (sx*cz+cx*sy*sz) 0- sy (-sx*cy) (cx*cy) 0- 0 0 0 1- where- cx = cos x- sx = sin x- cy = cos y- sy = sin y- cz = cos z- sz = sin z-- {-# INLINE lookAt #-}- lookAt up cam foc = mkMat- xb1 yb1 zb1 0- xb2 yb2 zb2 0- xb3 yb3 zb3 0- tx ty tz 1- where- (# xb1, xb2, xb3 #) = unpackV3# xb- (# yb1, yb2, yb3 #) = unpackV3# yb- (# zb1, zb2, zb3 #) = unpackV3# zb- zb = normalized $ cam - foc -- Basis vector for "backward", since +Z is behind the camera- xb = normalized $ up `cross` zb -- Basis vector for "right"- yb = zb `cross` xb -- Basis vector for "up"- ncam = -cam- tx = unScalar $ xb `dot` ncam- ty = unScalar $ yb `dot` ncam- tz = unScalar $ zb `dot` ncam-- {-# INLINE perspective #-}- perspective n f fovy aspect = mkMat- dpw 0 0 0- 0 dph 0 0- 0 0 a (-1)- 0 0 b 0- where- hpd = tan (fovy * 0.5) -- height/distance- wpd = aspect * hpd; -- width/distance- dph = recip hpd -- distance/height- dpw = recip wpd -- distance/width- nmf = n - f- a = (n + f) / nmf- b = 2 * n * f / nmf-- {-# INLINE orthogonal #-}- orthogonal n f w h = mkMat- iw 0 0 0- 0 ih 0 0- 0 0 a 0- 0 0 b 1- where- ih = 2 / h- iw = 2 / w- nmf = n - f- a = 2 / nmf- b = (n + f) / nmf-- {-# INLINE toHomPoint #-}- toHomPoint (unpackV3# -> (# x, y, z #)) = vec4 x y z 1-- {-# INLINE toHomVector #-}- toHomVector (unpackV3# -> (# x, y, z #)) = vec4 x y z 0-- {-# INLINE fromHom #-}- fromHom (unpackV4# -> (# x, y, z, w #))- | w == 0 = vec3 x y z- | otherwise = vec3 (x/w) (y/w) (z/w)
+ src/Numeric/Matrix/LU.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Numeric.Matrix.LU+ ( MatrixLU (..), LU (..)+ , luSolveR, luSolveL+ , detViaLU, inverseViaLU+ ) where++import Control.Monad+import Control.Monad.ST+import Data.Kind+import Numeric.DataFrame.Internal.PrimArray+import Numeric.DataFrame.ST+import Numeric.DataFrame.SubSpace+import Numeric.DataFrame.Type+import Numeric.Dimensions+import Numeric.Matrix.Internal+import Numeric.Scalar.Internal+import Numeric.Subroutine.SolveTriangular+++-- | Result of LU factorization with Partial Pivoting+-- \( PA = LU \).+data LU (t :: Type) (n :: Nat)+ = LU+ { luLower :: Matrix t n n+ -- ^ Unit lower triangular matrix \(L\).+ -- All elements on the diagonal of @L@ equal @1@.+ -- The rest of the elements satisfy \(|l_{ij}| \leq 1\).+ , luUpper :: Matrix t n n+ -- ^ Upper triangular matrix \(U\)+ , luPerm :: Matrix t n n+ -- ^ Row permutation matrix \(P\)+ , luPermDet :: Scalar t+ -- ^ Sign of permutation @luPermDet == det . luPerm@; \(|P| = \pm 1\).+ }++deriving instance (Show t, PrimBytes t, KnownDim n) => Show (LU t n)+deriving instance (Eq (Matrix t n n), Eq t) => Eq (LU t n)++class (KnownDim n, Ord t, Fractional t, PrimBytes t, KnownBackend t '[n,n])+ => MatrixLU t (n :: Nat) where+ -- | Compute LU factorization with Partial Pivoting+ lu :: Matrix t n n -> LU t n+++instance (KnownDim n, Ord t, Fractional t, PrimBytes t, KnownBackend t '[n,n])+ => MatrixLU t n where+ lu a = runST $ do+ pPtr <- unsafeThawDataFrame $ iwgen @_ @'[n] @'[] (\(Idx i :* U) -> S i)+ uPtr <- thawDataFrame a+ lPtr <- newDataFrame+ temp <- newDataFrame+ detPositive <- luInplace temp pPtr uPtr+ p <- unsafeFreezeDataFrame pPtr+ -- split U and L+ forM_ [0..n-1] $ \i -> do+ let ni = n*i+ forM_ [0..n-1] $ \j -> case compare i j of+ GT -> do+ lij <- readDataFrameOff uPtr (ni + j)+ writeDataFrameOff uPtr (ni + j) 0+ writeDataFrameOff lPtr (ni + j) lij+ EQ -> writeDataFrameOff lPtr (ni + j) 1+ LT -> writeDataFrameOff lPtr (ni + j) 0+ luLower <- unsafeFreezeDataFrame lPtr+ luUpper <- unsafeFreezeDataFrame uPtr+ let luPermDet = if detPositive then 1 else -1+ luPerm = iwgen @_ @'[n,n] @'[]+ (\(Idx i :* Idx j :* U) -> if S j == p ! i then 1 else 0)+ return LU {..}+ where+ n = fromIntegral (dimVal' @n) :: Int+++-- | Solve @Ax = b@ problem given LU decomposition of A.+luSolveR ::+ forall t (n :: Nat) (ds :: [Nat])+ . (MatrixLU t n, Dimensions ds)+ => LU t n -> DataFrame t (n :+ ds) -> DataFrame t (n :+ ds)+luSolveR LU {..} b = runST $ do+ xPtr <- thawDataFrame (luPerm %* b) -- NB: wasting resources!+ solveLowerTriangularR luLower xPtr+ solveUpperTriangularR luUpper xPtr+ unsafeFreezeDataFrame xPtr++-- | Solve @xA = b@ problem given LU decomposition of A.+luSolveL ::+ forall t (n :: Nat) (ds :: [Nat])+ . (MatrixLU t n, Dimensions ds)+ => LU t n -> DataFrame t (ds +: n) -> DataFrame t (ds +: n)+luSolveL LU {..} b+ | dn <- dim @n+ , dds <- dims @ds+ , Dims <- Snoc dds dn+ , Dict <- Dict @(SnocList ds n _)+ = runST $ do+ xPtr <- thawDataFrame b+ solveUpperTriangularL xPtr luUpper+ solveLowerTriangularL xPtr luLower+ (%* luPerm) <$> unsafeFreezeDataFrame xPtr+luSolveL _ _ = error "luSolveL: impossible pattern"++-- | Calculate inverse of a matrix via LU decomposition+inverseViaLU :: forall (t :: Type) (n :: Nat)+ . MatrixLU t n => Matrix t n n -> Matrix t n n+inverseViaLU a = runST $ do+ xPtr <- unsafeThawDataFrame luPerm -- luPerm is only ever used once+ solveLowerTriangularR luLower xPtr+ solveUpperTriangularR luUpper xPtr+ unsafeFreezeDataFrame xPtr+ where+ LU {..} = lu a+-- perfectly correct, but slightly slower versions:+-- inverseViaLU a = luSolveR (lu a) eye+-- inverseViaLU a = luSolveL (lu a) eye++-- | Calculate determinant of a matrix via LU decomposition+detViaLU :: forall (t :: Type) (n :: Nat)+ . MatrixLU t n => Matrix t n n -> Scalar t+detViaLU m = foldl (\x off -> scalar (ixOff off luUpper) * x) luPermDet [0,n+1..n*n]+ where+ n = fromIntegral (dimVal' @n) :: Int+ LU {..} = lu m++{- |+Run LU decomposition with partial pivoting inplace, such that+the upper triangular part of matrix \(A\) becomes \(U\) and+the lower triangular part (without diagonal) of matrix \(A\) becomes \(L\).++\(U\) is upper triangular.+\(L\) is unit lower triangular; all diagonal elements of \(L\) are implicit and+equal to 1; the rest of the elements a smaller than one \(|l_{ij}| \leq 1\).++Pivoting is represented as a permutation vector \(p\);+returned value is the sign of the permutation (positive if @True@, negative otherwise).++NB: Initialize \(p\) with indices @0..n-1@.++Reference: Algorithm 3.4.1 on p.128+ of "Matrix Computations" 4th edition by G. H. Golub and C. F. Van Loan.+ -}+luInplace ::+ forall (s :: Type) (t :: Type) (n :: Nat)+ . (PrimBytes t, Fractional t, Ord t, KnownDim n)+ => STDataFrame s t '[n] -- ^ Temporary buffer+ -> STDataFrame s Word '[n] -- ^ Current state of permutation \(p\)+ -> STDataFrame s t '[n,n] -- ^ Current state of \(A\)+ -> ST s Bool+luInplace temp pPtr aPtr = foldM (\b -> fmap (b /=) . go) True [0..n-2]+ where+ n = fromIntegral (dimVal' @n) :: Int++ -- Runs an iteration of the algorithm;+ --- returns whether there was a swap of rows.+ go :: Int -> ST s Bool+ go k = do+ mu <- findPivot k+ let swapped = k /= mu+ when swapped $ swapRows k mu+ akk <- readDataFrameOff aPtr (k*(n+1))+ when (akk /= 0) $ do+ let rakk = recip akk+ forM_ [k+1..n-1] $ \i -> do+ let ni = n*i+ aik <- (rakk *) <$> readDataFrameOff aPtr (ni + k)+ writeDataFrameOff aPtr (ni + k) aik+ forM_ [k+1..n-1] $ \j -> do+ akj <- readDataFrameOff aPtr (n*k + j)+ aij <- readDataFrameOff aPtr (ni + j)+ writeDataFrameOff aPtr (ni + j) (aij - aik*akj)+ return swapped++ findPivot :: Int -> ST s Int+ findPivot k = snd <$> foldM findPivotF (0, k) [k..n-1]+ where+ findPivotF :: (Scalar t, Int) -> Int -> ST s (Scalar t, Int)+ findPivotF aj@(a, _) i = do+ x <- abs <$> readDataFrameOff aPtr (n*i + k)+ return (if x > a then (x, i) else aj)++ swapRows :: Int -> Int -> ST s ()+ swapRows i j = do+ let iPtr = subDataFrameView' (fromIntegral i :* U) aPtr+ jPtr = subDataFrameView' (fromIntegral j :* U) aPtr+ copyMutableDataFrame' U iPtr temp+ copyMutableDataFrame' U jPtr iPtr+ copyMutableDataFrame' U temp jPtr+ t <- readDataFrameOff pPtr i+ readDataFrameOff pPtr j >>= writeDataFrameOff pPtr i+ writeDataFrameOff pPtr j t
+ src/Numeric/Matrix/QR.hs view
@@ -0,0 +1,231 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Numeric.Matrix.QR+ ( QR (..), LQ (..), MatrixQR (..)+ , detViaQR, inverseViaQR+ , qrSolveR, qrSolveL+ ) where+++import Control.Monad+import Control.Monad.ST+import Data.Kind+import Numeric.Basics+import Numeric.DataFrame.ST+import Numeric.DataFrame.SubSpace+import Numeric.DataFrame.Type+import Numeric.Dimensions+import Numeric.Matrix.Internal+import Numeric.Scalar.Internal+import Numeric.Subroutine.Householder+import Numeric.Subroutine.SolveTriangular++import Unsafe.Coerce (unsafeCoerce)++-- | Result of QR factorization+-- \( A = QR \).+data QR (t :: Type) (n :: Nat) (m :: Nat)+ = QR+ { qrQ :: Matrix t n n+ -- ^ Orthogonal matrix \( Q \)+ , qrQDet :: Scalar t+ -- ^ A shortcut for evaluating a determinant of \( |Q| = \pm 1 \)+ , qrR :: Matrix t n m+ -- ^ Upper-triangular matrix \( R \)+ }++-- | Result of LQ factorization+-- \( A = LQ \).+data LQ (t :: Type) (n :: Nat) (m :: Nat)+ = LQ+ { lqL :: Matrix t n m+ -- ^ Lower-triangular matrix \( L \)+ , lqQ :: Matrix t m m+ -- ^ Orthogonal matrix \( Q \)+ , lqQDet :: Scalar t+ -- ^ A shortcut for evaluating a determinant of \( |Q| = \pm 1 \)+ }++deriving instance ( Eq t, PrimBytes t+ , KnownDim n, KnownDim m) => Eq (QR t n m)+deriving instance ( Show t, PrimBytes t+ , KnownDim n, KnownDim m) => Show (QR t n m)+deriving instance ( Eq t, PrimBytes t+ , KnownDim n, KnownDim m) => Eq (LQ t n m)+deriving instance ( Show t, PrimBytes t+ , KnownDim n, KnownDim m) => Show (LQ t n m)+++class (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m)+ => MatrixQR t (n :: Nat) (m :: Nat) where+ -- | Compute QR factorization+ qr :: Matrix t n m -> QR t n m+ -- | Compute LQ factorization+ lq :: Matrix t n m -> LQ t n m++instance (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m)+ => MatrixQR t (n :: Nat) (m :: Nat) where+ qr a+ | lim == 0 = QR undefined undefined 1+ | otherwise = runST $ do+ uPtr <- newDataFrame+ pPtr <- unsafeThawDataFrame eye+ rPtr <- thawDataFrame a+ detNegative <-+ let f x i = (x /=) <$> householderReflectionInplaceL+ uPtr pPtr rPtr (Idx i :* Idx i :* U)+ in foldM f False [0..lim - 1]+ qrR <- unsafeFreezeDataFrame rPtr+ qrQ <- unsafeFreezeDataFrame pPtr+ let qrQDet = if detNegative then -1 else 1+ return QR {..}+ where+ n = dimVal' @n+ m = dimVal' @m+ lim = min n m+ lq a+ | lim == 0 = LQ undefined undefined 1+ | otherwise = runST $ do+ uPtr <- newDataFrame+ pPtr <- unsafeThawDataFrame eye+ lPtr <- thawDataFrame a+ detNegative <-+ let f x i = (x /=) <$> householderReflectionInplaceR+ uPtr pPtr lPtr (Idx i :* Idx i :* U)+ in foldM f False [0..lim - 1]+ lqL <- unsafeFreezeDataFrame lPtr+ -- WARNING! todo: get rid of transpose.+ lqQ <- transpose <$> unsafeFreezeDataFrame pPtr+ let lqQDet = if detNegative then -1 else 1+ return LQ {..}+ where+ n = dimVal' @n+ m = dimVal' @m+ lim = min n m++-- | Calculate determinant of a matrix via QR decomposition+detViaQR :: forall t n . MatrixQR t n n => Matrix t n n -> Scalar t+detViaQR m = foldl (\x off -> scalar (ixOff off qrR) * x) qrQDet [0,n+1..n*n]+ where+ n = fromIntegral (dimVal' @n) :: Int+ QR {..} = qr m++-- | Calculate inverse of a matrix via QR decomposition+inverseViaQR :: forall t n . MatrixQR t n n => Matrix t n n -> Matrix t n n+inverseViaQR = (`qrSolveR` eye)++{- |+Compute a QR or LQ decomposition of matrix \( A : n \times m \),+and solve a system of linear equations \( Ax = b \).++If \( n >= m \) QR decomposition is used;+if \( n > m \) this function solves linear least squares problem.+If \( n < m \) (underdetermined system) LQ decomposition is used+ to yield a minimum norm solution.+ -}+qrSolveR ::+ forall t (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (MatrixQR t n m, Dimensions ds)+ => Matrix t n m -> DataFrame t (n :+ ds) -> DataFrame t (m :+ ds)+qrSolveR a b = case compareDim dn dm of+ SEQ | Dict <- (unsafeCoerce (Dict @(m ~ m)) :: Dict (m ~ n))+ -> runST $ do+ let QR {..} = qr a+ xPtr <- thawDataFrame (transpose qrQ %* b) -- NB: make a stateful product for transposed mat+ solveUpperTriangularR qrR xPtr+ unsafeFreezeDataFrame xPtr+ SGT | Dict <- unsafeCoerce (Dict @(m <= m)) :: Dict (m <= n)+ , Dict <- unsafeCoerce (Dict @(n ~ n)) :: Dict ((((n - m) + 1) + m) ~ (n + 1) )+ , D <- minusDim dn dm `plusDim` D1+ -> runST $ do+ let QR {..} = qr a+ i0 :: Idxs '[n - m + 1]+ i0 = Idx 0 :* U+ xPtr <- thawDataFrame ( slice i0 $ transpose qrQ %* b+ ) -- NB: make a stateful product for transposed mat+ solveUpperTriangularR qrR xPtr+ unsafeFreezeDataFrame xPtr+ SLT | Dict <- unsafeCoerce (Dict @(m <= m)) :: Dict (n <= m)+ , Dict <- unsafeCoerce (Dict @(n ~ n)) :: Dict ((((m - n) + 1) + n) ~ (m + 1))+ , Dict <- unsafeCoerce (Dict @(n ~ n)) :: Dict (((n + 1) + (m - n)) ~ (m + 1))+ , dd@D <- minusDim dm dn+ , D <- dd `plusDim` D1+ , D <- dn `plusDim` D1+ -> runST $ do+ let LQ {..} = lq a+ i0 :: Idxs '[m - n + 1]+ i0 = Idx 0 :* U+ iz :: Idxs '[n + 1]+ iz = maxBound :* U+ xPtr <- newDataFrame+ copyDataFrame i0 b xPtr+ copyDataFrame iz (0 :: DataFrame t ((m - n) ': ds)) xPtr+ solveLowerTriangularR lqL (subDataFrameView i0 xPtr)+ (transpose lqQ %*) <$> unsafeFreezeDataFrame xPtr+ -- NB: make a stateful product for transposed mat+ _ -> error "qrSolveR: impossible pattern"+ where+ dn = dim @n+ dm = dim @m+++{- |+Compute a QR or LQ decomposition of matrix \( A : n \times m \),+and solve a system of linear equations \( xA = b \).++If \( n <= m \) LQ decomposition is used;+if \( n < m \) this function solves linear least squares problem.+If \( n > m \) (underdetermined system) QR decomposition is used+ to yield a minimum norm solution.+ -}+qrSolveL ::+ forall t (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (MatrixQR t n m, Dimensions ds)+ => Matrix t n m -> DataFrame t (ds +: m) -> DataFrame t (ds +: n)+qrSolveL a b+ | Dims <- Snoc (dims @ds) dn+ , Dims <- Snoc (dims @ds) dm+ , Dict <- Dict @(SnocList ds n _) -- remind GHC that we have (ds +: n)+ , Dict <- Dict @(SnocList ds m _) -- remind GHC that we have (ds +: m)+ = case compareDim dn dm of+ SEQ | Dict <- (unsafeCoerce (Dict @(m ~ m)) :: Dict (m ~ n))+ -> runST $ do+ let LQ {..} = lq a+ xPtr <- thawDataFrame (b %* transpose lqQ) -- NB: make a stateful product for transposed mat+ solveLowerTriangularL xPtr lqL+ unsafeFreezeDataFrame xPtr+ SGT | Dict <- unsafeCoerce (Dict @(m <= m)) :: Dict (m <= n)+ , Dict <- unsafeCoerce (Dict @(n ~ n)) :: Dict ((m + (n - m)) ~ n)+ , D <- minusDim dn dm+ -> runST $ do+ let QR {..} = qr a+ xPtr <- thawDataFrame b+ solveUpperTriangularL xPtr qrR+ ewmap @t @ds @'[n] (\x -> qrQ %* appendDF x (0 :: DataFrame t '[n - m]))+ <$> unsafeFreezeDataFrame xPtr+ SLT | Dict <- unsafeCoerce (Dict @(m <= m)) :: Dict (n <= m)+ , Dict <- unsafeCoerce (Dict @(n ~ n)) :: Dict ((((m - n) + 1) + n) ~ (m + 1))+ , D <- minusDim dm dn `plusDim` D1+ -> runST $ do+ let LQ {..} = lq a+ i0 :: Idx (m - n + 1)+ i0 = Idx 0+ xPtr <- thawDataFrame (b %* transpose lqQ) -- NB: make a stateful product for transposed mat+ solveLowerTriangularL xPtr lqL+ ewmap @t @ds @'[n] (sslice i0)+ <$> unsafeFreezeDataFrame xPtr+ _ -> error "qrSolveL/compareDim: impossible pattern"+ | otherwise = error "qrSolveL: impossible pattern"+ where+ dn = dim @n+ dm = dim @m
+ src/Numeric/Matrix/SVD.hs view
@@ -0,0 +1,799 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Numeric.Matrix.SVD+ ( MatrixSVD (..), SVD (..)+ , svd1, svd2, svd3, svd3q+ ) where++import Control.Monad+import Control.Monad.ST+import Data.Kind+import Numeric.Basics+import Numeric.DataFrame.Internal.PrimArray+import Numeric.DataFrame.ST+import Numeric.DataFrame.SubSpace+import Numeric.DataFrame.Type+import Numeric.Dimensions+import Numeric.Matrix.Bidiagonal+import Numeric.Matrix.Internal+import Numeric.Quaternion.Internal+import Numeric.Scalar.Internal+import Numeric.Subroutine.Sort+import Numeric.Tuple+import Numeric.Vector.Internal++-- | Result of SVD factorization+-- @ M = svdU %* asDiag svdS %* transpose svdV @.+--+-- Invariants:+--+-- * Singular values `svdS` are in non-increasing order and are non-negative.+-- * svdU and svdV are orthogonal matrices+-- * det svdU == 1+--+-- NB: <https://en.wikipedia.org/wiki/Singular_value_decomposition SVD on wiki>+data SVD (t :: Type) (n :: Nat) (m :: Nat)+ = SVD+ { svdU :: Matrix t n n+ -- ^ Left-singular basis matrix+ , svdS :: Vector t (Min n m)+ -- ^ Vector of singular values+ , svdV :: Matrix t m m+ -- ^ Right-singular basis matrix+ }++deriving instance ( Show t, PrimBytes t+ , KnownDim n, KnownDim m, KnownDim (Min n m))+ => Show (SVD t n m)+deriving instance ( Eq (Matrix t n n)+ , Eq (Matrix t m m)+ , Eq (Vector t (Min n m)))+ => Eq (SVD t n m)++class RealFloatExtras t+ => MatrixSVD (t :: Type) (n :: Nat) (m :: Nat) where+ -- | Compute SVD factorization of a matrix+ svd :: IterativeMethod => Matrix t n m -> SVD t n m++-- | Obvious dummy implementation of SVD for 1x1 matrices+svd1 :: (PrimBytes t, Num t, Eq t) => Matrix t 1 1 -> SVD t 1 1+svd1 m = SVD+ { svdU = 1+ , svdS = broadcast $ abs x+ , svdV = broadcast $ if x == 0 then 1 else signum x+ }+ where+ x = ixOff 0 m++-- | SVD of a 2x2 matrix can be computed analytically+--+-- Related discussion:+--+-- https://scicomp.stackexchange.com/questions/8899/robust-algorithm-for-2-times-2-svd/+--+-- https://ieeexplore.ieee.org/document/486688+svd2 :: forall t . RealFloatExtras t => Matrix t 2 2 -> SVD t 2 2+svd2 (DF2 (DF2 m00 m01) (DF2 m10 m11)) =+ SVD+ { svdU = DF2 (DF2 uc us)+ (DF2 (negate us) uc)+ , svdS = DF2 sigma1 (abs sigma2)+ , svdV = DF2 (DF2 vc (sg2 (negate vs)))+ (DF2 vs (sg2 vc ))+ }+ where+ x1 = m00 - m11 -- 2F+ x2 = m00 + m11 -- 2E+ y1 = m01 + m10 -- 2G+ y2 = m01 - m10 -- 2H+ yy = y1*y2+ h1 = hypot x1 y1 -- h1 >= abs x1+ h2 = hypot x2 y2 -- h2 >= abs x2+ sigma1 = 0.5 * (h2 + h1) -- sigma1 >= abs sigma2+ sigma2 = 0.5 * (h2 - h1) -- can be negative, which is accounted by sg2+ sg2 = negateUnless (sigma2 >= 0)+ hx1 = h1 + x1+ hx2 = h2 + x2+ hxhx = hx1*hx2+ hxy = hx1*y2+ yhx = y1*hx2+ (uc', us', vc', vs') = case (x1 > 0 || y1 /= 0, x2 > 0 || y2 /= 0) of+ (True , True ) -> (hxhx + yy, hxy - yhx, hxhx - yy, hxy + yhx)+ (True , False) -> (y1, hx1, -y1, hx1)+ (False, True ) -> (y2, -hx2, -y2, hx2)+ (False, False) -> (1, 0, -1, 0)+ ru = recip $ hypot uc' us'+ rv = recip $ hypot vc' vs'+ uc = uc' * ru+ us = us' * ru+ vc = vc' * rv+ vs = vs' * rv++-- | Get SVD decomposition of a 3x3 matrix using `svd3q` function.+--+-- This function reorders the singular components under the hood to make sure+-- @s1 >= s2 >= s3 >= 0@.+-- Thus, it has some overhead on top of `svd3q`.+svd3 :: forall t . (Quaternion t, RealFloatExtras t) => Matrix t 3 3 -> SVD t 3 3+svd3 m = SVD+ { svdU = toMatrix33 u+ , svdS = DF3 s1 s2 s3'+ , svdV = neg3If (s3 < 0) (toMatrix33 v)+ }+ where+ (u, (DF3 s1 s2 s3), v) = svd3q m+ s3' = abs s3+ neg3If :: Bool -> Matrix t 3 3 -> Matrix t 3 3+ neg3If False = id+ neg3If True = ewmap @t @'[3] neg3+ neg3 :: Vector t 3 -> Vector t 3+ neg3 (DF3 a b c) = DF3 a b (negate c)+++-- | Get SVD decomposition of a 3x3 matrix, with orthogonal matrices U and V+-- represented as quaternions.+-- Important: U and V are bound to be rotations at the expense of the last+-- singular value being possibly negative.+--+-- This is an adoptation of a specialized 3x3 SVD algorithm described in+-- "Computing the Singular Value Decomposition of 3x3 matrices+-- with minimal branching and elementary floating point operations",+-- by A. McAdams, A. Selle, R. Tamstorf, J. Teran, E. Sifakis.+--+-- http://pages.cs.wisc.edu/~sifakis/papers/SVD_TR1690.pdf+svd3q :: forall t . (Quaternion t, RealFloatExtras t)+ => Matrix t 3 3 -> (Quater t, Vector t 3, Quater t)+svd3q m = (u, s, v)+ where+ v = jacobiEigenQ (transpose m %* m)+ (s, u) = uncurry fixSigns $ qrDecomposition3 (m %* toMatrix33 v)+ -- last bit: make sure s1 >= s2 >= 0+ fixSigns :: Vector t 3 -> Quater t -> (Vector t 3, Quater t)+ fixSigns (DF3 s1 s2 s3) q@(Quater a b c d) = case (s1 >= 0, s2 >= 0) of+ (True , True ) -> (mk3 s1 s2 s3, q)+ (False, True ) -> (mk3 (negate s1) s2 (negate s3), Quater (-c) d a (-b))+ (True , False) -> (mk3 s1 (negate s2) (negate s3), Quater d c (-b) (-a))+ (False, False) -> (mk3 (negate s1) (negate s2) s3, Quater b (-a) d (-c))+ -- one more thing:+ -- the singular values are ordered, but may have small errors;+ -- as a result adjacent values may seem to be out of order by a very small number+ mk3 :: Scalar t -> Scalar t -> Scalar t -> Vector t 3+ mk3 s1 s2 s3' = case (s1 >= s2, s1 >= abs s3, s2 >= abs s3) of+ (True , True , True ) -> DF3 s1 s2 s3' -- s1 >= s2 >= s3+ (True , True , False) -> DF3 s1 s3 (cs s2) -- s1 >= s3 > s2+ (True , False, _ ) -> DF3 s3 s1 (cs s2) -- s3 > s1 >= s2+ (False, True , True ) -> DF3 s2 s1 s3' -- s2 > s1 >= s3+ (False, _ , False) -> DF3 s3 s2 (cs s1) -- s3 > s2 > s1+ (False, False, True ) -> DF3 s2 s3 (cs s1) -- s2 >= s3 > s1+ where+ s3 = abs s3'+ cs = negateUnless (s3' >= 0)+++-- | Approximate values for cos (a/2) and sin (a/2) of a Givens rotation for+-- a 2x2 symmetric matrix. (Algorithm 2)+jacobiGivensQ :: forall t . RealFloatExtras t => t -> t -> t -> (t, t)+jacobiGivensQ aii aij ajj+ | g*sh*sh < ch*ch = (w * ch, w * sh)+ | otherwise = (c', s')+ where+ ch = 2 * (aii-ajj)+ sh = aij+ w = recip $ hypot ch sh+ g = 5.82842712474619 :: t -- 3 + sqrt 8+ c' = 0.9238795325112867 :: t -- cos (pi/8)+ s' = 0.3826834323650898 :: t -- sin (pi/8)+++-- | A quaternion for a QR Givens iteration+qrGivensQ :: forall t . RealFloatExtras t => t -> t -> (t, t)+qrGivensQ a1 a2+ | a1 < 0 = (sh * w, ch * w)+ | otherwise = (ch * w, sh * w)+ where+ rho2 = a1*a1 + a2*a2+ sh = if rho2 > M_EPS then a2 else 0+ ch = abs a1 + sqrt (max rho2 M_EPS)+ w = recip $ hypot ch sh -- TODO: consider something like a hypot+++-- | One iteration of the Jacobi algorithm on a symmetric 3x3 matrix+--+-- The three words arguments are indices:+-- 0 <= i /= j /= k <= 2+jacobiEigen3Iteration :: (Quaternion t, RealFloatExtras t)+ => Int -> Int -> Int+ -> STDataFrame s t '[3,3]+ -> ST s (Quater t)+jacobiEigen3Iteration i j k sPtr = do+ sii <- readDataFrameOff sPtr ii+ sij <- readDataFrameOff sPtr ij+ sjj <- readDataFrameOff sPtr jj+ sik <- readDataFrameOff sPtr ik+ sjk <- readDataFrameOff sPtr jk+ -- Coefficients for a quaternion corresponding to a Givens rotation+ let (ch, sh) = jacobiGivensQ sii sij sjj+ a = ch*ch - sh*sh+ b = 2 * sh*ch+ aa = a * a+ ab = a * b+ bb = b * b+ -- update the matrix+ writeDataFrameOff sPtr ii $+ aa * sii + 2 * ab * sij + bb * sjj+ writeDataFrameOff sPtr ij $+ ab * (sjj - sii) + (aa - bb) * sij+ writeDataFrameOff sPtr jj $+ bb * sii - 2 * ab * sij + aa * sjj+ writeDataFrameOff sPtr ik $ a * sik + b * sjk+ writeDataFrameOff sPtr jk $ a * sjk - b * sik++ -- write the quaternion+ qPtr <- unsafeThawDataFrame 0+ writeDataFrameOff qPtr k (negate sh)+ writeDataFrameOff qPtr 3 ch+ fromVec4 <$> unsafeFreezeDataFrame qPtr+ where+ ii = i*3 + i+ ij = if i < j then i*3 + j else j*3 + i+ jj = j*3 + j+ ik = if i < k then i*3 + k else k*3 + i+ jk = if j < k then j*3 + k else k*3 + j+++-- | Total number of the Givens rotations during the Jacobi eigendecomposition+-- part of the 3x3 SVD equals eigenItersX3*3.+-- Value `eigenItersX3 = 6` corresponds to 18 iterations and gives a good precision.+eigenItersX3 :: Int+eigenItersX3 = 12++-- | Run a few iterations of the Jacobi algorithm on a real-valued 3x3 symmetric matrix.+-- The eigenvectors basis of such matrix is orthogonal, and can be represented as+-- a quaternion.+jacobiEigenQ :: forall t+ . (Quaternion t, RealFloatExtras t)+ => Matrix t 3 3 -> Quater t+jacobiEigenQ m = runST $ do+ mPtr <- thawDataFrame m+ q <- go eigenItersX3 mPtr 1+ s1 <- readDataFrameOff mPtr 0+ s2 <- readDataFrameOff mPtr 4+ s3 <- readDataFrameOff mPtr 8+ return $ sortQ s1 s2 s3 * q+ where+ go :: Int -> STDataFrame s t '[3,3] -> Quater t -> ST s (Quater t)+ go 0 _ q = pure q++ -- -- primitive cyclic iteration;+ -- -- fast, but the convergence is not perfect+ -- --+ -- -- set eigenItersX3 = 6 for good precision+ -- go n p q = do+ -- q1 <- jacobiEigen3Iteration 0 1 2 p+ -- q2 <- jacobiEigen3Iteration 1 2 0 p+ -- q3 <- jacobiEigen3Iteration 2 0 1 p+ -- go (n - 1) p (q3 * q2 * q1 * q)++ -- Pick the largest element on lower triangle;+ -- slow because of branching, but has a better convergence+ --+ -- set eigenItersX3 = 12 for good precision+ -- (slightly faster than the cyclic version with -O0)+ go n p q = do+ a10 <- abs <$> readDataFrameOff p 1+ a20 <- abs <$> readDataFrameOff p 2+ a21 <- abs <$> readDataFrameOff p 5+ q' <- jiter n p a10 a20 a21+ go (n - 1) p (q' * q)+ jiter :: Int -> STDataFrame s t '[3,3]+ -> Scalar t -> Scalar t -> Scalar t -> ST s (Quater t)+ jiter n p a10 a20 a21+ | gt2 a10 a20 a21+ = jacobiEigen3Iteration 0 1 2 p+ | gt2 a20 a10 a21+ = jacobiEigen3Iteration 2 0 1 p+ | gt2 a21 a10 a20+ = jacobiEigen3Iteration 1 2 0 p+ | otherwise+ = case mod n 3 of+ 0 -> jacobiEigen3Iteration 0 1 2 p+ 1 -> jacobiEigen3Iteration 2 0 1 p+ _ -> jacobiEigen3Iteration 1 2 0 p+ gt2 :: Scalar t -> Scalar t -> Scalar t -> Bool+ gt2 a b c = case compare a b of+ GT -> a >= c+ EQ -> a > c+ LT -> False++ -- Make such a quaternion that rotates the matrix so that:+ -- abs s1 >= abs s2 >= abs s3+ -- Note, the corresponding singular values may be negative, which must be+ -- taken into account later.+ sortQ :: Scalar t -> Scalar t -> Scalar t -> Quater t+ sortQ s1 s2 s3 = sortQ' (s1 >= s2) (s1 >= s3) (s2 >= s3)+ sortQ' :: Bool -> Bool -> Bool -> Quater t+ sortQ' True True True = Quater 0 0 0 1 -- s1 >= s2 >= s3+ sortQ' True True False = Quater M_SQRT1_2 0 0 (-M_SQRT1_2) -- s1 >= s3 > s2+ sortQ' True False _ = Quater 0.5 0.5 0.5 0.5 -- s3 > s1 >= s2+ sortQ' False True True = Quater 0 0 M_SQRT1_2 (-M_SQRT1_2) -- s2 > s1 >= s3+ sortQ' False _ False = Quater 0 M_SQRT1_2 0 (-M_SQRT1_2) -- s3 > s2 > s1+ sortQ' False False True = Quater 0.5 0.5 0.5 (-0.5) -- s2 >= s3 > s1+++-- | One Givens rotation for a QR algorithm on a 3x3 matrix+--+-- The three words arguments are indices:+-- 0 <= i /= j /= k <= 2+--+-- if i < j then the eigen values are already sorted!+qrDecomp3Iteration :: (Quaternion t, RealFloatExtras t)+ => Int -> Int -> Int+ -> STDataFrame s t '[3,3]+ -> ST s (Quater t)+qrDecomp3Iteration i j k sPtr = do+ sii <- readDataFrameOff sPtr ii+ sij <- readDataFrameOff sPtr ij+ sji <- readDataFrameOff sPtr ji+ sjj <- readDataFrameOff sPtr jj+ sik <- readDataFrameOff sPtr ik+ sjk <- readDataFrameOff sPtr jk+ -- Coefficients for a quaternion corresponding to a Givens rotation+ let (ch, sh) = qrGivensQ sii sji+ a = ch*ch - sh*sh+ b = 2 * sh*ch+ -- update the matrix+ writeDataFrameOff sPtr ii $ a * sii + b * sji+ writeDataFrameOff sPtr ij $ a * sij + b * sjj+ writeDataFrameOff sPtr ik $ a * sik + b * sjk+ writeDataFrameOff sPtr ji 0 -- a * sji - b * sii+ writeDataFrameOff sPtr jj $ a * sjj - b * sij+ writeDataFrameOff sPtr jk $ a * sjk - b * sik++ -- write the quaternion+ qPtr <- unsafeThawDataFrame 0+ writeDataFrameOff qPtr k (negateUnless leftTriple sh)+ writeDataFrameOff qPtr 3 ch+ fromVec4 <$> unsafeFreezeDataFrame qPtr+ where+ leftTriple = (j - i) /= 1 && (k - j) /= 1+ i3 = i*3+ j3 = j*3+ ii = i3 + i+ ij = i3 + j+ ik = i3 + k+ ji = j3 + i+ jj = j3 + j+ jk = j3 + k++-- | Run QR decomposition in context of 3x3 svd: AV = US = QR+-- The input here is matrix AV+-- The R upper-triangular matrix here is in fact a diagonal matrix Sigma;+-- The Q orthogonal matrix is matrix U in the svd decomposition,+-- represented here as a quaternion.+qrDecomposition3 :: (Quaternion t, RealFloatExtras t)+ => Matrix t 3 3 -> (Vector t 3, Quater t)+qrDecomposition3 m = runST $ do+ mPtr <- thawDataFrame m+ q1 <- qrDecomp3Iteration 0 1 2 mPtr+ q2 <- qrDecomp3Iteration 0 2 1 mPtr+ q3 <- qrDecomp3Iteration 1 2 0 mPtr+ sig0 <- readDataFrameOff mPtr 0+ sig1 <- readDataFrameOff mPtr 4+ sig2 <- readDataFrameOff mPtr 8+ return (DF3 sig0 sig1 sig2, q3 * q2 * q1)+++instance RealFloatExtras t => MatrixSVD t 1 1 where+ svd = svd1++instance RealFloatExtras t => MatrixSVD t 2 2 where+ svd = svd2++instance (RealFloatExtras t, Quaternion t) => MatrixSVD t 3 3 where+ svd = svd3++instance {-# INCOHERENT #-}+ ( RealFloatExtras t, KnownDim n, KnownDim m)+ => MatrixSVD t n m where+ svd a = runST $ do+ D <- pure dnm+ Dict <- pure $ minIsSmaller dn dm -- GHC is not convinced :(+ alphas <- unsafeThawDataFrame bdAlpha+ betas <- unsafeThawDataFrame bdBeta+ uPtr <- unsafeThawDataFrame bdU+ vPtr <- unsafeThawDataFrame bdV++ -- remove last beta if m > n+ bLast <- readDataFrameOff betas nm1+ when (abs bLast > M_EPS) $+ svdGolubKahanZeroCol alphas betas vPtr nm1++ -- main routine for a bidiagonal matrix+ let maxIter = 3*nm -- number of tries+ withinIters <- svdBidiagonalInplace alphas betas uPtr vPtr nm maxIter+ unless withinIters . tooManyIterations+ $ "SVD - Givens rotation sweeps for a bidiagonal matrix ("+ ++ show maxIter ++ " sweeps max)."++ -- sort singular values+ sUnsorted <- unsafeFreezeDataFrame alphas+ let sSorted :: Vector (Tuple '[t, Word]) (Min n m)+ sSorted = sortBy (\(S (x :! _)) (S (y :! _)) -> compare y x)+ $ iwmap @_ @_ @'[] (\(Idx i :* U) (S x) -> S (abs x :! i :! U) ) sUnsorted+ svdS = ewmap @t @_ @'[] (\(S (x :! _)) -> S x) sSorted+ perm = ewmap @Word @_ @'[] (\(S (_ :! i :! U)) -> S i) sSorted+ pCount =+ if nm < 2+ then 0 :: Word+ else foldl (\s (i, j) -> if perm!i > perm!j then succ s else s)+ 0 [(i, j) | i <- [0..nm2w], j <- [i+1..nm2w+1]]+ pPositive = even pCount++ -- alphas and svdS are now out of sync, but that is not a problem++ -- make sure det U == 1+ when ((bdUDet < 0) == pPositive) $ do+ readDataFrameOff alphas 0 >>= writeDataFrameOff alphas 0 . negate+ forM_ [0..n - 1] $ \i ->+ readDataFrameOff uPtr (i*n) >>= writeDataFrameOff uPtr (i*n) . negate++ -- negate negative singular values+ forM_ [0..nm1] $ \i -> do+ s <- readDataFrameOff alphas i+ when (s < 0) $ do+ writeDataFrameOff alphas i $ negate s+ forM_ [0..m - 1] $ \j ->+ readDataFrameOff vPtr (j*m + i)+ >>= writeDataFrameOff vPtr (j*m + i) . negate++ -- apply permutations if necessary+ if pCount == 0+ then do+ svdU <- unsafeFreezeDataFrame uPtr+ svdV <- unsafeFreezeDataFrame vPtr+ return SVD {..}+ else do+ svdU' <- unsafeFreezeDataFrame uPtr+ svdV' <- unsafeFreezeDataFrame vPtr+ let svdU = iwgen @_ @_ @'[] $ \(i :* Idx j :* U) ->+ if j >= dimVal dnm+ then index (i :* Idx j :* U) svdU'+ else index (i :* Idx (unScalar $ perm!j) :* U) svdU'+ svdV = iwgen @_ @_ @'[] $ \(i :* Idx j :* U) ->+ if j >= dimVal dnm+ then index (i :* Idx j :* U) svdV'+ else index (i :* Idx (unScalar $ perm!j) :* U) svdV'+ return SVD {..}+ where+ n = fromIntegral $ dimVal dn :: Int+ m = fromIntegral $ dimVal dm :: Int+ dn = dim @n+ dm = dim @m+ dnm = minDim dn dm+ nm1 = nm - 1+ nm = fromIntegral (dimVal dnm) :: Int+ nm2w = fromIntegral (max (nm - 2) 0) :: Word+ -- compute the bidiagonal form b first, solve svd for b.+ BiDiag {..} = bidiagonalHouseholder a++++{- Compute svd for a square bidiagonal matrix inplace+ \( B = U S V^\intercal \)++@+ B = | a1 b1 0 ... 0 |+ | 0 a2 b2 0 ... 0 |+ | 0 0 a3 b3 ... 0 |+ | ................. |+ | 0 0 ... an1 bn1 |+ | 0 0 ... 0 an | bn? (in case if n > m)+@+ -}+svdBidiagonalInplace ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (nm :: Nat)+ . ( IterativeMethod, RealFloatExtras t+ , KnownDim n, KnownDim m, KnownDim nm, nm ~ Min n m)+ => STDataFrame s t '[nm] -- ^ the main diagonal of B and then the singular values.+ -> STDataFrame s t '[nm] -- ^ first upper diagonal of B+ -> STDataFrame s t '[n,n] -- ^ U+ -> STDataFrame s t '[m,m] -- ^ V+ -> Int -- ^ 0 < q <= nm -- size of a reduced matrix, such that leftover is diagonal+ -> Int -- iters+ -> ST s Bool -- whether the algorithm succeeds within the maxIter+svdBidiagonalInplace _ _ _ _ 0 _ = pure True+svdBidiagonalInplace _ _ _ _ 1 _ = pure True+svdBidiagonalInplace _ _ _ _ _ 0 = pure False+svdBidiagonalInplace aPtr bPtr uPtr vPtr q' iter = do+ Dict <- pure $ minIsSmaller (dim @n) (dim @m)+ (p, q) <- findCounters q'+ if (q /= 0)+ then do+ findZeroDiagonal p q >>= \case+ Just k+ | k == q-1 -> svdGolubKahanZeroCol aPtr bPtr vPtr (k-1)+ | otherwise -> svdGolubKahanZeroRow aPtr bPtr uPtr k+ Nothing -> svdGolubKahanStep aPtr bPtr uPtr vPtr p q+ svdBidiagonalInplace aPtr bPtr uPtr vPtr q (iter - 1)+ else return True+ where+ -- nm = fromIntegral $ dimVal' @nm :: Int++ -- Check if off-diagonal elements are close to zero and nullify them+ -- if they are small along the way.+ -- And find such indices p and q that satisfy condition in alg. 8.6.2+ -- on p. 492. of "Matrix Computations " (4-th edition).+ -- Except these are inverted:+ -- p -- is the starting index of B22 (last submatrix with non-zero superdiagonal)+ -- q -- is the starting index of B33 (diagonal submatrix)+ --+ -- that is, p and q determine the index and the size of next work piece.+ findCounters :: Int -> ST s (Int, Int)+ findCounters = goQ+ where+ checkEps :: Int -> ST s Bool+ checkEps k = do+ b <- abs <$> readDataFrameOff bPtr (k-1)+ if b == 0+ then return True+ else do+ a1 <- abs <$> readDataFrameOff aPtr (k-1)+ a2 <- abs <$> readDataFrameOff aPtr k+ if b <= M_EPS * (max (a1 + a2) 1)+ then True <$ writeDataFrameOff bPtr (k-1) 0+ else return False+ goQ :: Int -> ST s (Int, Int)+ goQ 0 = pure (0, 0) -- guard against calling with q == 0+ goQ 1 = pure (0, 0) -- 1x1 matrix is always diagonal+ goQ k = checkEps (k-1) >>= \case+ True -> goQ (k-1)+ False -> flip (,) k <$> goP (k-2)+ goP :: Int -> ST s Int+ goP 0 = pure 0+ goP k = checkEps k >>= \case+ True -> return k+ False -> goP (k-1)++ -- For indices p and q (p < q), find the biggest index (< q) such that+ -- a[p] == 0+ findZeroDiagonal :: Int -> Int -> ST s (Maybe Int)+ findZeroDiagonal p q+ | k < p = pure Nothing+ | otherwise = do+ ak <- readDataFrameOff aPtr k+ if ak == 0+ then pure $ Just k+ else if abs ak <= M_EPS+ then Just k <$ writeDataFrameOff aPtr k 0+ else findZeroDiagonal p k+ where+ k = q - 1+++-- | Apply a series of column transformations to make b[k] (and whole column k+1) zero+-- (page 491, 1st paragraph) when a[k+1] == 0.+-- To make this element zero, I apply a series of Givens transforms on columns+-- (multiply on the right).+--+-- Prerequisites:+-- * a[k+1] == 0+-- * 0 <= k < min n (m-1)+-- Invariants:+-- * matrix \(B :: n \times m \) is bidiagonal, represented by two diagonals;+-- * matrix V is orthogonal+-- * \( B = A V^\intercal \), where \(A :: n \times m\) is an implicit original matrix+-- Results:+-- * Same bidiagonal matrix with b[k] == 0; i.e. (k+1)-th column is zero.+-- * matrix V is updated (multiplied on the right)+--+-- NB: All changes are made inplace.+--+svdGolubKahanZeroCol ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat)+ . (RealFloatExtras t, KnownDim n, KnownDim m, n <= m)+ => STDataFrame s t '[n] -- ^ the main diagonal of \(B\)+ -> STDataFrame s t '[n] -- ^ first upper diagonal of \(B\)+ -> STDataFrame s t '[m,m] -- ^ \(V\)+ -> Int -- ^ 0 <= k < min (n+1) m+ -> ST s ()+svdGolubKahanZeroCol aPtr bPtr vPtr k+ | k < 0 || k >= lim = error $ unwords+ [ "svdGolubKahanZeroCol: k =", show k+ , "is outside of a valid range 0 <= k <", show lim]+ -- this trick is to convince GHC that constraint (n <= m) is not redundant+ | Dict <- Dict @(n <= m) = do+ b <- readDataFrameOff bPtr k+ writeDataFrameOff bPtr k 0+ foldM_ goGivens b [k, k-1 .. 0]+ where+ n = fromIntegral $ dimVal' @n :: Int+ m = fromIntegral $ dimVal' @m :: Int+ lim = min n (m-1)+ goGivens :: Scalar t -> Int -> ST s (Scalar t)+ goGivens 0 _ = return 0 -- non-diagonal element is nullified prematurely+ goGivens b i = do+ ai <- readDataFrameOff aPtr i+ let rab = recip $ hypot b ai+ c = ai*rab+ s = b *rab+ updateGivensMat vPtr i (k+1) c s+ writeDataFrameOff aPtr i $ ai*c + b*s -- B[i,i]+ if i == 0+ then return 0+ else do+ bi1 <- readDataFrameOff bPtr (i - 1) -- B[i,i-1]+ writeDataFrameOff bPtr (i - 1) $ bi1 * c+ return $ negate (bi1 * s)++-- | Apply a series of row transformations to make b[k] (and whole column k) zero+-- (page 490, last paragraph) when a[k] == 0.+-- To make this element zero, I apply a series of Givens transforms on rows+-- (multiply on the left).+--+-- Prerequisites:+-- * a[k] == 0+-- * 0 <= k < n - 1+-- Invariants:+-- * matrix \(B :: m \times n \) is bidiagonal, represented by two diagonals;+-- * matrix U is orthogonal+-- * \( B = U A \), where \(A :: m \times n \) is an implicit original matrix+-- Results:+-- * Same bidiagonal matrix with b[k] == 0; i.e. k-th column is zero.+-- * matrix U is updated (multiplied on the right)+--+-- NB: All changes are made inplace.+--+svdGolubKahanZeroRow ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat)+ . (RealFloatExtras t, KnownDim n, KnownDim m, n <= m)+ => STDataFrame s t '[n] -- ^ the main diagonal of B+ -> STDataFrame s t '[n] -- ^ first upper diagonal of B+ -> STDataFrame s t '[m,m] -- ^ U+ -> Int -- ^ 0 <= k < n - 1+ -> ST s ()+svdGolubKahanZeroRow aPtr bPtr uPtr k+ | k < 0 || k >= n1 = error $ unwords+ [ "svdGolubKahanZeroRow: k =", show k+ , "is outside of a valid range 0 <= k <", show n1]+ -- this trick is to convince GHC that constraint (n <= m) is not redundant+ | Dict <- Dict @(n <= m) = do+ b <- readDataFrameOff bPtr k+ writeDataFrameOff bPtr k 0+ foldM_ goGivens b [k+1..n1]+ where+ n = fromIntegral $ dimVal' @n :: Int+ n1 = n - 1+ goGivens :: Scalar t -> Int -> ST s (Scalar t)+ goGivens 0 _ = return 0 -- non-diagonal element is nullified prematurely+ goGivens b j = do+ aj <- readDataFrameOff aPtr j+ bj <- readDataFrameOff bPtr j+ let rab = recip $ hypot b aj+ c = aj*rab+ s = b*rab+ updateGivensMat uPtr k j c (negate s)+ writeDataFrameOff aPtr j $ b*s + aj*c+ writeDataFrameOff bPtr j $ bj*c+ return $ negate (bj * s)++-- | A Golub-Kahan bidiagonal SVD step on an unreduced matrix+svdGolubKahanStep ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (nm :: Nat)+ . ( RealFloatExtras t+ , KnownDim n, KnownDim m, KnownDim nm, nm ~ Min n m)+ => STDataFrame s t '[nm] -- ^ the main diagonal of B and then the singular values.+ -> STDataFrame s t '[nm] -- ^ first upper diagonal of B+ -> STDataFrame s t '[n,n] -- ^ U+ -> STDataFrame s t '[m,m] -- ^ V+ -> Int -- ^ p : 0 <= p < q <= nm; p <= q - 2+ -> Int -- ^ q : 0 <= p < q <= nm; p <= q - 2+ -> ST s ()+svdGolubKahanStep aPtr bPtr uPtr vPtr p q+ | p > q - 2 || p < 0 || q > nm+ = error $ unwords+ [ "svdGolubKahanStep: p =", show p, "and q =", show q+ , "do not satisfy p <= q - 2 or 0 <= p < q <=", show nm]+ | Dict <- Dict @(nm ~ Min n m) = do+ (y,z) <- getWilkinsonShiftYZ+ goGivens2 y z p+ where+ nm = fromIntegral $ dimVal' @nm :: Int++ -- get initial values for one recursion sweep.+ -- Note, input must satisfy: q >= p+2+ getWilkinsonShiftYZ :: ST s (Scalar t, Scalar t)+ getWilkinsonShiftYZ = do+ a1 <- readDataFrameOff aPtr p+ b1 <- readDataFrameOff bPtr p+ am <- readDataFrameOff aPtr (q-2)+ an <- readDataFrameOff aPtr (q-1)+ bm <- if q >= p + 3+ then readDataFrameOff bPtr (q-3)+ else pure 0+ bn <- readDataFrameOff bPtr (q-2)+ let t11 = a1*a1+ t12 = a1*b1+ tmm = am*am + bm*bm+ tnn = an*an + bn*bn+ tnm = am*bn+ d = 0.5*(tmm - tnn)+ mu = tnn + d - negateUnless (d >= 0) (hypot d tnm)+ return (t11 - mu, t12)++ -- yv = b[k-1]; zv = B[k-1,k+1] -- to be eliminated by 1st Givens r+ -- yu = a[k]; zu = B[k+1,k-1] -- to be eliminated by 2nd Givens r+ goGivens2 :: Scalar t -> Scalar t -> Int -> ST s ()+ goGivens2 yv zv k = do+ a1 <- readDataFrameOff aPtr k -- B[k,k]+ a2 <- readDataFrameOff aPtr (k+1) -- B[k+1,k+1]+ b1 <- readDataFrameOff bPtr k -- B[k,k+1]+ let a1' = a1*cv + b1*sv -- B[k,k] == yu+ a2' = a2*cv -- B[k+1,k+1]+ b0' = yv*cv + zv*sv -- B[k-1,k]+ b1' = b1*cv - a1*sv -- B[k,k+1]+ yu = a1' -- B[k,k]+ zu = a2*sv -- B[k+1,k]+ ryzu = recip $ hypot yu zu+ cu = yu * ryzu+ su = zu * ryzu+ a1'' = yu *cu + zu *su+ a2'' = a2'*cu - b1'*su+ b1'' = b1'*cu + a2'*su+ updateGivensMat vPtr k (k+1) cv sv+ updateGivensMat uPtr k (k+1) cu su++ when (k > p) $ writeDataFrameOff bPtr (k-1) b0'+ writeDataFrameOff bPtr k b1''+ writeDataFrameOff aPtr k a1''+ writeDataFrameOff aPtr (k+1) a2''+ when (k < q - 2) $ do+ b2 <- readDataFrameOff bPtr (k+1) -- B[k+1,k+2]+ let b2'' = b2*cu+ zvn = b2*su+ writeDataFrameOff bPtr (k+1) b2''+ goGivens2 b1'' zvn (k+1)+ where+ ryzv = recip $ hypot yv zv+ cv = yv * ryzv+ sv = zv * ryzv++-- | Update a transformation matrix with a Givens transform (on the right)+updateGivensMat ::+ forall (s :: Type) (t :: Type) (n :: Nat)+ . (PrimBytes t, Num t, KnownDim n)+ => STDataFrame s t '[n,n]+ -> Int -> Int+ -> Scalar t -> Scalar t -> ST s ()+updateGivensMat p i j c s = forM_ [0..n-1] $ \k -> do+ let nk = n*k+ ioff = nk + i+ joff = nk + j+ uki <- readDataFrameOff p ioff+ ukj <- readDataFrameOff p joff+ writeDataFrameOff p ioff $ uki*c + ukj*s+ writeDataFrameOff p joff $ ukj*c - uki*s+ where+ n = fromIntegral $ dimVal' @n :: Int+++minIsSmaller :: forall (n :: Nat) (m :: Nat)+ . Dim n -> Dim m -> Dict (Min n m <= n, Min n m <= m)+minIsSmaller dn dm+ | Just Dict <- lessOrEqDim dnm dn+ , Just Dict <- lessOrEqDim dnm dm+ = Dict+ | otherwise+ = error "minIsSmaller: impossible type-level comparison"+ where+ dnm = minDim dn dm
src/Numeric/PrimBytes.hs view
@@ -90,7 +90,6 @@ import GHC.Generics import GHC.Int import GHC.IO (IO (..))-import GHC.Ptr (Ptr (..)) import GHC.Word import Numeric.Dimensions import qualified Numeric.Tuple.Lazy as TL@@ -1328,7 +1327,7 @@ anyList :: forall (k :: Type) (xs :: [k]) . RepresentableList xs => [Any]-anyList = unsafeCoerce# (tList @_ @xs)+anyList = unsafeCoerce# (tList @xs) {-# INLINE anyList #-} instance RepresentableList xs => PrimBytes (Idxs (xs :: [k])) where@@ -1368,7 +1367,7 @@ go i (W# x :xs) s = go (plusAddr# i SIZEOF_HSWORD#) xs (writeWordOffAddr# i 0# x s) {-# INLINE writeAddr #-}- byteSize _ = case dimVal (order' @_ @xs) of+ byteSize _ = case dimVal (order' @xs) of W# n -> byteSize (undefined :: Idx x) *# word2Int# n {-# INLINE byteSize #-} byteAlign _ = byteAlign (undefined :: Idx x)@@ -1376,14 +1375,14 @@ byteFieldOffset _ _ = negateInt# 1# {-# INLINE byteFieldOffset #-} indexArray ba off- | n@(W# n#) <- dimVal (order' @_ @xs)+ | n@(W# n#) <- dimVal (order' @xs) = unsafeCoerce# (go (off *# word2Int# n#) n) where go _ 0 = [] go i n = W# (indexWordArray# ba i) : go (i +# 1#) (n-1) {-# INLINE indexArray #-} readArray mba off s- | n@(W# n#) <- dimVal (order' @_ @xs)+ | n@(W# n#) <- dimVal (order' @xs) = unsafeCoerce# (go (off *# word2Int# n#) n s) where go _ 0 s0 = (# s0, [] #)@@ -1393,7 +1392,7 @@ = (# s2, W# w : xs #) {-# INLINE readArray #-} writeArray mba off is- | W# n# <- dimVal (order' @_ @xs)+ | W# n# <- dimVal (order' @xs) = go (off *# word2Int# n#) (listIdxs is) where go _ [] s = s@@ -1437,7 +1436,7 @@ , L.All PrimBytes xs ) => PrimBytes (TS.Tuple xs) where type PrimFields (TS.Tuple xs) = TupleFields 1 xs- fromBytes off ba = go 0# (tList @_ @xs)+ fromBytes off ba = go 0# (tList @xs) where go :: L.All PrimBytes ds => Int# -> TypeList ds -> TS.Tuple ds@@ -1447,7 +1446,7 @@ , n' <- roundUpInt n (byteAlign x) = TS.Id (fromBytes (off +# n') ba) :* go (n' +# byteSize x) ts {-# INLINE fromBytes #-}- readBytes mb off = go mb 0# (tList @_ @xs)+ readBytes mb off = go mb 0# (tList @xs) where go :: L.All PrimBytes ds => MutableByteArray# s@@ -1469,7 +1468,7 @@ | n' <- roundUpInt n (byteAlign x) = go mb (n' +# byteSize x) xs ts (writeBytes mb (off +# n') x s) {-# INLINE writeBytes #-}- readAddr addr = go 0# (tList @_ @xs)+ readAddr addr = go 0# (tList @xs) where go :: L.All PrimBytes ds => Int# -> TypeList ds -> State# s -> (# State# s, TS.Tuple ds #)@@ -1490,7 +1489,7 @@ | n' <- roundUpInt n (byteAlign x) = go (n' +# byteSize x) xs ts (writeAddr x (plusAddr# addr n') s) {-# INLINE writeAddr #-}- byteSize _ = go 0# 1# (tList @_ @xs)+ byteSize _ = go 0# 1# (tList @xs) where go :: L.All PrimBytes ys => Int# -> Int# -> TypeList ys -> Int# go s a Empty = s `roundUpInt` a@@ -1499,7 +1498,7 @@ in go ( roundUpInt s xa +# byteSize x) ( maxInt a xa ) ps {-# INLINE byteSize #-}- byteAlign _ = go (tList @_ @xs)+ byteAlign _ = go (tList @xs) where go :: L.All PrimBytes ys => TypeList ys -> Int# go Empty = 0#@@ -1507,7 +1506,7 @@ {-# INLINE byteAlign #-} byteFieldOffset name _ | Just n <- readMaybe $ symbolVal' name- = go (n-1) 0# (tList @_ @xs)+ = go (n-1) 0# (tList @xs) | otherwise = negateInt# 1# where go :: L.All PrimBytes ys => Word -> Int# -> TypeList ys -> Int#
src/Numeric/ProductOrd/NonTransitive.hs view
@@ -35,8 +35,6 @@ import Data.Data import Data.Foldable import Data.Kind (Type)-import Data.Monoid as Mon (Monoid (..))-import Data.Semigroup as Sem (Semigroup (..)) import Foreign.Storable (Storable) import GHC.Generics
src/Numeric/ProductOrd/Partial.hs view
@@ -35,8 +35,6 @@ import Data.Data import Data.Foldable import Data.Kind (Type)-import Data.Monoid as Mon (Monoid (..))-import Data.Semigroup as Sem (Semigroup (..)) import Foreign.Storable (Storable) import GHC.Generics
src/Numeric/Quaternion/Internal.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -9,7 +10,9 @@ ( Quaternion (..), Quater(Quater) ) where +import Numeric.DataFrame.Type (KnownBackend) import Numeric.Matrix.Internal (Matrix)+import Numeric.PrimBytes (PrimBytes) import Numeric.Vector.Internal (Vector) import Text.Read @@ -21,7 +24,10 @@ {-# COMPLETE Quater #-} -- | Quaternion operations-class Quaternion t where+class ( Floating (Quater t), Floating t, Ord t, PrimBytes t+ , KnownBackend t '[3], KnownBackend t '[4]+ , KnownBackend t '[3,3], KnownBackend t '[4, 4]+ ) => Quaternion t where -- | Quaternion data type. The ordering of coordinates is @(x,y,z,w)@, -- where @w@ is the argument, and @x y z@ are the components of a 3D vector data Quater t
src/Numeric/Quaternion/Internal/QDouble.hs view
@@ -10,19 +10,18 @@ {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Numeric.Quaternion.Internal.QDouble ( QDouble, Quater (..) ) where import qualified Control.Monad.ST as ST import Data.Coerce (coerce)+import Numeric.Basics import Numeric.DataFrame.Internal.PrimArray import qualified Numeric.DataFrame.ST as ST import Numeric.DataFrame.Type-import Numeric.PrimBytes (PrimBytes) import Numeric.Quaternion.Internal-import Numeric.Scalar.Internal import Numeric.Vector.Internal type QDouble = Quater Double@@ -31,11 +30,11 @@ deriving instance PrimArray Double (Quater Double) instance Quaternion Double where- newtype Quater Double = QDouble Vec4d+ newtype Quater Double = QDouble (Vector Double 4) {-# INLINE packQ #-}- packQ = coerce (vec4 :: Double -> Double -> Double -> Double -> Vec4d)+ packQ = coerce (vec4 :: Double -> Double -> Double -> Double -> Vector Double 4) {-# INLINE unpackQ# #-}- unpackQ# = coerce (unpackV4# :: Vec4d -> (# Double, Double, Double, Double #))+ unpackQ# = coerce (unpackV4# :: Vector Double 4 -> (# Double, Double, Double, Double #)) {-# INLINE fromVecNum #-} fromVecNum (unpackV3# -> (# x, y, z #)) = packQ x y z {-# INLINE fromVec4 #-}@@ -47,7 +46,7 @@ {-# INLINE im #-} im (unpackQ# -> (# x, y, z, _ #)) = packQ x y z 0.0 {-# INLINE re #-}- re (unpackQ# -> (# _, _, _, w #)) = packQ 0.0 0.0 0.0 w+ re (unpackQ# -> (# _, _, _, w #)) = packQ 0 0 0 w {-# INLINE imVec #-} imVec (unpackQ# -> (# x, y, z, _ #)) = vec3 x y z {-# INLINE taker #-}@@ -72,7 +71,7 @@ (l*z + d*k + t2 * (y*i - x*j)) {-# INLINE getRotScale #-} getRotScale a b = case (# unpackV3# a, unpackV3# b #) of- (# _, (# 0, 0, 0 #) #) -> packQ 0.0 0.0 0.0 0.0+ (# _, (# 0, 0, 0 #) #) -> packQ 0 0 0 0 (# (# 0, 0, 0 #), _ #) -> let x = (1 / 0 :: Double) in packQ x x x x (# (# a1, a2, a3 #), (# b1, b2, b3 #) #) -> let ma = sqrt (a1*a1 + a2*a2 + a3*a3)@@ -99,7 +98,7 @@ (# t1, t2, t3 #) -> packQ (t1 * r) (t2 * r) (t3 * r) (c / ma2) {-# INLINE axisRotation #-} axisRotation v a = case unpackV3# v of- (# 0, 0, 0 #) -> packQ 0.0 0.0 0.0 (if abs a < pi then 1 else -1)+ (# 0, 0, 0 #) -> packQ 0 0 0 (negateUnless (abs a < M_PI) 1) (# x, y, z #) -> let c = cos (a * 0.5) s = sin (a * 0.5)@@ -261,39 +260,35 @@ = QDouble (a - b) {-# INLINE (-) #-} (unpackQ# -> (# a1, a2, a3, a4 #)) * (unpackQ# -> (# b1, b2, b3, b4 #))- = packQ- ((a4 * b1) +- (a1 * b4) +- (a2 * b3) -- (a3 * b2)- )- ((a4 * b2) -- (a1 * b3) +- (a2 * b4) +- (a3 * b1)- )- ((a4 * b3) +- (a1 * b2) -- (a2 * b1) +- (a3 * b4)- )- ((a4 * b4) -- (a1 * b1) -- (a2 * b2) -- (a3 * b3)- )+ = packQ ((a4 * b1) + (a1 * b4) + (a2 * b3) - (a3 * b2))+ ((a4 * b2) - (a1 * b3) + (a2 * b4) + (a3 * b1))+ ((a4 * b3) + (a1 * b2) - (a2 * b1) + (a3 * b4))+ ((a4 * b4) - (a1 * b1) - (a2 * b2) - (a3 * b3)) {-# INLINE (*) #-} negate (QDouble a) = QDouble (negate a) {-# INLINE negate #-}- abs = packQ 0.0 0.0 0.0 . sqrt . square+ abs = packQ 0 0 0 . sqrt . square {-# INLINE abs #-} signum q@(unpackQ# -> (# x, y, z, w #))- = case square q of- 0.0 -> packQ 0.0 0.0 0.0 0.0- qd -> case recip (sqrt qd) of- s -> packQ (x * s) (y * s) (z * s) (w * s)+ | qd == 0 = q+ | otherwise = case ix + iy + iz + iw + nn of+ 0 -> packQ (x * l) (y * l) (z * l) (w * l)+ 1 -> packQ (copysign 1 x) 0 0 0+ 2 -> packQ 0 (copysign 1 y) 0 0+ 4 -> packQ 0 0 (copysign 1 z) 0+ 8 -> packQ 0 0 0 (copysign 1 w)+ _ -> packQ n n n n+ where+ n = 0 / 0 :: Double+ qd = x*x + y*y + z*z + w*w+ ix = if isInfinite x then 1 else 0 :: Int+ iy = if isInfinite y then 2 else 0 :: Int+ iz = if isInfinite z then 4 else 0 :: Int+ iw = if isInfinite w then 8 else 0 :: Int+ nn = if isNaN x || isNaN y || isNaN z || isNaN w then 16 else 0 :: Int+ l = recip (sqrt qd) {-# INLINE signum #-}- fromInteger = packQ 0.0 0.0 0.0 . fromInteger+ fromInteger = packQ 0 0 0 . fromInteger {-# INLINE fromInteger #-} @@ -304,172 +299,253 @@ {-# INLINE (/) #-} a / b = a * recip b {-# INLINE fromRational #-}- fromRational = packQ 0.0 0.0 0.0 . fromRational+ fromRational = packQ 0 0 0 . fromRational instance Floating QDouble where {-# INLINE pi #-}- pi = packQ 0.0 0.0 0.0 3.141592653589793+ pi = packQ 0 0 0 M_PI {-# INLINE exp #-} exp (unpackQ# -> (# x, y, z, w #))- = case (# (x * x) +- (y * y) +- (z * z)- , exp w- #) of- (# 0.0, et #) -> packQ 0.0 0.0 0.0 et- (# mv2, et #) -> case sqrt mv2 of- mv -> case et * sin mv / mv of- l -> packQ (x * l) (y * l) (z * l) (et * cos mv)+ | mv2 == 0 = packQ x y z ew+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ ew = exp w+ l = ew * sin mv / mv+ arg = ew * cos mv {-# INLINE log #-} log = log' (Vec3 1 0 0) {-# INLINE sqrt #-} sqrt = sqrt' (Vec3 1 0 0) {-# INLINE sin #-} sin (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (sin w)- mv2 -> case sqrt mv2 of- mv -> case cos w * sinh mv / mv of- l -> packQ (x * l) (y * l) (z * l) (sin w * cosh mv)+ | mv2 == 0 = packQ x y z (sin w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = cos w * sinh mv / mv+ arg = sin w * cosh mv {-# INLINE cos #-} cos (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (cos w)- mv2 -> case sqrt mv2 of- mv -> case sin w * sinh mv / negate mv of- l -> packQ (x * l) (y * l) (z * l) (cos w * cosh mv)+ | mv2 == 0 = packQ x y z (cos w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = sin w * sinh mv / negate mv+ arg = cos w * cosh mv {-# INLINE tan #-} tan (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (tan w)- mv2 ->- let mv = sqrt mv2- chv = cosh mv- shv = sinh mv- ct = cos w- st = sin w- cq = recip ( (ct * ct * chv * chv) + (st * st * shv * shv) )- l = chv * shv * cq / mv- in packQ (x * l) (y * l) (z * l) (ct * st * cq)+ | mv2 == 0 = packQ x y z (tan w)+ | isInfinite mv2 = signum (packQ x y z 0)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ b = 2*mv+ a = 2*w+ sina = sin a+ eb = exp (-b)+ eb2 = eb*eb+ d = 1 + eb2 + 2 * eb * cos a+ rd = recip d+ pa = M_PI - abs a+ rd' = 2 / (b*b + pa*pa)+ (l, arg) =+ if d >= M_EPS+ then ((1 - eb2) * rd / mv, 2 * sina * eb * rd)+ else (2 * rd' , negate sina * rd') {-# INLINE sinh #-} sinh (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (sinh w)- mv2 -> case sqrt mv2 of- mv -> case cosh w * sin mv / mv of- l -> packQ (x * l) (y * l) (z * l) (sinh w * cos mv)+ | mv2 == 0 = packQ x y z (sinh w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = cosh w * sin mv / mv+ arg = sinh w * cos mv {-# INLINE cosh #-} cosh (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (cosh w)- mv2 -> case sqrt mv2 of- mv -> case sinh w * sin mv / mv of- l -> packQ (x * l) (y * l) (z * l) (cosh w * cos mv)+ | mv2 == 0 = packQ x y z (cosh w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = sinh w * sin mv / mv+ arg = cosh w * cos mv {-# INLINE tanh #-} tanh (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (tanh w)- mv2 ->- let mv = sqrt mv2- cv = cos mv- sv = sin mv- cht = cosh w- sht = sinh w- cq = recip ( (cht * cht * cv * cv) + (sht * sht * sv * sv) )- l = cv * sv * cq / mv- in packQ (x * l) (y * l) (z * l) (cht * sht * cq)+ | mv2 == 0 = packQ x y z (tanh w)+ | isInfinite mv2 = packQ 0 0 0 (signum w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ b = 2*w+ a = 2*mv+ eb = exp (- abs b)+ eb2 = eb*eb+ d = 1 + eb2 + 2 * eb * cos a+ rd = recip d+ pa = M_PI - a+ rd' = 2 / (b*b + pa*pa)+ (l, arg) =+ if d >= M_EPS+ then (2 * sin a * eb * rd / mv, copysign (1 - eb2) b * rd)+ else (2 * rd' , b * rd') {-# INLINE asin #-}- asin q = -i * log' axis (i*q + sqrt' axis (1 - q*q))- where- axis = sigVec q- i = fromVecNum axis 0+ -- The original formula:+ -- asin q = -i * log (i*q + sqrt (1 - q*q))+ -- below is a more numerically stable version.+ asin (unpackQ# -> (# x, y, z, w #))+ | v2 == 0 = if w2 <= 1+ then packQ x y z (asin w)+ else packQ l 0 0 arg+ | otherwise = packQ (x*c) (y*c) (z*c) arg+ where+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ w1qq = 0.5 *(1 - w2 + v2) -- real part of (1 - q*q)/2+ l1qq = sqrt (w1qq*w1qq + w2*v2) -- length of (1 - q*q)/2+ sp2 = l1qq + w1qq+ sn2 = l1qq - w1qq+ sp = sqrt sp2+ sn = copysign (sqrt sn2) w+ -- choose a more stable (symbolically equiv) version+ dp = if 2 * v2 <= sp2 then sp - v else v2 / ((sp + v)*(sn2 + v2))+ dn = if 2 * w2 <= sn2 then w - sn else w2 / ((sn + w)*(sp2 + w2))+ (wD, vD) = case compare w1qq 0 of+ GT -> (dp, w * dp / sp)+ LT -> (v * dn / sn, dn)+ EQ -> (-v, w)+ l = -0.5 * log (wD*wD + vD*vD)+ c = l / v+ arg = atan2 vD wD {-# INLINE acos #-}- acos q = pi/2 - asin q+ acos q = M_PI_2 - asin q {-# INLINE atan #-} -- atan q = i / 2 * log ( (i + q) / (i - q) ) atan (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0 -> packQ 0 0 0 (atan w)- v2 ->- let v = sqrt v2- w2 = w*w- vw2 = w2 + v2- t = 1 + vw2- c = 0.25 * log ((t + 2*v) / (t - 2*v)) / v- in packQ (c*x) (c*y) (c*z) $ 0.5 * atan2 (2*w) (1 - vw2)+ | v2 == 0 = packQ x y z (atan w)+ | otherwise = packQ (x*c) (y*c) (z*c) arg+ where+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ q2 = w2 + v2+ v' = v - 1+ mzero = w2 + v'*v'+ (c, arg) =+ if mzero == 0+ then ( sqrt maxFinite / v, 0)+ else ( 0.25 * (log (1 + q2 + 2*v) - log mzero) / v+ , 0.5 * atan2 (2*w) (1 - q2) ) {-# INLINE asinh #-} -- The original formula: -- asinh q = log (q + sqrt (q*q + 1)) -- below is a more numerically stable version. asinh (unpackQ# -> (# x, y, z, w #))- | v2 == 0- = packQ 0 0 0 (asinh w)- | w == 0- = if v2 <= 1- then let c = asin v / v- in packQ (c*x) (c*y) (c*z) 0- else let c = 0.5 * pi / v- w' = 0.5 * log (2*v2 - 1 + 2 * v * sqrt ( v2 - 1 ))- in packQ (c*x) (c*y) (c*z) w'- | otherwise- = let c = atan2 v t / v- w' = 0.5 * log ( t2 + v2) - log t- + if w >= 0- then log ( t + w )- else log 2 + log w2 - log ( t - w ) - log ( r + w2 + v2 - 1 )- in packQ (c*x) (c*y) (c*z) w'+ | v2 == 0 = packQ x y z (asinh w)+ | otherwise = packQ (x*c) (y*c) (z*c) arg where v2 = (x * x) + (y * y) + (z * z)- v = sqrt v2+ v = sqrt v2 w2 = w*w- wvp1 = w2 - v2 + 1- r = sqrt $ wvp1*wvp1 + 4*w2*v2- t = sqrt $ ( r + wvp1 ) * 0.5- t2 = t*t+ w1qq = 0.5 *(1 + w2 - v2) -- real part of (1 + q*q)/2+ l1qq = sqrt (w1qq*w1qq + w2*v2) -- length of (1 + q*q)/2+ sp2 = l1qq + w1qq+ sn2 = l1qq - w1qq+ sp = sqrt sp2+ sn = copysign (sqrt sn2) w+ -- choose a more stable (symbolically equiv) version+ dp = if 2 * w >= - sp then w + sp else w2 / ((sp - w)*(w2 + sn2))+ dn = if 2 * v <= - sn || sn >= 0+ then v + sn else v2 / ((v - sn)*(v2 + sp2))+ (wD, vD) = case compare w1qq 0 of+ GT -> (dp, v * dp / sp)+ LT -> (w * dn / sn, dn)+ EQ -> (w, v)+ c = atan2 vD wD / v+ arg = 0.5 * log (wD*wD + vD*vD) {-# INLINE acosh #-}+ -- The original formula:+ -- asinh q = log (q + sqrt (q + 1) * sqrt (q - 1))+ -- below is a more numerically stable version. -- note, log (q + sqrt (q*q - 1)) would not work, because that would not -- be the principal value.- acosh q = log' axis (q + sqrt' axis (q + 1) * sqrt' axis (q - 1))+ acosh (unpackQ# -> (# x, y, z, w #))+ | v2 == 0 = packQ x y z (acosh w)+ | otherwise = packQ (x*c) (y*c) (z*c) arg where- axis = sigVec q+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ w1qq = 0.5 *(w2 - v2 - 1) -- real part of (q*q - 1)/2+ l1qq = sqrt (w1qq*w1qq + w2*v2) -- length of (q*q - 1)/2+ sp2 = l1qq + w1qq+ sn2 = l1qq - w1qq+ sp = sqrt sp2+ sn = copysign (sqrt sn2) w+ -- choose a more stable (symbolically equiv) version+ dp = if 2 * w >= - sp then w + sp else w2 / ((w - sp)*(w2 + sn2))+ dn = if 2 * v <= - sn || sn >= 0+ then v + sn else v2 / ((sn - v)*(v2 + sp2))+ (wD, vD) = case compare w1qq 0 of+ GT -> (dp, v * dp / sp)+ LT -> (w * dn / sn, dn)+ EQ -> (w, v)+ c = atan2 vD wD / v+ arg = 0.5 * log (wD*wD + vD*vD) {-# INLINE atanh #-}- atanh q = 0.5 * log' (sigVec q) ((1+q)/(1-q))+ -- atanh q = 0.5 * log ( (1 + q) / (1 - q) )+ atanh (unpackQ# -> (# x, y, z, w #))+ | v2 == 0 = packQ x y z (atanh w)+ | otherwise = packQ (x*c) (y*c) (z*c) (copysign arg w)+ where+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ q2 = w2 + v2+ w' = abs w - 1+ c = 0.5 * atan2 (2*v) (1 - q2) / v+ arg = if w' == 0+ then (1/0)+ else 0.25 * (log (1 + q2 + 2 * abs w) - log (v2 + w'*w')) + -- If q is negative real, provide a fallback axis to align log.-log' :: Vec3d -> QDouble -> QDouble+log' :: Vector Double 3 -> QDouble -> QDouble log' r (unpackQ# -> (# x, y, z, w #)) = case (x * x) + (y * y) + (z * z) of 0.0 | w >= 0- -> packQ 0.0 0.0 0.0 (log w)+ -> packQ 0 0 0 (log w) | Vec3 rx ry rz <- r- -> packQ (pi*rx) (pi*ry) (pi*rz) (log (negate w))- mv2 -> case (# sqrt (mv2 + (w * w)), sqrt mv2 #) of- (# mq, mv #) -> case atan2 mv w / mv of- l -> packQ (x * l) (y * l) (z * l) (log mq)+ -> packQ (M_PI*rx) (M_PI*ry) (M_PI*rz) (log (negate w))+ mv2 -> case (# mv2 + w * w, sqrt mv2 #) of+ (# q2, mv #) -> case atan2 mv w / mv of+ l -> packQ (x * l) (y * l) (z * l) (0.5 * log q2) -- If q is negative real, provide a fallback axis to align sqrt.-sqrt' :: Vec3d -> QDouble -> QDouble+sqrt' :: Vector Double 3 -> QDouble -> QDouble sqrt' r (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 | w >= 0- -> packQ 0.0 0.0 0.0 (sqrt w)- | Vec3 rx ry rz <- r- , sw <- sqrt (negate w)- -> packQ (sw*rx) (sw*ry) (sw*rz) 0- mv2 ->- let mq = sqrt (mv2 + w * w)- l2 = sqrt mq- tq = w / (mq * 2.0)- sina = sqrt (0.5 - tq) * l2 / sqrt mv2- in packQ (x * sina) (y * sina) (z * sina) (sqrt (0.5 + tq) * l2)--sigVec :: QDouble -> Vec3d-sigVec (unpackQ# -> (# x, y, z, _ #))- = case sqrt ((x * x) + (y * y) + (z * z)) of- 0 -> vec3 1 0 0- l -> vec3 (x/l) (y/l) (z/l)+ | v2 == 0 && w >= 0+ = packQ x y z (sqrt w)+ | v2 == 0+ , Vec3 rx ry rz <- r+ , sw <- sqrt (negate w)+ = packQ (sw*rx) (sw*ry) (sw*rz) 0+ | otherwise+ = packQ (x * c) (y * c) (z * c) arg+ where+ v2 = (x * x) + (y * y) + (z * z)+ mq = sqrt (v2 + w * w)+ arg = sqrt $ 0.5 * if w >= 0 then mq + w else v2 / (mq - w)+ c = 0.5 / arg instance Eq QDouble where {-# INLINE (==) #-}
src/Numeric/Quaternion/Internal/QFloat.hs view
@@ -10,19 +10,18 @@ {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Numeric.Quaternion.Internal.QFloat ( QFloat, Quater (..) ) where import qualified Control.Monad.ST as ST import Data.Coerce (coerce)+import Numeric.Basics import Numeric.DataFrame.Internal.PrimArray import qualified Numeric.DataFrame.ST as ST import Numeric.DataFrame.Type-import Numeric.PrimBytes (PrimBytes) import Numeric.Quaternion.Internal-import Numeric.Scalar.Internal import Numeric.Vector.Internal type QFloat = Quater Float@@ -31,11 +30,11 @@ deriving instance PrimArray Float (Quater Float) instance Quaternion Float where- newtype Quater Float = QFloat Vec4f+ newtype Quater Float = QFloat (Vector Float 4) {-# INLINE packQ #-}- packQ = coerce (vec4 :: Float -> Float -> Float -> Float -> Vec4f)+ packQ = coerce (vec4 :: Float -> Float -> Float -> Float -> Vector Float 4) {-# INLINE unpackQ# #-}- unpackQ# = coerce (unpackV4# :: Vec4f -> (# Float, Float, Float, Float #))+ unpackQ# = coerce (unpackV4# :: Vector Float 4 -> (# Float, Float, Float, Float #)) {-# INLINE fromVecNum #-} fromVecNum (unpackV3# -> (# x, y, z #)) = packQ x y z {-# INLINE fromVec4 #-}@@ -47,7 +46,7 @@ {-# INLINE im #-} im (unpackQ# -> (# x, y, z, _ #)) = packQ x y z 0.0 {-# INLINE re #-}- re (unpackQ# -> (# _, _, _, w #)) = packQ 0.0 0.0 0.0 w+ re (unpackQ# -> (# _, _, _, w #)) = packQ 0 0 0 w {-# INLINE imVec #-} imVec (unpackQ# -> (# x, y, z, _ #)) = vec3 x y z {-# INLINE taker #-}@@ -72,7 +71,7 @@ (l*z + d*k + t2 * (y*i - x*j)) {-# INLINE getRotScale #-} getRotScale a b = case (# unpackV3# a, unpackV3# b #) of- (# _, (# 0, 0, 0 #) #) -> packQ 0.0 0.0 0.0 0.0+ (# _, (# 0, 0, 0 #) #) -> packQ 0 0 0 0 (# (# 0, 0, 0 #), _ #) -> let x = (1 / 0 :: Float) in packQ x x x x (# (# a1, a2, a3 #), (# b1, b2, b3 #) #) -> let ma = sqrt (a1*a1 + a2*a2 + a3*a3)@@ -99,7 +98,7 @@ (# t1, t2, t3 #) -> packQ (t1 * r) (t2 * r) (t3 * r) (c / ma2) {-# INLINE axisRotation #-} axisRotation v a = case unpackV3# v of- (# 0, 0, 0 #) -> packQ 0.0 0.0 0.0 (if abs a < pi then 1 else -1)+ (# 0, 0, 0 #) -> packQ 0 0 0 (negateUnless (abs a < M_PI) 1) (# x, y, z #) -> let c = cos (a * 0.5) s = sin (a * 0.5)@@ -261,39 +260,35 @@ = QFloat (a - b) {-# INLINE (-) #-} (unpackQ# -> (# a1, a2, a3, a4 #)) * (unpackQ# -> (# b1, b2, b3, b4 #))- = packQ- ((a4 * b1) +- (a1 * b4) +- (a2 * b3) -- (a3 * b2)- )- ((a4 * b2) -- (a1 * b3) +- (a2 * b4) +- (a3 * b1)- )- ((a4 * b3) +- (a1 * b2) -- (a2 * b1) +- (a3 * b4)- )- ((a4 * b4) -- (a1 * b1) -- (a2 * b2) -- (a3 * b3)- )+ = packQ ((a4 * b1) + (a1 * b4) + (a2 * b3) - (a3 * b2))+ ((a4 * b2) - (a1 * b3) + (a2 * b4) + (a3 * b1))+ ((a4 * b3) + (a1 * b2) - (a2 * b1) + (a3 * b4))+ ((a4 * b4) - (a1 * b1) - (a2 * b2) - (a3 * b3)) {-# INLINE (*) #-} negate (QFloat a) = QFloat (negate a) {-# INLINE negate #-}- abs = packQ 0.0 0.0 0.0 . sqrt . square+ abs = packQ 0 0 0 . sqrt . square {-# INLINE abs #-} signum q@(unpackQ# -> (# x, y, z, w #))- = case square q of- 0.0 -> packQ 0.0 0.0 0.0 0.0- qd -> case recip (sqrt qd) of- s -> packQ (x * s) (y * s) (z * s) (w * s)+ | qd == 0 = q+ | otherwise = case ix + iy + iz + iw + nn of+ 0 -> packQ (x * l) (y * l) (z * l) (w * l)+ 1 -> packQ (copysign 1 x) 0 0 0+ 2 -> packQ 0 (copysign 1 y) 0 0+ 4 -> packQ 0 0 (copysign 1 z) 0+ 8 -> packQ 0 0 0 (copysign 1 w)+ _ -> packQ n n n n+ where+ n = 0 / 0 :: Float+ qd = x*x + y*y + z*z + w*w+ ix = if isInfinite x then 1 else 0 :: Int+ iy = if isInfinite y then 2 else 0 :: Int+ iz = if isInfinite z then 4 else 0 :: Int+ iw = if isInfinite w then 8 else 0 :: Int+ nn = if isNaN x || isNaN y || isNaN z || isNaN w then 16 else 0 :: Int+ l = recip (sqrt qd) {-# INLINE signum #-}- fromInteger = packQ 0.0 0.0 0.0 . fromInteger+ fromInteger = packQ 0 0 0 . fromInteger {-# INLINE fromInteger #-} @@ -304,172 +299,253 @@ {-# INLINE (/) #-} a / b = a * recip b {-# INLINE fromRational #-}- fromRational = packQ 0.0 0.0 0.0 . fromRational+ fromRational = packQ 0 0 0 . fromRational instance Floating QFloat where {-# INLINE pi #-}- pi = packQ 0.0 0.0 0.0 3.141592653589793+ pi = packQ 0 0 0 M_PI {-# INLINE exp #-} exp (unpackQ# -> (# x, y, z, w #))- = case (# (x * x) +- (y * y) +- (z * z)- , exp w- #) of- (# 0.0, et #) -> packQ 0.0 0.0 0.0 et- (# mv2, et #) -> case sqrt mv2 of- mv -> case et * sin mv / mv of- l -> packQ (x * l) (y * l) (z * l) (et * cos mv)+ | mv2 == 0 = packQ x y z ew+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ ew = exp w+ l = ew * sin mv / mv+ arg = ew * cos mv {-# INLINE log #-} log = log' (Vec3 1 0 0) {-# INLINE sqrt #-} sqrt = sqrt' (Vec3 1 0 0) {-# INLINE sin #-} sin (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (sin w)- mv2 -> case sqrt mv2 of- mv -> case cos w * sinh mv / mv of- l -> packQ (x * l) (y * l) (z * l) (sin w * cosh mv)+ | mv2 == 0 = packQ x y z (sin w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = cos w * sinh mv / mv+ arg = sin w * cosh mv {-# INLINE cos #-} cos (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (cos w)- mv2 -> case sqrt mv2 of- mv -> case sin w * sinh mv / negate mv of- l -> packQ (x * l) (y * l) (z * l) (cos w * cosh mv)+ | mv2 == 0 = packQ x y z (cos w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = sin w * sinh mv / negate mv+ arg = cos w * cosh mv {-# INLINE tan #-} tan (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (tan w)- mv2 ->- let mv = sqrt mv2- chv = cosh mv- shv = sinh mv- ct = cos w- st = sin w- cq = recip ( (ct * ct * chv * chv) + (st * st * shv * shv) )- l = chv * shv * cq / mv- in packQ (x * l) (y * l) (z * l) (ct * st * cq)+ | mv2 == 0 = packQ x y z (tan w)+ | isInfinite mv2 = signum (packQ x y z 0)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ b = 2*mv+ a = 2*w+ sina = sin a+ eb = exp (-b)+ eb2 = eb*eb+ d = 1 + eb2 + 2 * eb * cos a+ rd = recip d+ pa = M_PI - abs a+ rd' = 2 / (b*b + pa*pa)+ (l, arg) =+ if d >= M_EPS+ then ((1 - eb2) * rd / mv, 2 * sina * eb * rd)+ else (2 * rd' , negate sina * rd') {-# INLINE sinh #-} sinh (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (sinh w)- mv2 -> case sqrt mv2 of- mv -> case cosh w * sin mv / mv of- l -> packQ (x * l) (y * l) (z * l) (sinh w * cos mv)+ | mv2 == 0 = packQ x y z (sinh w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = cosh w * sin mv / mv+ arg = sinh w * cos mv {-# INLINE cosh #-} cosh (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (cosh w)- mv2 -> case sqrt mv2 of- mv -> case sinh w * sin mv / mv of- l -> packQ (x * l) (y * l) (z * l) (cosh w * cos mv)+ | mv2 == 0 = packQ x y z (cosh w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ l = sinh w * sin mv / mv+ arg = cosh w * cos mv {-# INLINE tanh #-} tanh (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 -> packQ 0.0 0.0 0.0 (tanh w)- mv2 ->- let mv = sqrt mv2- cv = cos mv- sv = sin mv- cht = cosh w- sht = sinh w- cq = recip ( (cht * cht * cv * cv) + (sht * sht * sv * sv) )- l = cv * sv * cq / mv- in packQ (x * l) (y * l) (z * l) (cht * sht * cq)+ | mv2 == 0 = packQ x y z (tanh w)+ | isInfinite mv2 = packQ 0 0 0 (signum w)+ | otherwise = packQ (x * l) (y * l) (z * l) arg+ where+ mv2 = (x * x) + (y * y) + (z * z)+ mv = sqrt mv2+ b = 2*w+ a = 2*mv+ eb = exp (- abs b)+ eb2 = eb*eb+ d = 1 + eb2 + 2 * eb * cos a+ rd = recip d+ pa = M_PI - a+ rd' = 2 / (b*b + pa*pa)+ (l, arg) =+ if d >= M_EPS+ then (2 * sin a * eb * rd / mv, copysign (1 - eb2) b * rd)+ else (2 * rd' , b * rd') {-# INLINE asin #-}- asin q = -i * log' axis (i*q + sqrt' axis (1 - q*q))- where- axis = sigVec q- i = fromVecNum axis 0+ -- The original formula:+ -- asin q = -i * log (i*q + sqrt (1 - q*q))+ -- below is a more numerically stable version.+ asin (unpackQ# -> (# x, y, z, w #))+ | v2 == 0 = if w2 <= 1+ then packQ x y z (asin w)+ else packQ l 0 0 arg+ | otherwise = packQ (x*c) (y*c) (z*c) arg+ where+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ w1qq = 0.5 *(1 - w2 + v2) -- real part of (1 - q*q)/2+ l1qq = sqrt (w1qq*w1qq + w2*v2) -- length of (1 - q*q)/2+ sp2 = l1qq + w1qq+ sn2 = l1qq - w1qq+ sp = sqrt sp2+ sn = copysign (sqrt sn2) w+ -- choose a more stable (symbolically equiv) version+ dp = if 2 * v2 <= sp2 then sp - v else v2 / ((sp + v)*(sn2 + v2))+ dn = if 2 * w2 <= sn2 then w - sn else w2 / ((sn + w)*(sp2 + w2))+ (wD, vD) = case compare w1qq 0 of+ GT -> (dp, w * dp / sp)+ LT -> (v * dn / sn, dn)+ EQ -> (-v, w)+ l = -0.5 * log (wD*wD + vD*vD)+ c = l / v+ arg = atan2 vD wD {-# INLINE acos #-}- acos q = pi/2 - asin q+ acos q = M_PI_2 - asin q {-# INLINE atan #-} -- atan q = i / 2 * log ( (i + q) / (i - q) ) atan (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0 -> packQ 0 0 0 (atan w)- v2 ->- let v = sqrt v2- w2 = w*w- vw2 = w2 + v2- t = 1 + vw2- c = 0.25 * log ((t + 2*v) / (t - 2*v)) / v- in packQ (c*x) (c*y) (c*z) $ 0.5 * atan2 (2*w) (1 - vw2)+ | v2 == 0 = packQ x y z (atan w)+ | otherwise = packQ (x*c) (y*c) (z*c) arg+ where+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ q2 = w2 + v2+ v' = v - 1+ mzero = w2 + v'*v'+ (c, arg) =+ if mzero == 0+ then ( sqrt maxFinite / v, 0)+ else ( 0.25 * (log (1 + q2 + 2*v) - log mzero) / v+ , 0.5 * atan2 (2*w) (1 - q2) ) {-# INLINE asinh #-} -- The original formula: -- asinh q = log (q + sqrt (q*q + 1)) -- below is a more numerically stable version. asinh (unpackQ# -> (# x, y, z, w #))- | v2 == 0- = packQ 0 0 0 (asinh w)- | w == 0- = if v2 <= 1- then let c = asin v / v- in packQ (c*x) (c*y) (c*z) 0- else let c = 0.5 * pi / v- w' = 0.5 * log (2*v2 - 1 + 2 * v * sqrt ( v2 - 1 ))- in packQ (c*x) (c*y) (c*z) w'- | otherwise- = let c = atan2 v t / v- w' = 0.5 * log ( t2 + v2) - log t- + if w >= 0- then log ( t + w )- else log 2 + log w2 - log ( t - w ) - log ( r + w2 + v2 - 1 )- in packQ (c*x) (c*y) (c*z) w'+ | v2 == 0 = packQ x y z (asinh w)+ | otherwise = packQ (x*c) (y*c) (z*c) arg where v2 = (x * x) + (y * y) + (z * z)- v = sqrt v2+ v = sqrt v2 w2 = w*w- wvp1 = w2 - v2 + 1- r = sqrt $ wvp1*wvp1 + 4*w2*v2- t = sqrt $ ( r + wvp1 ) * 0.5- t2 = t*t+ w1qq = 0.5 *(1 + w2 - v2) -- real part of (1 + q*q)/2+ l1qq = sqrt (w1qq*w1qq + w2*v2) -- length of (1 + q*q)/2+ sp2 = l1qq + w1qq+ sn2 = l1qq - w1qq+ sp = sqrt sp2+ sn = copysign (sqrt sn2) w+ -- choose a more stable (symbolically equiv) version+ dp = if 2 * w >= - sp then w + sp else w2 / ((sp - w)*(w2 + sn2))+ dn = if 2 * v <= - sn || sn >= 0+ then v + sn else v2 / ((v - sn)*(v2 + sp2))+ (wD, vD) = case compare w1qq 0 of+ GT -> (dp, v * dp / sp)+ LT -> (w * dn / sn, dn)+ EQ -> (w, v)+ c = atan2 vD wD / v+ arg = 0.5 * log (wD*wD + vD*vD) {-# INLINE acosh #-}+ -- The original formula:+ -- asinh q = log (q + sqrt (q + 1) * sqrt (q - 1))+ -- below is a more numerically stable version. -- note, log (q + sqrt (q*q - 1)) would not work, because that would not -- be the principal value.- acosh q = log' axis (q + sqrt' axis (q + 1) * sqrt' axis (q - 1))+ acosh (unpackQ# -> (# x, y, z, w #))+ | v2 == 0 = packQ x y z (acosh w)+ | otherwise = packQ (x*c) (y*c) (z*c) arg where- axis = sigVec q+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ w1qq = 0.5 *(w2 - v2 - 1) -- real part of (q*q - 1)/2+ l1qq = sqrt (w1qq*w1qq + w2*v2) -- length of (q*q - 1)/2+ sp2 = l1qq + w1qq+ sn2 = l1qq - w1qq+ sp = sqrt sp2+ sn = copysign (sqrt sn2) w+ -- choose a more stable (symbolically equiv) version+ dp = if 2 * w >= - sp then w + sp else w2 / ((w - sp)*(w2 + sn2))+ dn = if 2 * v <= - sn || sn >= 0+ then v + sn else v2 / ((sn - v)*(v2 + sp2))+ (wD, vD) = case compare w1qq 0 of+ GT -> (dp, v * dp / sp)+ LT -> (w * dn / sn, dn)+ EQ -> (w, v)+ c = atan2 vD wD / v+ arg = 0.5 * log (wD*wD + vD*vD) {-# INLINE atanh #-}- atanh q = 0.5 * log ((1+q)/(1-q))+ -- atanh q = 0.5 * log ( (1 + q) / (1 - q) )+ atanh (unpackQ# -> (# x, y, z, w #))+ | v2 == 0 = packQ x y z (atanh w)+ | otherwise = packQ (x*c) (y*c) (z*c) (copysign arg w)+ where+ v2 = (x * x) + (y * y) + (z * z)+ v = sqrt v2+ w2 = w*w+ q2 = w2 + v2+ w' = abs w - 1+ c = 0.5 * atan2 (2*v) (1 - q2) / v+ arg = if w' == 0+ then (1/0)+ else 0.25 * (log (1 + q2 + 2 * abs w) - log (v2 + w'*w')) + -- If q is negative real, provide a fallback axis to align log.-log' :: Vec3f -> QFloat -> QFloat+log' :: Vector Float 3 -> QFloat -> QFloat log' r (unpackQ# -> (# x, y, z, w #)) = case (x * x) + (y * y) + (z * z) of 0.0 | w >= 0- -> packQ 0.0 0.0 0.0 (log w)+ -> packQ 0 0 0 (log w) | Vec3 rx ry rz <- r- -> packQ (pi*rx) (pi*ry) (pi*rz) (log (negate w))- mv2 -> case (# sqrt (mv2 + (w * w)), sqrt mv2 #) of- (# mq, mv #) -> case atan2 mv w / mv of- l -> packQ (x * l) (y * l) (z * l) (log mq)+ -> packQ (M_PI*rx) (M_PI*ry) (M_PI*rz) (log (negate w))+ mv2 -> case (# mv2 + w * w, sqrt mv2 #) of+ (# q2, mv #) -> case atan2 mv w / mv of+ l -> packQ (x * l) (y * l) (z * l) (0.5 * log q2) -- If q is negative real, provide a fallback axis to align sqrt.-sqrt' :: Vec3f -> QFloat -> QFloat+sqrt' :: Vector Float 3 -> QFloat -> QFloat sqrt' r (unpackQ# -> (# x, y, z, w #))- = case (x * x) + (y * y) + (z * z) of- 0.0 | w >= 0- -> packQ 0.0 0.0 0.0 (sqrt w)- | Vec3 rx ry rz <- r- , sw <- sqrt (negate w)- -> packQ (sw*rx) (sw*ry) (sw*rz) 0- mv2 ->- let mq = sqrt (mv2 + w * w)- l2 = sqrt mq- tq = w / (mq * 2.0)- sina = sqrt (0.5 - tq) * l2 / sqrt mv2- in packQ (x * sina) (y * sina) (z * sina) (sqrt (0.5 + tq) * l2)--sigVec :: QFloat -> Vec3f-sigVec (unpackQ# -> (# x, y, z, _ #))- = case sqrt ((x * x) + (y * y) + (z * z)) of- 0 -> vec3 1 0 0- l -> vec3 (x/l) (y/l) (z/l)+ | v2 == 0 && w >= 0+ = packQ x y z (sqrt w)+ | v2 == 0+ , Vec3 rx ry rz <- r+ , sw <- sqrt (negate w)+ = packQ (sw*rx) (sw*ry) (sw*rz) 0+ | otherwise+ = packQ (x * c) (y * c) (z * c) arg+ where+ v2 = (x * x) + (y * y) + (z * z)+ mq = sqrt (v2 + w * w)+ arg = sqrt $ 0.5 * if w >= 0 then mq + w else v2 / (mq - w)+ c = 0.5 / arg instance Eq QFloat where {-# INLINE (==) #-}
src/Numeric/Scalar/Internal.hs view
@@ -11,11 +11,8 @@ , pattern S ) where --import Data.Type.Lits (Nat)-import GHC.Base (unsafeCoerce#)-import Numeric.DataFrame.SubSpace (SubSpace (ewgen))-import Numeric.DataFrame.Type (DataFrame, pattern S)+import Numeric.DataFrame.Internal.PrimArray (broadcast)+import Numeric.DataFrame.Type -- | Alias for zero-dimensional DataFrame type Scalar t = DataFrame t ('[] :: [Nat])@@ -24,20 +21,8 @@ type Sci = Scalar Int type Scw = Scalar Word --- | Convert scalar back to ordinary type-unScalar :: Scalar t -> t--- rely on that Scalar is just two times newtype alias to t-unScalar = unsafeCoerce#-{-# INLINE unScalar #-}---- | Convert any type to scalar wrapper-scalar :: t -> Scalar t--- rely on that Scalar is just two times newtype alias to t-scalar = unsafeCoerce#-{-# INLINE scalar #-}- -- | Broadcast scalar value onto a whole data frame-fromScalar :: SubSpace t ds '[] ds- => Scalar t -> DataFrame t ds-fromScalar = ewgen+fromScalar :: PrimArray t (DataFrame t ds)+ => Scalar t -> DataFrame t (ds :: [Nat])+fromScalar = broadcast . unScalar {-# INLINE fromScalar #-}
+ src/Numeric/Subroutine/Householder.hs view
@@ -0,0 +1,278 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Numeric.Subroutine.Householder+ ( householderReflectionInplaceR+ , householderReflectionInplaceL+ ) where+++import Control.Monad+import Control.Monad.ST+import Data.Kind+import Numeric.Basics+import Numeric.DataFrame.ST+import Numeric.DataFrame.Type+import Numeric.Dimensions+import Numeric.Scalar.Internal+++{- | Run a Householder transformation inplace.++ Given some orthongonal matrix \(P\), some matrix \(R\) and index \((k,l)\),+ reflects \(R\) along some hyperplane, such that all elements of \(R\)+ below index \( (k, l) \) become zeros,+ then updates \(P\) with the inverse of the same transform as \(R\).++ Notes and invariants:++ 1. The transformation happens inplace for both matrices \(P\) and \(R\);+ if \( R = P^\intercal A \), then \( R' = P^*PR = P'^\intercal A \), where+ \( P' \) and \( R' \) are the updated versions of the input matrices,+ \( P^* \) and \( A \) are implicit matrices.++ 2. All elements below and to the left of index \(k,l\) in \(R\)+ are assumed (and not checked) to be zeros;+ these are not touched by the subroutine to save flops.++ 3. A logical starting value for \(P\) is an identity matrix.+ The subroutine can be used for a QR decomposition:+ \( Q = P \).++ Returns @True@ if reflection has been performed, and @False@ if it was not needed.+ This can be used to track the sign of @det P@.+ -}+householderReflectionInplaceL ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat)+ . (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m)+ => STDataFrame s t '[n] -- ^ Temporary buffer for a Householder axis vector+ -> STDataFrame s t '[n,n] -- ^ Current state of \(P^\intercal\)+ -> STDataFrame s t '[n,m] -- ^ Current state of \(R\)+ -> Idxs '[n,m] -- ^ Pivot element+ -> ST s Bool+householderReflectionInplaceL u p r (Idx i :* Idx j :* U)+ = householderReflectionInplaceL' u p r+ (fromIntegral $ dimVal' @n)+ (fromIntegral $ dimVal' @m)+ (fromIntegral i)+ (fromIntegral j)++householderReflectionInplaceL' ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat)+ . (PrimBytes t, Epsilon t, Ord t)+ => STDataFrame s t '[n] -- ^ Temporary buffer for a Householder axis vector+ -> STDataFrame s t '[n,n] -- ^ \(P^\intercal\)+ -> STDataFrame s t '[n,m] -- ^ \(R\)+ -> Int -- ^ \(n\)+ -> Int -- ^ \(m\)+ -> Int -- ^ \( 0 \leq k < n \)+ -> Int -- ^ \( 0 \leq l < m \)+ -> ST s Bool+householderReflectionInplaceL' uPtr pPtr rPtr n m k l = do+ -- pivot element (k,l) of new R+ alpha <- getAlphaAndUpdateU+ u2 <- getU2+ -- u2 == 0 means the column is already zeroed+ if u2 > M_EPS+ then do+ let c = 2 / u2 -- a mult constant for updating matrices+ -- update R+ updateRl alpha+ forM_ [l+1..m-1] $ updateRi c+ -- update P+ forM_ [0..n-1] $ updatePi c+ return True+ else return False+ where+ n' = n - k -- remaining rows+ rOff0 = k*m + l -- offset of element (k,l) in matrix R++ -- u = Rk - alpha*ek+ getAlphaAndUpdateU :: ST s (Scalar t)+ getAlphaAndUpdateU = do+ alpha' <- sqrt . fst <$> nTimesM n'+ (\(r, off) -> do+ x <- readDataFrameOff rPtr off+ when (abs x <= M_EPS) $ writeDataFrameOff rPtr off 0+ return (r + x*x, off + m)+ ) (0, rOff0)+ x0 <- readDataFrameOff rPtr rOff0+ let alpha = if x0 >= 0 then negate alpha' else alpha'+ -- update (lower part of) u+ writeDataFrameOff uPtr k (x0 - alpha)+ when (n' >= 1) $ void $ nTimesM (n' - 1)+ (\(i, off) -> (i+1, off+m) <$+ (readDataFrameOff rPtr off >>= writeDataFrameOff uPtr i)+ ) (k+1, rOff0+m)+ return alpha++ -- l-th column of R zeroes below pivot+ updateRl :: Scalar t -> ST s ()+ updateRl alpha = do+ writeDataFrameOff rPtr rOff0 alpha+ when (n' >= 1) $ void $ nTimesM (n' - 1)+ (\off -> (off+m) <$ writeDataFrameOff rPtr off 0) (rOff0+m)++ -- update i-th column of R+ updateRi :: Scalar t -> Int -> ST s ()+ updateRi c i = do+ -- dot product of u and Ri+ uRi <- fmap fst . flip (nTimesM n') (0, (k, k*m+i)) $ \(r, (j, off)) -> do+ ju <- readDataFrameOff uPtr j+ jiR <- readDataFrameOff rPtr off+ return (r + ju * jiR, (j+1, off+m))+ let c' = c * uRi+ -- update each element+ void $ flip (nTimesM n') (k, k*m+i) $ \(j, off) -> do+ ju <- readDataFrameOff uPtr j+ jiR <- readDataFrameOff rPtr off+ writeDataFrameOff rPtr off $ jiR - c'*ju+ return (j+1, off+m)++ -- update i-th row of P+ updatePi :: Scalar t -> Int -> ST s ()+ updatePi c i = do+ let off0 = i*n+ -- dot product of u and Pi+ uPi <- fmap fst . flip (nTimesM n') (0, k) $ \(r, j) -> do+ ju <- readDataFrameOff uPtr j+ ijP <- readDataFrameOff pPtr (off0 + j)+ return (r + ju * ijP, j+1)+ let c' = c * uPi+ -- update each element+ forM_ [k..n-1] $ \j -> do+ ju <- readDataFrameOff uPtr j+ ijP <- readDataFrameOff pPtr (off0 + j)+ writeDataFrameOff pPtr (off0 + j) $ ijP - c'*ju++ -- get module squared of u (for Q = I - 2 u*uT / |u|^2 )+ getU2 :: ST s (Scalar t)+ getU2 = fst <$> nTimesM n'+ (\(r, off) -> (\x -> (r + x*x, off + 1)) <$> readDataFrameOff uPtr off) (0, k)++{- | Run a Householder transformation inplace.++ Similar to `householderReflectionInplaceR`, but works from right to left+ - use to zero elements to the right from the pivot.++ Returns @True@ if reflection has been performed, and @False@ if it was not needed.+ This can be used to track the sign of @det P@.+ -}+householderReflectionInplaceR ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat)+ . (PrimBytes t, Ord t, Epsilon t, KnownDim n, KnownDim m)+ => STDataFrame s t '[m] -- ^ Temporary buffer for a Householder axis vector+ -> STDataFrame s t '[m,m] -- ^ Current state of \(P^\intercal\)+ -> STDataFrame s t '[n,m] -- ^ Current state of \(R\)+ -> Idxs '[n,m] -- ^ Pivot element+ -> ST s Bool+householderReflectionInplaceR u p r (Idx i :* Idx j :* U)+ = householderReflectionInplaceR' u p r+ (fromIntegral $ dimVal' @n)+ (fromIntegral $ dimVal' @m)+ (fromIntegral i)+ (fromIntegral j)++householderReflectionInplaceR' ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat)+ . (PrimBytes t, Epsilon t, Ord t)+ => STDataFrame s t '[m] -- ^ Temporary buffer for a Householder axis vector+ -> STDataFrame s t '[m,m] -- ^ \(P^\intercal\)+ -> STDataFrame s t '[n,m] -- ^ \(R\)+ -> Int -- ^ \(n\)+ -> Int -- ^ \(m\)+ -> Int -- ^ \( 0 \leq k < n \)+ -> Int -- ^ \( 0 \leq l < m \)+ -> ST s Bool+householderReflectionInplaceR' uPtr pPtr rPtr n m k l = do+ -- pivot element (k,l) of new R+ alpha <- getAlphaAndUpdateU+ u2 <- getU2+ -- u2 == 0 means the column is already zeroed+ if u2 > M_EPS+ then do+ let c = 2 / u2 -- a mult constant for updating matrices+ -- update R+ updateRk alpha+ forM_ [k+1..n-1] $ updateRi c+ -- update P+ forM_ [0..m-1] $ updatePi c+ return True+ else return False+ where+ m' = m - l -- remaining cols+ rOff0 = k*m + l -- offset of element (k,l) in matrix R++ -- u = Rl - alpha*el+ getAlphaAndUpdateU :: ST s (Scalar t)+ getAlphaAndUpdateU = do+ alpha' <- sqrt . fst <$> nTimesM m'+ (\(r, off) -> do+ x <- readDataFrameOff rPtr off+ when (abs x <= M_EPS) $ writeDataFrameOff rPtr off 0+ return (r + x*x, off + 1)+ ) (0, rOff0)+ x0 <- readDataFrameOff rPtr rOff0+ let alpha = if x0 >= 0 then negate alpha' else alpha'+ -- update (lower part of) u+ writeDataFrameOff uPtr l (x0 - alpha)+ forM_ [1..m'-1] $ \i ->+ readDataFrameOff rPtr (rOff0 + i) >>= writeDataFrameOff uPtr (l + i)+ return alpha++ -- k-th row of R zeroes below pivot+ updateRk :: Scalar t -> ST s ()+ updateRk alpha = do+ writeDataFrameOff rPtr rOff0 alpha+ forM_ [rOff0+1..rOff0+m'-1] $ flip (writeDataFrameOff rPtr) 0++ -- update i-th row of R+ updateRi :: Scalar t -> Int -> ST s ()+ updateRi c i = do+ let off0 = i*m+ -- dot product of u and Ri+ uRi <- fmap fst . flip (nTimesM m') (0, l) $ \(r, j) -> do+ ju <- readDataFrameOff uPtr j+ jiR <- readDataFrameOff rPtr (off0 + j)+ return (r + ju * jiR, j+1)+ let c' = c * uRi+ -- update each element+ forM_ [l..m-1] $ \j -> do+ ju <- readDataFrameOff uPtr j+ jiR <- readDataFrameOff rPtr (off0 + j)+ writeDataFrameOff rPtr (off0 + j) $ jiR - c'*ju++ -- update i-th row of P+ updatePi :: Scalar t -> Int -> ST s ()+ updatePi c i = do+ let off0 = i*m+ -- dot product of u and Pi+ uPi <- fmap fst . flip (nTimesM m') (0, l) $ \(r, j) -> do+ ju <- readDataFrameOff uPtr j+ ijP <- readDataFrameOff pPtr (off0 + j)+ return (r + ju * ijP, j+1)+ let c' = c * uPi+ -- update each element+ forM_ [l..m-1] $ \j -> do+ ju <- readDataFrameOff uPtr j+ ijP <- readDataFrameOff pPtr (off0 + j)+ writeDataFrameOff pPtr (off0 + j) $ ijP - c'*ju++ -- get module squared of u (for Q = I - 2 u*uT / |u|^2 )+ getU2 :: ST s (Scalar t)+ getU2 = fst <$> nTimesM m'+ (\(r, off) -> (\x -> (r + x*x, off + 1)) <$> readDataFrameOff uPtr off) (0, l)+++nTimesM :: Monad m => Int -> (a -> m a) -> a -> m a+nTimesM 0 _ x = pure x+nTimesM n m x = m x >>= nTimesM (n - 1) m
+ src/Numeric/Subroutine/SolveTriangular.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{- |+A few ways to solve a system of linear equations in ST monad.+The tesult is always computed inplace.+ -}+module Numeric.Subroutine.SolveTriangular+ ( solveUpperTriangularR+ , solveUpperTriangularL+ , solveLowerTriangularR+ , solveLowerTriangularL+ ) where+++import Control.Monad+import Control.Monad.ST+import Data.Kind+import Numeric.DataFrame.Internal.PrimArray+import Numeric.DataFrame.ST+import Numeric.DataFrame.Type+import Numeric.Dimensions+++{- |+Solve a system of linear equations \( Rx = b \)+ or a linear least squares problem \( \min {|| Rx - b ||}^2 \),+where \( R \) is an upper-triangular matrix.++DataFrame \( b \) is modified in-place; by the end of the process \( b_m = x \).++NB: you can use `subDataFrameView` to truncate @b@ without performing a copy.+ -}+solveUpperTriangularR ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (PrimBytes t, Fractional t, Eq t, KnownDim m, m <= n)+ => DataFrame t '[n,m] -- ^ \(R\)+ -> STDataFrame s t (m :+ ds) -- ^ Current state of \(b_m\)+ -- (first @m@ rows of @b@)+ -> ST s ()+solveUpperTriangularR r bPtr | Dict <- Dict @(m <= n) = mapM_ go [m-1,m-2..0]+ where+ m = fromIntegral $ dimVal' @m :: Int+ k = fromIntegral kw :: Int+ CumulDims (_:kw:_) = getDataFrameSteps bPtr+ go :: Int -> ST s ()+ go i | rii == 0 = forM_ [0..k-1] $ \j -> writeDataFrameOff bPtr (ki + j) 0+ | otherwise = forM_ [0..k-1] $ \j -> do+ bij <- (rrii*) <$> readDataFrameOff bPtr (ki + j)+ writeDataFrameOff bPtr (ki + j) bij+ forM_ [0..i-1] $ \t -> do+ let rti = scalar (ixOff (m*t + i) r)+ ix = k*t + j+ btj <- readDataFrameOff bPtr ix+ writeDataFrameOff bPtr ix (btj - rti*bij)+ where+ mi = m*i+ ki = k*i+ rii = scalar (ixOff (mi + i) r)+ rrii = recip rii+++{- |+Solve a system of linear equations \( xR = b \),+where \( R \) is an upper-triangular matrix.++DataFrame \( b \) is modified in-place; by the end of the process \( b = x_m \).+The \( (n - m) \) rows of \(R\) are not used.+Pad each dimension of \(x\) with \( (n - m) \) zeros if you want to get the full+solution.+ -}+solveUpperTriangularL ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (PrimBytes t, Fractional t, Eq t, KnownDim m, m <= n)+ => STDataFrame s t (ds +: m) -- ^ Current state of \(b\)+ -- (first @m@ "columns" of x)+ -> DataFrame t '[n,m] -- ^ \(R\)+ -> ST s ()+solveUpperTriangularL bPtr r | Dict <- Dict @(m <= n) = mapM_ go [0..m-1]+ where+ m = fromIntegral $ dimVal' @m :: Int+ k = fromIntegral mkw `quot` m+ CumulDims (mkw:_) = getDataFrameSteps bPtr+ go :: Int -> ST s ()+ go i | rii == 0 = forM_ [0..k-1] $ \j -> writeDataFrameOff bPtr (m*j + i) 0+ | otherwise = forM_ [0..k-1] $ \j -> do+ let mj = m*j+ bji <- (rrii*) <$> readDataFrameOff bPtr (mj + i)+ writeDataFrameOff bPtr (mj + i) bji+ forM_ [i+1..m-1] $ \t -> do+ let rit = scalar (ixOff (mi + t) r)+ bjt <- readDataFrameOff bPtr (mj + t)+ writeDataFrameOff bPtr (mj + t) (bjt - rit*bji)+ where+ mi = m*i+ rii = scalar (ixOff (mi + i) r)+ rrii = recip rii++{- |+Solve a system of linear equations \( Lx = b \),+where \( L \) is a lower-triangular matrix.++DataFrame \( b \) is modified in-place; by the end of the process \( b = x_n \).+The \( (m - n) \) columns of \(L\) are not used.+Pad \(x\) with \( (m - n) \) zero elements if you want to get the full solution.+ -}+solveLowerTriangularR ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (PrimBytes t, Fractional t, Eq t, KnownDim n, KnownDim m, n <= m)+ => DataFrame t '[n,m] -- ^ \(L\)+ -> STDataFrame s t (n :+ ds) -- ^ Current state of \(b\)+ -- (first @n@ elements of x)+ -> ST s ()+solveLowerTriangularR l bPtr | Dict <- Dict @(n <= m) = mapM_ go [0..n-1]+ where+ m = fromIntegral $ dimVal' @m :: Int+ n = fromIntegral $ dimVal' @n :: Int+ k = fromIntegral kw :: Int+ CumulDims (_:kw:_) = getDataFrameSteps bPtr+ go :: Int -> ST s ()+ go i | lii == 0 = forM_ [0..k-1] $ \j -> writeDataFrameOff bPtr (ki + j) 0+ | otherwise = forM_ [0..k-1] $ \j -> do+ bij <- (rlii*) <$> readDataFrameOff bPtr (ki + j)+ writeDataFrameOff bPtr (ki + j) bij+ forM_ [i+1..n-1] $ \t -> do+ let rti = scalar (ixOff (m*t + i) l)+ ix = k*t + j+ btj <- readDataFrameOff bPtr ix+ writeDataFrameOff bPtr ix (btj - rti*bij)+ where+ mi = m*i+ ki = k*i+ lii = scalar (ixOff (mi + i) l)+ rlii = recip lii++{- |+Solve a system of linear equations \( xL = b \)+ or a linear least squares problem \( \min {|| xL - b ||}^2 \),+where \( L \) is a lower-triangular matrix.++DataFrame \( b \) is modified in-place; by the end of the process \( b_n = x \).+The last \( (m - n) \) columns of \(L\) and \(b\) and are not touched.+ -}+solveLowerTriangularL ::+ forall (s :: Type) (t :: Type) (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (PrimBytes t, Fractional t, Eq t, KnownDim n, KnownDim m, n <= m)+ => STDataFrame s t (ds +: m) -- ^ Current state of \(b\)+ -> DataFrame t '[n,m] -- ^ \(L\)+ -> ST s ()+solveLowerTriangularL bPtr l | Dict <- Dict @(n <= m) = mapM_ go [n-1,n-2..0]+ where+ m = fromIntegral $ dimVal' @m :: Int+ n = fromIntegral $ dimVal' @n :: Int+ k = fromIntegral kmw `quot` m+ CumulDims (kmw:_) = getDataFrameSteps bPtr+ go :: Int -> ST s ()+ go i | lii == 0 = forM_ [0..k-1] $ \j -> writeDataFrameOff bPtr (m*j + i) 0+ | otherwise = forM_ [0..k-1] $ \j -> do+ let mj = m*j+ bji <- (rlii*) <$> readDataFrameOff bPtr (mj + i)+ writeDataFrameOff bPtr (mj + i) bji+ forM_ [0..i-1] $ \t -> do+ let lit = scalar (ixOff (mi + t) l)+ ix = m*j + t+ bjt <- readDataFrameOff bPtr ix+ writeDataFrameOff bPtr ix (bjt - lit*bji)+ where+ mi = m*i+ lii = scalar (ixOff (mi + i) l)+ rlii = recip lii
+ src/Numeric/Subroutine/Sort.hs view
@@ -0,0 +1,250 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+module Numeric.Subroutine.Sort+ ( SortBy (..), sortBy, sort+ , SortableDataFrame+ ) where+++import Control.Monad+import Control.Monad.ST+import Control.Monad.ST.Unsafe+import Data.Kind+import Data.Type.Lits+import Numeric.DataFrame.Internal.PrimArray+import Numeric.DataFrame.ST+import Numeric.DataFrame.Type+import Numeric.Dimensions+import Unsafe.Coerce++-- | Sort a @DataFrame@ along the first dimension.+--+-- Note: the elements (which are of type @DataFrame t ns@) are compared+-- lexicographically.+sort :: forall (t :: Type) n ns+ . ( SortableDataFrame t (n ': ns), Ord t, SortBy n)+ => DataFrame t (n ': ns)+ -> DataFrame t (n ': ns)+sort df = case dimKind @(KindOf n) of+ DimKNat -> case uniqueOrCumulDims df of+ Left _ -> df -- all equal, no need for sorting.+ Right steps+ | SomeDims (Dims :: Dims ms) <- fromSteps steps+ , Dict <- (unsafeCoerce (Dict @(ns ~ ns)) :: Dict (ns ~ ms))+ -> sortBy compare df+ | otherwise+ -> error "sort/DimNat/uniqueOrCumulDims -- impossible pattern"+ DimKXNat+ | XFrame (df' :: DataFrame t ms) <- df+ , D :* Dims <- dims @ms+ -> XFrame (sortBy compare df')+ | otherwise+ -> error "sort/DimXNat -- impossible pattern"++-- | Sort a @DataFrame@ along the first dimension using given comparison function.+sortBy :: forall (t :: Type) n ns+ . ( SortableDataFrame t (n ': ns)+ , SortBy n)+ => (DataFrame t ns -> DataFrame t ns -> Ordering)+ -> DataFrame t (n ': ns)+ -> DataFrame t (n ': ns)+sortBy cmp df = case dimKind @(KindOf n) of+ DimKNat -> runST $+ flip (withThawDataFrame (const $ pure df)) df $ \mdf -> do+ sortByInplace+ (\x y -> cmp <$> unsafeFreezeDataFrame x <*> unsafeFreezeDataFrame y)+ mdf+ unsafeFreezeDataFrame mdf+ DimKXNat+ | XFrame dfN <- df+ , D :* Dims <- dims `inSpaceOf` dfN+ -> XFrame (sortBy (\a b -> cmp (XFrame a) (XFrame b)) dfN)+ | otherwise+ -> error "sortBy/DimXNat -- impossible pattern"++-- | The required context for sorting a DataFrame is slightly different+-- for @Nat@ and @XNat@ indexed arrays.+-- This type family abstracts away the difference.+type family SortableDataFrame (t :: Type) (ns :: [k]) :: Constraint where+ SortableDataFrame t ((n ': ns) :: [Nat])+ = (PrimArray t (DataFrame t ns), PrimArray t (DataFrame t (n ': ns)))+ SortableDataFrame t ((n ': ns) :: [XNat])+ = PrimBytes t+++class BoundedDim n => SortBy n where+ -- | Note, "Inplace" here means the input frame is modified.+ -- It does not mean the algorithm does not use extra space (it does use).+ sortByInplace :: PrimBytes t+ => (STDataFrame s t ns -> STDataFrame s t ns -> ST s Ordering)+ -- ^ must not modify state!+ -> STDataFrame s t (n ': ns)+ -> ST s ()+++instance SortBy 0 where+ sortByInplace _ _ = pure ()++instance SortBy 1 where+ sortByInplace _ _ = pure ()++instance SortBy 2 where+ sortByInplace cmp xs = cmp a b >>= \case+ GT -> do+ tmp <- oneMoreDataFrame a+ swapDF tmp a b+ _ -> pure ()+ where+ a = subDataFrameView' (Idx 0 :* U) xs+ b = subDataFrameView' (Idx 1 :* U) xs++instance SortBy 3 where+ sortByInplace cmp xs = join $+ go <$> (unsafeDupableInterleaveST (oneMoreDataFrame a))+ <*> cmp a b <*> cmp b c <*> cmp a c+ where+ a = subDataFrameView' (Idx 0 :* U) xs+ b = subDataFrameView' (Idx 1 :* U) xs+ c = subDataFrameView' (Idx 2 :* U) xs+ go tmp GT LT GT -- b < c < a+ = swap3DF tmp a b c+ go tmp LT GT GT -- c < a < b+ = swap3DF tmp b a c+ go tmp GT bc ac | bc /= GT && ac /= GT+ = swapDF tmp a b+ go tmp ab GT ac | ab /= GT && ac /= GT+ = swapDF tmp b c+ go tmp ab bc GT | ab /= LT && bc /= LT+ = swapDF tmp a c+ go _ _ _ _ = pure ()++instance SortBy 4 where+ sortByInplace cmp xs = do+ tmp <- unsafeDupableInterleaveST (oneMoreDataFrame a)+ cmpSwap tmp a c+ cmpSwap tmp b d+ cmpSwap tmp a b+ cmpSwap tmp c d+ cmpSwap tmp b c+ where+ a = subDataFrameView' (Idx 0 :* U) xs+ b = subDataFrameView' (Idx 1 :* U) xs+ c = subDataFrameView' (Idx 2 :* U) xs+ d = subDataFrameView' (Idx 3 :* U) xs+ cmpSwap tmp x y = cmp x y >>= \case+ GT -> swapDF tmp x y+ _ -> pure ()++instance {-# INCOHERENT #-}+ KnownDim n => SortBy (n :: Nat) where+ sortByInplace cmp (xs :: STDataFrame s t (n ': ns)) = do+ tmp <- oneMoreDataFrame xs+ copyMutableDataFrame' U xs tmp+ mergeSort D tmp xs+ where+ mergeSort :: Dim (d :: Nat)+ -> STDataFrame s t (d ': ns)+ -> STDataFrame s t (d ': ns)+ -> ST s ()+ mergeSort D0 _ _ = pure ()+ mergeSort D1 _ _ = pure ()+ mergeSort (d@D :: Dim d) b a = do+ d2l@D <- pure $ divDim d D2+ Just d2r@D <- pure $ minusDimM d d2l+ d2li@D <- pure $ plusDim d2l D1+ d2ri@D <- pure $ plusDim d2r D1+ Just Dict <- pure $ sameDim (plusDim d D1) (plusDim d2li d2r)+ Just Dict <- pure $ sameDim (plusDim d D1) (plusDim d2ri d2l)+ let leA = subDataFrameView @t @d @(d - Div d 2 + 1) @(Div d 2) @'[]+ (Idx 0 :* U) a+ riA = subDataFrameView @t @d @(Div d 2 + 1) @(d - Div d 2) @'[]+ (Idx (dimVal d2l) :* U) a+ leB = subDataFrameView @t @d @(d - Div d 2 + 1) @(Div d 2) @'[]+ (Idx 0 :* U) b+ riB = subDataFrameView @t @d @(Div d 2 + 1) @(d - Div d 2) @'[]+ (Idx (dimVal d2l) :* U) b+ mergeSort d2l leA leB+ mergeSort d2r riA riB+ merge d2l d2r d leB riB a+ merge :: forall (a :: Nat) (b :: Nat) (ab :: Nat)+ . Dim a -> Dim b -> Dim ab+ -> STDataFrame s t (a ': ns)+ -> STDataFrame s t (b ': ns)+ -> STDataFrame s t (ab ': ns)+ -> ST s ()+ merge da@D db@D dab@D a b ab = foldM_ f (Just (0,0)) [0 .. dimVal dab - 1]+ where+ f Nothing _ = pure Nothing+ f (Just (i,j)) k+ | i >= dimVal da+ , Dx dj@(D :: Dim j) <- someDimVal j+ , D <- plusDim dj D1+ , Just bmj@D <- minusDimM db dj+ , Just bmji@D <- minusDimM (plusDim dab D1) bmj+ , Just Dict <- sameDim (plusDim dab D1) (plusDim bmji bmj)+ , Just Dict <- sameDim (plusDim db D1) (dj `plusDim` D1 `plusDim` bmj)+ = Nothing <$ copyMutableDataFrame @t @ab @(ab + 1 - (b - j))+ @(b - j) (Idx k :* U)+ (subDataFrameView @t @b @(j + 1) @(b - j) (Idx j :* U) b) ab+ | j >= dimVal db+ , Dx di@(D :: Dim i) <- someDimVal i+ , D <- plusDim di D1+ , Just bmi@D <- minusDimM da di+ , Just bmii@D <- minusDimM (plusDim dab D1) bmi+ , Just Dict <- sameDim (plusDim dab D1) (plusDim bmii bmi)+ , Just Dict <- sameDim (plusDim da D1) (di `plusDim` D1 `plusDim` bmi)+ = Nothing <$ copyMutableDataFrame (Idx k :* U)+ (subDataFrameView @t @a @(i + 1) @(a - i) (Idx i :* U) a) ab+ | otherwise+ = cmp (subDataFrameView' (Idx i :* U) a)+ (subDataFrameView' (Idx j :* U) b) >>= \case+ GT -> Just (i, j + 1)+ <$ copyMutableDataFrame' (Idx k :* U)+ (subDataFrameView' (Idx j :* U) b) ab+ _ -> Just (i + 1, j)+ <$ copyMutableDataFrame' (Idx k :* U)+ (subDataFrameView' (Idx i :* U) a) ab+++instance BoundedDim xn => SortBy (xn :: XNat) where+ sortByInplace cmp (XSTFrame xs)+ | D :* _ <- dims `inSpaceOf` xs+ = sortByInplace (\x y -> cmp (castDataFrame x) (castDataFrame y)) xs+ | otherwise = error "sortByInplace: impossible pattern"+++-- | Swap contents of two DataFrames+swapDF :: forall (s :: Type) (t :: Type) (ns :: [Nat])+ . PrimBytes t+ => STDataFrame s t ns -- ^ Temporary buffer+ -> STDataFrame s t ns+ -> STDataFrame s t ns+ -> ST s ()+swapDF tmp a b = do+ copyMutableDataFrame' U a tmp+ copyMutableDataFrame' U b a+ copyMutableDataFrame' U tmp b++-- | Rotate left contents of three DataFrames+swap3DF :: forall (s :: Type) (t :: Type) (ns :: [Nat])+ . PrimBytes t+ => STDataFrame s t ns -- ^ Temporary buffer+ -> STDataFrame s t ns+ -> STDataFrame s t ns+ -> STDataFrame s t ns+ -> ST s ()+swap3DF tmp a b c = do+ copyMutableDataFrame' U a tmp+ copyMutableDataFrame' U b a+ copyMutableDataFrame' U c b+ copyMutableDataFrame' U tmp c
src/Numeric/Vector/Internal.hs view
@@ -8,7 +8,9 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-}@@ -29,6 +31,7 @@ , det2, cross, (×) ) where +import Data.Kind import Numeric.DataFrame.SubSpace import Numeric.DataFrame.Type import Numeric.Scalar.Internal@@ -97,8 +100,8 @@ f _ = scalar b {-# INLINE vec2 #-} unpackV2# v =- (# unScalar (indexOffset# 0# v)- , unScalar (indexOffset# 1# v) #)+ (# unScalar (indexOffset 0 v)+ , unScalar (indexOffset 1 v) #) {-# INLINE unpackV2# #-} instance {-# OVERLAPPABLE #-} SubSpace t '[3] '[] '[3] => Vector3 t where@@ -109,9 +112,9 @@ f _ = scalar c {-# INLINE vec3 #-} unpackV3# v =- (# unScalar (indexOffset# 0# v)- , unScalar (indexOffset# 1# v)- , unScalar (indexOffset# 2# v) #)+ (# unScalar (indexOffset 0 v)+ , unScalar (indexOffset 1 v)+ , unScalar (indexOffset 2 v) #) {-# INLINE unpackV3# #-} instance {-# OVERLAPPABLE #-} SubSpace t '[4] '[] '[4] => Vector4 t where@@ -123,10 +126,10 @@ f _ = scalar d {-# INLINE vec4 #-} unpackV4# v =- (# unScalar (indexOffset# 0# v)- , unScalar (indexOffset# 1# v)- , unScalar (indexOffset# 2# v)- , unScalar (indexOffset# 3# v) #)+ (# unScalar (indexOffset 0 v)+ , unScalar (indexOffset 1 v)+ , unScalar (indexOffset 2 v)+ , unScalar (indexOffset 3 v) #) {-# INLINE unpackV4# #-} @@ -137,7 +140,7 @@ , Num (Vector t n) , SubSpace t '[n] '[] '[n] )- => Vector t n -> Vector t n -> Vector t n+ => Vector t (n :: Nat) -> Vector t n -> Vector t n (.*.) a b = fromScalar . ewfoldl (+) 0 $ a * b infixl 7 .*. @@ -146,7 +149,7 @@ , Num (Vector t n) , SubSpace t '[n] '[] '[n] )- => Vector t n -> Vector t n -> Scalar t+ => Vector t (n :: Nat) -> Vector t n -> Scalar t dot a b = ewfoldl (+) 0 $ a * b -- | Dot product of two vectors@@ -155,41 +158,47 @@ , Num (Vector t n) , SubSpace t '[n] '[] '[n] )- => Vector t n -> Vector t n -> Scalar t+ => Vector t (n :: Nat) -> Vector t n -> Scalar t (·) = dot {-# INLINE (·) #-} -- | Sum of absolute values-normL1 :: ( Num t, SubSpace t '[n] '[] '[n] )- => Vector t n -> Scalar t+normL1 :: ( Num (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n] )+ => Vector t (n :: k) -> DataFrame t ('[] :: [k]) normL1 = ewfoldr (\a -> (abs a +)) 0 -- | hypot function (square root of squares)-normL2 :: ( Floating t , SubSpace t '[n] '[] '[n] )- => Vector t n -> Scalar t+normL2 :: ( Floating (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n] )+ => Vector t (n :: k) -> DataFrame t ('[] :: [k]) normL2 = sqrt . ewfoldr (\a -> (a*a +)) 0 -- | Normalize vector w.r.t. Euclidean metric (L2).-normalized :: ( Floating t , Fractional (Vector t n), SubSpace t '[n] '[] '[n] )+normalized :: forall (k :: Type) (t :: Type) (n :: k)+ . ( Floating (DataFrame t ('[] :: [k]))+ , SubSpace t '[n] '[] '[n] ) => Vector t n -> Vector t n-normalized v = v / n+normalized v = ewmap @t @'[n] @'[] (*rn) v where- n = fromScalar . sqrt $ ewfoldr (\a -> (a*a +)) 0 v+ rn = recip . sqrt $ ewfoldr (\a -> (a*a +)) 0 v -- | Maximum of absolute values-normLPInf :: ( Ord t, Num t , SubSpace t '[n] '[] '[n] )- => Vector t n -> Scalar t+normLPInf :: ( Ord (DataFrame t ('[] :: [k]))+ , Num (DataFrame t ('[] :: [k]))+ , SubSpace t '[n] '[] '[n] )+ => Vector t (n :: k) -> DataFrame t ('[] :: [k]) normLPInf = ewfoldr (max . abs) 0 -- | Minimum of absolute values-normLNInf :: ( Ord t, Num t , SubSpace t '[n] '[] '[n] )- => Vector t n -> Scalar t-normLNInf x = ewfoldr (min . abs) (scalar . abs $ ixOff 0 x) x+normLNInf :: ( Ord (DataFrame t ('[] :: [k]))+ , Num (DataFrame t ('[] :: [k]))+ , SubSpace t '[n] '[] '[n] )+ => Vector t (n :: k) -> DataFrame t ('[] :: [k])+normLNInf x = ewfoldr (min . abs) (abs $ indexOffset 0 x) x -- | Norm in Lp space-normLP :: ( Floating t , SubSpace t '[n] '[] '[n] )- => Int -> Vector t n -> Scalar t+normLP :: (Floating (DataFrame t ('[] :: [k])), SubSpace t '[n] '[] '[n])+ => Int -> Vector t (n :: k) -> DataFrame t ('[] :: [k]) normLP i' = (**ri) . ewfoldr (\a -> (a**i +)) 0 where i = fromIntegral i'@@ -202,23 +211,15 @@ -- | Take a determinant of a matrix composed from two 2D vectors. -- Like a cross product in 2D.-det2 :: ( Num t, SubSpace t '[2] '[] '[2] )+det2 :: ( Num t, PrimBytes t ) => Vector t 2 -> Vector t 2 -> Scalar t-det2 a b = (a ! 0 :* U) * (b ! 1 :* U)- - (a ! 1 :* U) * (b ! 0 :* U)+det2 (DF2 a0 a1) (DF2 b0 b1) = a0*b1 - b0*a1 -- | Cross product-cross :: ( Num t, SubSpace t '[3] '[] '[3] )+cross :: ( Num t, PrimBytes t ) => Vector t 3 -> Vector t 3 -> Vector t 3-cross a b = vec3 ( unScalar- $ (a ! 1 :* U) * (b ! 2 :* U)- - (a ! 2 :* U) * (b ! 1 :* U) )- ( unScalar- $ (a ! 2 :* U) * (b ! 0 :* U)- - (a ! 0 :* U) * (b ! 2 :* U) )- ( unScalar- $ (a ! 0 :* U) * (b ! 1 :* U)- - (a ! 1 :* U) * (b ! 0 :* U) )+cross (DF3 a0 a1 a2) (DF3 b0 b1 b2)+ = DF3 (a1*b2 - a2*b1) (a2*b0 - a0*b2) (a0*b1 - a1*b0) -- | Cross product for two vectors in 3D
+ test/Numeric/Arbitraries.hs view
@@ -0,0 +1,614 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PartialTypeSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | Provide instance of Arbitrary for all DataFrame types.+-- Also, this module is an example of fancy type inference and DataFrame+-- traversals with monadic actions.+module Numeric.Arbitraries where++import Test.QuickCheck++#if !(MIN_VERSION_base(4,13,0))+import Control.Monad.Fail+#endif+import Data.Int+import Data.Kind (Type)+import Data.List (inits, tails)+import Data.Semigroup hiding (All)+import Data.Word+import Language.Haskell.TH (ExpQ, Q, TypeQ)+import qualified Language.Haskell.TH as TH+import Numeric.DataFrame+import Numeric.Dimensions+import Numeric.Quaternion+import qualified Numeric.Tuple.Lazy as LT+import qualified Numeric.Tuple.Strict as ST++-- | Maximum number of elements in SomeDims lists+maxDims :: Word+maxDims = 5++-- | Some tests are rather slow when we have too many elements+maxTotalDim :: Word+maxTotalDim = 400++-- | Maximum value of @Dim (XN _)@+maxDimSize :: Word+maxDimSize = 30++-- | Odds of using `fromScalar` constructor instead of element-by-element.+-- Interpret as "one to fromScalarChanceFactor"+fromScalarChanceFactor :: Int+fromScalarChanceFactor = 5++concatDimsQ :: TypeQ -> TypeQ -> TypeQ+concatDimsQ as bs = [t| Concat $(as) $(bs) |]++aFewPropVariants :: ExpQ -> ExpQ+aFewPropVariants = TH.listE . replicate 100++someElemTypeFQ :: Q TypeQ+someElemTypeFQ = TH.runIO . generate $ elements elemTypesF++someElemTypeIQ :: Q TypeQ+someElemTypeIQ = TH.runIO . generate $ elements elemTypesI++someElemTypeQ :: Q TypeQ+someElemTypeQ = TH.runIO . generate . elements $ elemTypesI ++ elemTypesF++elemTypesF :: [TypeQ]+elemTypesF = [ [t|Float|], [t|Double|] ]++elemTypesI :: [TypeQ]+elemTypesI =+ [ [t|Int|], [t|Int8|], [t|Int16|], [t|Int32|], [t|Int64|]+ , [t|Word|], [t|Word8|], [t|Word16|], [t|Word32|], [t|Int64|]+ ]+++-- | Generate type (n :: Nat) at compile time+someDimNQ :: Q TypeQ+someDimNQ = TH.litT . TH.numTyLit . toInteger+ <$> TH.runIO (generate (chooseDim 1))++-- | Generate type (n :: XNat) at compile time+someDimXQ :: Q TypeQ+someDimXQ = fmap pure $ do+ d <- TH.litT . TH.numTyLit . toInteger+ <$> TH.runIO (generate (chooseDim 1))+ TH.runIO (generate arbitrary) >>=+ \isXN -> if isXN then [t|XN $(d)|] else [t|N $(d)|]++-- | Generate type (N n) at compile time+someDimXfixedQ :: Q TypeQ+someDimXfixedQ = fmap pure $ do+ d <- TH.litT . TH.numTyLit . toInteger+ <$> TH.runIO (generate (chooseDim 1))+ [t| N $(d) |]++-- | Generate type (ns :: [k]) at compile time+someDimsQ :: Q TypeQ+someDimsQ = do+ isXNat <- TH.runIO (generate arbitrary)+ if isXNat then someDimsXQ+ else someDimsNQ++-- | Generate type (ns :: [Nat]) at compile time+someDimsNQ :: Q TypeQ+someDimsNQ = fmap pure $ TH.runIO (removeDims <$> generate arbitrary)+ >>= \(SomeDims ds) -> wordsToDimsNQ (listDims ds)++-- | Generate type (Map 'N (ns :: [Nat])) at compile time+someDimsXQfixed :: Q TypeQ+someDimsXQfixed = fmap pure $ TH.runIO (removeDims <$> generate arbitrary)+ >>= \(SomeDims ds) -> wordsToDimsXQfixed (listDims ds)++-- | Generate type (Map 'N (ns :: [Nat])) at compile time+someDimsXQ :: Q TypeQ+someDimsXQ = fmap pure $ TH.runIO (removeDims <$> generate arbitrary)+ >>= \(SomeDims ds) -> wordsToDimsXQ(listDims ds)++-- | Generate type (ns :: [k]) at compile time+someLenAsBsQ :: Int -> Q (TypeQ, TypeQ)+someLenAsBsQ len = do+ isXNat <- TH.runIO (generate arbitrary)+ if isXNat then someLenAsBsXQ len+ else someLenAsBsNQ len++-- | Generate type (ns :: [k]) at compile time+someAsBsQ :: Q (TypeQ, TypeQ)+someAsBsQ = do+ isXNat <- TH.runIO (generate arbitrary)+ if isXNat then someAsBsXQ+ else someAsBsNQ++-- | Generate type (ns :: [k]) at compile time+someAsBsQfixed :: Q (TypeQ, TypeQ)+someAsBsQfixed = do+ isXNat <- TH.runIO (generate arbitrary)+ if isXNat then someAsBsXQfixed+ else someAsBsNQ++-- | Generate type (as :: [Nat], bs :: [Nat]) at compile time,+-- given a fixed length of the first dims (as).+someLenAsBsNQ :: Int -> Q (TypeQ, TypeQ)+someLenAsBsNQ len = do+ (asw, bsw) <- TH.runIO (generate (genConcatDims len))+ as <- wordsToDimsNQ asw+ bs <- wordsToDimsNQ bsw+ return (pure as, pure bs)++-- | Generate type (as :: [XNat], Map 'N bs :: [XNat]) at compile time,+-- given a fixed length of the first dims (as).+someLenAsBsXQ :: Int -> Q (TypeQ, TypeQ)+someLenAsBsXQ len = do+ (asw, bsw) <- TH.runIO (generate (genConcatDims len))+ as <- wordsToDimsXQ asw+ bs <- wordsToDimsXQfixed bsw+ return (pure as, pure bs)++-- | Generate type (Map 'N as :: [XNat], Map 'N bs :: [XNat]) at compile time,+-- given a fixed length of the first dims (as).+someLenAsBsXQfixed :: Int -> Q (TypeQ, TypeQ)+someLenAsBsXQfixed len = do+ (asw, bsw) <- TH.runIO (generate (genConcatDims len))+ as <- wordsToDimsXQfixed asw+ bs <- wordsToDimsXQfixed bsw+ return (pure as, pure bs)++-- | Generate type (as :: [Nat], bs :: [Nat]) at compile time.+someAsBsNQ :: Q (TypeQ, TypeQ)+someAsBsNQ = TH.runIO (generate (choose (0, fromIntegral maxDims - 1)))+ >>= someLenAsBsNQ++-- | Generate type (as :: [XNat], Map 'N bs :: [XNat]) at compile time.+someAsBsXQ :: Q (TypeQ, TypeQ)+someAsBsXQ = TH.runIO (generate (choose (0, fromIntegral maxDims - 1)))+ >>= someLenAsBsXQ++-- | Generate type (Map 'N as :: [XNat], Map 'N bs :: [XNat]) at compile time.+someAsBsXQfixed:: Q (TypeQ, TypeQ)+someAsBsXQfixed = TH.runIO (generate (choose (0, fromIntegral maxDims - 1)))+ >>= someLenAsBsXQfixed+++wordsToDimsNQ :: [Word] -> TypeQ+wordsToDimsNQ = (\ns -> [t| ($(ns) :: [Nat]) |]) . foldr f TH.promotedNilT+ where+ f :: Word -> TypeQ -> TypeQ+ f w l = let d = TH.litT . TH.numTyLit $ toInteger w+ in TH.promotedConsT `TH.appT` d `TH.appT` l++wordsToDimsXQfixed :: [Word] -> TypeQ+wordsToDimsXQfixed = (\ns -> [t| ($(ns) :: [XNat]) |]) . foldr f TH.promotedNilT+ where+ f :: Word -> TypeQ -> TypeQ+ f w l = let d = TH.litT . TH.numTyLit $ toInteger w+ in TH.promotedConsT `TH.appT` [t| N $(d) |] `TH.appT` l++wordsToDimsXQ :: [Word] -> TypeQ+wordsToDimsXQ = (\ns -> [t| ($(ns) :: [XNat]) |]) . foldr f TH.promotedNilT+ where+ f :: Word -> TypeQ -> TypeQ+ f w l = let d = TH.litT . TH.numTyLit $ toInteger w+ xd = TH.runIO (generate arbitrary) >>=+ \isXN -> if isXN then [t|XN $(d)|] else [t|N $(d)|]+ in TH.promotedConsT `TH.appT` xd `TH.appT` l++-- | Generate two lists of Dims as Words, such that the combined list+-- is under the maxTotalDim limit, but the first list has exactly the given+-- number of dims.+genConcatDims :: Int -> Gen ([Word], [Word])+genConcatDims len+ = arbitrary >>= (\(SomeDims xs) -> checkLen (listDims xs)) . removeDims+ where+ checkLen :: [Word] -> Gen ([Word], [Word])+ checkLen xs =+ let td = product xs+ (as, bs) = splitAt len xs+ l = length as+ in if l == len then pure (as, bs)+ else (,) <$> addDims (len - l) (maxTotalDim `quot` td) as+ <*> pure bs+ addDims :: Int -> Word -> [Word] -> Gen [Word]+ addDims 0 _ xs = pure xs+ addDims n lim xs = do+ x <- choose (1, max 1 (min lim maxDimSize))+ addDims (n-1) (lim `quot` x) (x : xs)+++-- | Remove dims from a dim list until its totalDim is less than maxTotalDim+removeDims :: SomeDims -> SomeDims+removeDims = removeDimsAbove maxTotalDim++-- | Remove dims from a dim list until its totalDim is less than a given value+removeDimsAbove :: Word -> SomeDims -> SomeDims+removeDimsAbove _ (SomeDims U) = SomeDims U+removeDimsAbove z (SomeDims nns@(_ :* ns))+ | totalDim nns > z = removeDimsAbove z (SomeDims ns)+ | otherwise = SomeDims nns++-- | Reduce individual XN-dims untils its totalDim is less than maxTotalDim+reduceDims :: (All KnownDimType xns, BoundedDims xns)+ => Dims (xns :: [XNat]) -> Dims (xns :: [XNat])+reduceDims = reduceDims' 1++reduceDims' :: (All KnownDimType xns, BoundedDims xns)+ => Word -> Dims (xns :: [XNat]) -> Dims (xns :: [XNat])+reduceDims' _ U = U+reduceDims' l nns@(n :* ns)+ | totalDim nns * l <= maxTotalDim = nns+ | otherwise = case n of+ Dn d -> n :* reduceDims' (l * dimVal d) ns+ (Dx d :: Dim xn) -> case compareDim (dimBound @xn) D2 of+ SLT -> Dx D2 :* reduceDims' (l * 2) ns+ SEQ -> Dx D2 :* reduceDims' (l * 2) ns+ SGT -> n :* reduceDims' (l * dimVal d) ns++-- | Most of the time, we assume the error is proportional to the maginutude of+-- the biggest element.+maxElem :: (SubSpace t ds '[] ds, Ord t, Num t)+ => DataFrame t (ds :: [Nat]) -> Scalar t+maxElem = ewfoldr' (max . abs) 0++rotateList :: [a] -> [[a]]+rotateList xs = init (zipWith (++) (tails xs) (inits xs))++class (RealFloatExtras t, Show t) => Approx t x | x -> t where+ -- | Check if two values are approximately equal+ approxEq :: t -- ^ Extra multiplier constant+ -> x -> x -> Property++-- | Check if two values are approximately equal.+(=~=) :: Approx t x => x -> x -> Property+(=~=) = approxEq 1+infix 4 =~=++instance Approx Double Double where+ approxEq c a b = counterexample+ (unlines+ [ " Double approxEq failed:"+ , " values: " ++ show (a, b)+ , " error: " ++ show err+ , " tolerance: " ++ show tol+ ]+ ) $ err <= tol+ where+ err = abs (a - b)+ mel = abs a `max` abs b `max` 1+ tol = M_EPS*mel*c++instance Approx Float Float where+ approxEq c a b = counterexample+ (unlines+ [ " Double approxEq failed:"+ , " values: " ++ show (a, b)+ , " error: " ++ show err+ , " tolerance: " ++ show tol+ ]+ ) $ err <= tol+ where+ err = abs (a - b)+ mel = abs a `max` abs b `max` 1+ tol = M_EPS*mel*c++instance MonadFail Gen where fail = error++instance (Quaternion t, Arbitrary t, Num t) => Arbitrary (Quater t) where+ arbitrary = sequence+ [ Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ , Quater <$> arbitrary <*> pure 0 <*> pure 0 <*> pure 0+ , Quater <$> pure 0 <*> arbitrary <*> pure 0 <*> pure 0+ , Quater <$> pure 0 <*> pure 0 <*> arbitrary <*> pure 0+ , Quater <$> pure 0 <*> pure 0 <*> pure 0 <*> arbitrary+ , Quater <$> arbitrary <*> arbitrary <*> pure 0 <*> pure 0+ , Quater <$> arbitrary <*> pure 0 <*> arbitrary <*> pure 0+ , Quater <$> arbitrary <*> pure 0 <*> pure 0 <*> arbitrary+ , Quater <$> pure 0 <*> arbitrary <*> arbitrary <*> pure 0+ , Quater <$> pure 0 <*> arbitrary <*> pure 0 <*> arbitrary+ , Quater <$> pure 0 <*> pure 0 <*> arbitrary <*> arbitrary+ , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> pure 0+ , Quater <$> arbitrary <*> arbitrary <*> pure 0 <*> arbitrary+ , Quater <$> arbitrary <*> pure 0 <*> arbitrary <*> arbitrary+ , Quater <$> pure 0 <*> arbitrary <*> arbitrary <*> arbitrary+ ] >>= elements++ shrink (Quater x y z t)+ -- shrink either real or the whole imaginary part+ = ($) <$> zipWith3 Quater (shrink x) (shrink y) (shrink z) <*> shrink t++instance (RealFloatExtras t, Show t, Quaternion t) => Approx t (Quater t) where+ approxEq c a b = counterexample+ (unlines+ [ " Quaternion approxEq failed:"+ , " max elem: " ++ show mel+ , " error: " ++ show err+ , " tolerance: " ++ show tol+ ]+ ) $ err <= tol+ where+ v1 = toVec4 a+ v2 = toVec4 b+ err = unScalar $ maxElem (v1 - v2)+ mel = unScalar $ 1 `max` maxElem v1 `max` maxElem v2+ tol = M_EPS*mel*c++instance (Arbitrary t, PrimBytes t, Num t, Ord t, Dimensions ds)+ => Arbitrary (DataFrame t (ds :: [Nat])) where+ arbitrary = do+ full <- (1 < ) <$> choose (1, fromScalarChanceFactor)+ zeroChance <- choose (0, 8)+ if full -- I want to check fromScalar code path sometimes+ then arbitrary >>= elementWise @_ @ds @'[] (f zeroChance) . ewgen . scalar+ else fromScalar . scalar <$> arbitrary+ where+ f :: (Arbitrary a, Num a) => Double -> Scalar a -> Gen (Scalar a)+ f zeroChance _ = do+ dice <- (zeroChance >=) <$> choose (0, 10)+ if dice+ then return 0+ else scalar <$> arbitrary+ shrink df+ | Just (Max ma) <- ewfoldMap @t @ds @'[] @ds+ ((\x -> if x == 0 then Nothing else Just (Max x)) . abs) df+ = [ ewmap (\x -> if abs x == ma then 0 else x) df+ , ewmap (scalar . withAbs . unScalar) df+ ]+ | otherwise = []+ where+ withAbs :: t -> t+ withAbs x+ | abs x <= 1 = 0+ | otherwise = signum x * closest2 (abs x) 1+ closest2 :: t -> t -> t+ closest2 x b = if x <= b * 2 then b else closest2 x (b*2)++instance (RealFloatExtras t, Show t, Dimensions ds)+ => Approx (DataFrame t ('[] :: [Nat])) (DataFrame t (ds :: [Nat])) where+ approxEq (S t) = approxEqDF t++instance (RealFloatExtras t, Show t)+ => Approx (DataFrame t ('[] :: [XNat])) (DataFrame t (ds :: [XNat])) where+ approxEq (XFrame (S t)) (XFrame a) (XFrame b) = case sameDims da db of+ Just Dict -> approxEqDF t a b+ Nothing -> counterexample+ (unlines+ [ " DataFrame approxEq failed [XNat] due to different dims:"+ , " dims a: " ++ show da+ , " dims b: " ++ show db+ ]+ ) False+ where+ da = dims `inSpaceOf` a+ db = dims `inSpaceOf` b++approxEqDF ::+ forall t (ds :: [Nat])+ . ( Dimensions ds+ , Show t, RealFloatExtras t+ , KnownBackend t ds)+ => t -> DataFrame t ds -> DataFrame t (ds :: [Nat]) -> Property+approxEqDF c a b+ | U <- dims @ds = counterexample+ (unlines+ [ " Scalar approxEq failed:"+ , " values: " ++ show (unScalar a, unScalar b)+ , " error: " ++ show err+ , " tolerance: " ++ show tol+ ]+ ) $ err <= tol+ | otherwise = counterexample+ (unlines+ [ " DataFrame approxEq failed:"+ , " max elem: " ++ show mel+ , " error: " ++ show err+ , " tolerance: " ++ show tol+ ]+ ) $ err <= tol+ where+ err = unScalar $ maxElem (a - b)+ mel = unScalar $ maxElem a `max` maxElem b `max` 1+ tol = M_EPS*mel*c++instance ( All Arbitrary ts, All PrimBytes ts, All Num ts, All Ord ts+ , RepresentableList ts, Dimensions ds)+ => Arbitrary (DataFrame ts (ds :: [Nat])) where+ -- We create arbitrary MultiFrame by combining several SingleFrames.+ -- SingleFrames are "variables" or "columns" of a MultiFrame that are+ -- independent byte arrays bounded by a common dimensions type signature.+ arbitrary = -- Use RepresentableList to find out how many columns are there.+ case tList @ts of+ -- Zero columns, empty MultiFrame+ U -> return Z+ -- Cons-like construction.+ -- Note, pattern matching TypeList brings RepresentableList evidence+ -- for Tail ts.+ _ :* (TypeList :: TypeList ts') -> do+ at <- arbitrary+ ats' <- arbitrary @(DataFrame ts' ds)+ return (at :*: ats')+ -- MultiFrame is a newtype wrapper on a TypedList.+ -- Thus, we can always recover RepresentableList ts by using function @types@+ shrink (at :*: ats@(MultiFrame ats'))+ | TypeList <- types ats'+ = (:*:) <$> shrink at <*> shrink ats+ shrink _ = []++-- | Generate a random word not smaller than the given number, and not bigger+-- than maxDimSize. Also tend to generate smaller values more often to have+-- a chance to pack more dims within maxTotalDim.+chooseDim :: Word -> Gen Word+chooseDim lowLim = f <$> choose (0, maxDimSize)+ where+ l = realToFrac lowLim :: Double+ f :: Word -> Word+ f w = let x = realToFrac w :: Double+ n = realToFrac maxDimSize :: Double+ in round $ l + max 0 (n - l) * (x / n) ** 3++instance KnownDim a => Arbitrary (Dim (N a)) where+ arbitrary = return $ Dn (dim @a)+ shrink _ = []++instance KnownDim m => Arbitrary (Dim (XN m)) where+ arbitrary = do+ dimN <- chooseDim $ dimVal' @m+ case constrainDim @(XN m) (someDimVal dimN) of+ Nothing -> error "impossible argument"+ Just d -> return d+ shrink _ = []++instance Arbitrary SomeDims where+ arbitrary = do+ dimN <- choose (0, maxDims) :: Gen Word+ wdims <- mapM (\_ -> chooseDim 1) [1..dimN]+ return $ someDimsVal wdims+ shrink (SomeDims U) = []+ shrink (SomeDims (_ :* ds)) = [SomeDims ds]++instance Arbitrary (Dims '[]) where+ arbitrary = return U+ shrink _ = []++instance (KnownDim n, Arbitrary (Dims xs)) => Arbitrary (Dims (N n ': xs)) where+ arbitrary = (:*) <$> arbitrary <*> arbitrary+ shrink _ = []++instance (KnownDim m, Arbitrary (Dims xs)) => Arbitrary (Dims (XN m ': xs)) where+ arbitrary = (:*) <$> arbitrary <*> arbitrary+ shrink _ = []++instance (Arbitrary t, PrimBytes t, Num t, Ord t)+ => Arbitrary (SomeDataFrame t) where+ arbitrary = do+ SomeDims (Dims :: Dims ds) <- removeDims <$> arbitrary+ SomeDataFrame <$> arbitrary @(DataFrame t ds)+ shrink (SomeDataFrame df) = SomeDataFrame <$> shrink df++-- All same as above, just change constraints a bit+instance ( All Arbitrary ts, All PrimBytes ts, All Num ts, All Ord ts+ , RepresentableList ts)+ => Arbitrary (SomeDataFrame ts) where+ arbitrary = do+ SomeDims ds <- removeDims <$> arbitrary+ case ds of+ (Dims :: Dims ds) -> case inferKnownBackend @ts @ds of+ Dict -> SomeDataFrame <$> arbitrary @(DataFrame ts ds)+ shrink (SomeDataFrame df) = SomeDataFrame <$> shrink df+++instance ( Arbitrary t, PrimBytes t, Num t, Ord t+ , Arbitrary (Dims xs), All KnownDimType xs, BoundedDims xs)+ => Arbitrary (DataFrame t (xs :: [XNat])) where+ arbitrary = do+ XDims (Dims :: Dims ds) <- reduceDims <$> arbitrary @(Dims xs)+ XFrame <$> arbitrary @(DataFrame t ds)+ shrink (XFrame df) = XFrame <$> shrink df++instance ( All Arbitrary ts, All PrimBytes ts, All Num ts, All Ord ts+ , RepresentableList ts+ , Arbitrary (Dims xs), All KnownDimType xs, BoundedDims xs)+ => Arbitrary (DataFrame ts (xs :: [XNat])) where+ arbitrary = do+ ds <- reduceDims <$> arbitrary @(Dims xs)+ case ds of+ XDims (Dims :: Dims ds) -> case inferKnownBackend @ts @ds of+ Dict -> XFrame <$> arbitrary @(DataFrame ts ds)+ shrink (XFrame df) = XFrame <$> shrink df+++instance KnownDim n => Arbitrary (Idx (n :: Nat)) where+ arbitrary = Idx <$> choose (minBound, dimVal' @n - 1)++instance KnownDim n => Arbitrary (Idx (N n)) where+ arbitrary = Idx <$> choose (minBound, dimVal' @n - 1)++instance KnownDim n => Arbitrary (Idx (XN n)) where+ arbitrary = Idx <$> choose (minBound, dimVal' @n - 1)++instance Dimensions ns => Arbitrary (Idxs (ns :: [Nat])) where+ arbitrary = go (dims @ns)+ where+ go :: forall (bs :: [Nat]) . Dims bs -> Gen (Idxs bs)+ go U = pure U+ go (D :* bs) = (:*) <$> arbitrary <*> go bs++instance (BoundedDims ns, All KnownDimType ns) => Arbitrary (Idxs (ns :: [XNat])) where+ arbitrary = go (minimalDims @ns)+ where+ go :: forall (bs :: [XNat])+ . (BoundedDims bs, All KnownDimType bs) => Dims bs -> Gen (Idxs bs)+ go U = pure U+ go (Dn D :* bs) = (:*) <$> arbitrary <*> go bs+ go (Dx D :* bs) = (:*) <$> arbitrary <*> go bs+++instance (RepresentableList xs, All Arbitrary xs) => Arbitrary (ST.Tuple xs) where+ arbitrary = go (tList @xs)+ where+ go :: forall (bs :: [Type])+ . All Arbitrary bs+ => TypeList bs -> Gen (ST.Tuple bs)+ go U = pure U+ go (_ :* bs) = (ST.:$) <$> arbitrary <*> go bs++instance (RepresentableList xs, All Arbitrary xs) => Arbitrary (LT.Tuple xs) where+ arbitrary = go (tList @xs)+ where+ go :: forall (bs :: [Type])+ . All Arbitrary bs+ => TypeList bs -> Gen (LT.Tuple bs)+ go U = pure U+ go (_ :* bs) = (LT.:$) <$> arbitrary <*> go bs++data AnyMatrix+data NonSingular++data SomeSquareMatrix prop t+ = forall (n :: Nat)+ . (KnownDim n, KnownBackend t '[n], KnownBackend t '[n, n])+ => SSM (DataFrame t '[n,n])++instance (Show t, PrimBytes t) => Show (SomeSquareMatrix prop t) where+ show (SSM df) = show df++instance (Arbitrary t, PrimBytes t, Num t, Ord t)+ => Arbitrary (SomeSquareMatrix AnyMatrix t) where+ arbitrary = do+ Dx (D :: Dim n) <- arbitrary @(Dim (XN 2))+ SSM <$> arbitrary @(DataFrame t '[n,n])+ shrink (SSM df)= SSM <$> shrink df++instance (Arbitrary t, PrimBytes t, Num t, Ord t)+ => Arbitrary (SomeSquareMatrix NonSingular t) where+ arbitrary = do+ SSM (someMat :: DataFrame t '[n, n]) <- arbitrary @(SomeSquareMatrix AnyMatrix t)+ -- https://en.wikipedia.org/wiki/Diagonally_dominant_matrix+ return . SSM $+ iwmap @t @'[n] @'[n] @'[n,n]+ @t @'[n] @'[n,n]+ ( \i v ->+ let s = ewfoldl (\a -> (a +) . abs) 1 v+ in update i s v+ ) someMat+ shrink _ = []
− test/Numeric/DataFrame/Arbitraries.hs
@@ -1,274 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE PartialTypeSignatures #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | Provide instance of Arbitrary for all DataFrame types.--- Also, this module is an example of fancy type inference and DataFrame--- traversals with monadic actions.-module Numeric.DataFrame.Arbitraries where--import Test.QuickCheck--import Data.Kind (Type)-import Data.Semigroup hiding (All)-import Numeric.DataFrame-import Numeric.Dimensions-import Numeric.Quaternion-import qualified Numeric.Tuple.Lazy as LT-import qualified Numeric.Tuple.Strict as ST---maxDims :: Word-maxDims = 5--maxDimSize :: Word-maxDimSize = 7--fromScalarChanceFactor :: Int-fromScalarChanceFactor = 5---instance (Quaternion t, Arbitrary t, Num t) => Arbitrary (Quater t) where- arbitrary = sequence- [ Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary- , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary- , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary- , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary- , Quater <$> arbitrary <*> pure 0 <*> pure 0 <*> pure 0- , Quater <$> pure 0 <*> arbitrary <*> pure 0 <*> pure 0- , Quater <$> pure 0 <*> pure 0 <*> arbitrary <*> pure 0- , Quater <$> pure 0 <*> pure 0 <*> pure 0 <*> arbitrary- , Quater <$> arbitrary <*> arbitrary <*> pure 0 <*> pure 0- , Quater <$> arbitrary <*> pure 0 <*> arbitrary <*> pure 0- , Quater <$> arbitrary <*> pure 0 <*> pure 0 <*> arbitrary- , Quater <$> pure 0 <*> arbitrary <*> arbitrary <*> pure 0- , Quater <$> pure 0 <*> arbitrary <*> pure 0 <*> arbitrary- , Quater <$> pure 0 <*> pure 0 <*> arbitrary <*> arbitrary- , Quater <$> arbitrary <*> arbitrary <*> arbitrary <*> pure 0- , Quater <$> arbitrary <*> arbitrary <*> pure 0 <*> arbitrary- , Quater <$> arbitrary <*> pure 0 <*> arbitrary <*> arbitrary- , Quater <$> pure 0 <*> arbitrary <*> arbitrary <*> arbitrary- ] >>= elements-- shrink (Quater x y z t)- -- shrink either real or the whole imaginary part- = ($) <$> zipWith3 Quater (shrink x) (shrink y) (shrink z) <*> shrink t----instance (Arbitrary t, PrimBytes t, Num t, Ord t, Dimensions ds)- => Arbitrary (DataFrame t (ds :: [Nat])) where- arbitrary- | -- First, we need to find out exact array implementation to use- -- inside this DataFrame.- -- We need to do that whenever exact value of ds is not known- Dict <- inferKnownBackend @t @ds- -- After that, GHC can infer all necessary fancy things like SubSpace- -- to do complex operations on sub-dimensions of a DataFrame.- --- -- Note, we could put SubSpace into constraints of this instance as well.- -- That would render the above lines unnecessary, but would make- -- inference more difficult later.- = do- full <- (1 < ) <$> choose (1, fromScalarChanceFactor)- if full -- I want to check fromScalar code path sometimes- then arbitrary >>= elementWise @_ @ds @'[] f . ewgen . scalar- else fromScalar . scalar <$> arbitrary- where- f :: Arbitrary a => Scalar a -> Gen (Scalar a)- f _ = scalar <$> arbitrary- shrink df- | Dict <- inferKnownBackend @t @ds- , mma <- ewfoldMap @t @ds @'[] @ds- ((\x -> if x == 0 then Nothing else Just (Max x)) . abs) df- = case mma of- Nothing- -> [] -- all-zero is the most primitive DF possible- Just (Max ma)- -> [ ewmap (\x -> if abs x == ma then 0 else x) df- , ewmap (scalar . withAbs . unScalar) df- ]- where- withAbs :: t -> t- withAbs x- | abs x <= 1 = 0- | otherwise = signum x * closest2 (abs x) 1- closest2 :: t -> t -> t- closest2 x b = if x <= b * 2 then b else closest2 x (b*2)--instance ( All Arbitrary ts, All PrimBytes ts, All Num ts, All Ord ts- , RepresentableList ts, Dimensions ds)- => Arbitrary (DataFrame ts (ds :: [Nat])) where- -- We create arbitrary MultiFrame by combining several SingleFrames.- -- SingleFrames are "variables" or "columns" of a MultiFrame that are- -- independent byte arrays bounded by a common dimensions type signature.- arbitrary = -- Use RepresentableList to find out how many columns are there.- case tList @_ @ts of- -- Zero columns, empty MultiFrame- U -> return Z- -- Cons-like construction.- -- Note, pattern matching TypeList brings RepresentableList evidence- -- for Tail ts.- _ :* (TypeList :: TypeList ts') -> do- at <- arbitrary- ats' <- arbitrary @(DataFrame ts' ds)- return (at :*: ats')- -- MultiFrame is a newtype wrapper on a TypedList.- -- Thus, we can always recover RepresentableList ts by using function @types@- shrink (at :*: ats@(MultiFrame ats'))- | TypeList <- types ats'- = (:*:) <$> shrink at <*> shrink ats- shrink _ = []---instance KnownDim a => Arbitrary (Dim (N a)) where- arbitrary = return $ Dn (dim @a)- shrink _ = []--instance KnownDim m => Arbitrary (Dim (XN m)) where- arbitrary = do- dimN <- choose (dimVal' @m, maxDims)- case constrainDim @XNat @(XN m) (someDimVal dimN) of- Nothing -> error "impossible argument"- Just d -> return d- shrink _ = []--instance Arbitrary SomeDims where- arbitrary = do- dimN <- choose (0, maxDims) :: Gen Word- wdims <- mapM (\_ -> choose (2, maxDimSize) :: Gen Word) [1..dimN]- return $ someDimsVal wdims- shrink (SomeDims U) = []- shrink (SomeDims (_ :* ds)) = [SomeDims ds]--instance Arbitrary (Dims '[]) where- arbitrary = return U- shrink _ = []--instance (KnownDim n, Arbitrary (Dims xs)) => Arbitrary (Dims (N n ': xs)) where- arbitrary = (:*) <$> arbitrary <*> arbitrary- shrink _ = []--instance (KnownDim m, Arbitrary (Dims xs)) => Arbitrary (Dims (XN m ': xs)) where- arbitrary = (:*) <$> arbitrary <*> arbitrary- shrink _ = []--instance (Arbitrary t, PrimBytes t, Num t, Ord t)- => Arbitrary (SomeDataFrame t) where- arbitrary = do- -- Generate random dimension list- SomeDims ds <- arbitrary- -- and pattern-match against it with Dims pattern.- -- This gives Dimensions ds evidence immediately.- case ds of- -- We also need to figure out an array implementation...- (Dims :: Dims ds) -> case inferKnownBackend @t @ds of- -- ... and generating a random DataFrame becomes a one-liner- Dict -> SomeDataFrame <$> arbitrary @(DataFrame t ds)- shrink (SomeDataFrame df) = SomeDataFrame <$> shrink df---- All same as above, just change constraints a bit-instance ( All Arbitrary ts, All PrimBytes ts, All Num ts, All Ord ts- , RepresentableList ts)- => Arbitrary (SomeDataFrame ts) where- arbitrary = do- SomeDims ds <- arbitrary- case ds of- (Dims :: Dims ds) -> case inferKnownBackend @ts @ds of- Dict -> SomeDataFrame <$> arbitrary @(DataFrame ts ds)- shrink (SomeDataFrame df) = SomeDataFrame <$> shrink df--instance ( Arbitrary t, PrimBytes t, Num t, Ord t- , Arbitrary (Dims xs), All KnownXNatType xs)- => Arbitrary (DataFrame t (xs :: [XNat])) where- arbitrary = do- ds <- arbitrary @(Dims xs)- case ds of- XDims (_ :: Dims ds) -> case inferKnownBackend @t @ds of- Dict -> XFrame <$> arbitrary @(DataFrame t ds)- shrink (XFrame df) = XFrame <$> shrink df--instance ( All Arbitrary ts, All PrimBytes ts, All Num ts, All Ord ts- , RepresentableList ts- , Arbitrary (Dims xs), All KnownXNatType xs)- => Arbitrary (DataFrame ts (xs :: [XNat])) where- arbitrary = do- ds <- arbitrary @(Dims xs)- case ds of- XDims (_ :: Dims ds) -> case inferKnownBackend @ts @ds of- Dict -> XFrame <$> arbitrary @(DataFrame ts ds)- shrink (XFrame df) = XFrame <$> shrink df---instance KnownDim n => Arbitrary (Idx n) where- arbitrary = elements [0..]--instance Dimensions ns => Arbitrary (Idxs ns) where- arbitrary = go (Dims @ns)- where- go :: forall (bs :: [Nat]) . Dims bs -> Gen (Idxs bs)- go U = pure U- go (D :* bs) = (:*) <$> arbitrary <*> go bs--instance (RepresentableList xs, All Arbitrary xs) => Arbitrary (ST.Tuple xs) where- arbitrary = go (tList @Type @xs)- where- go :: forall (bs :: [Type])- . All Arbitrary bs- => TypeList bs -> Gen (ST.Tuple bs)- go U = pure U- go (_ :* bs) = (ST.:$) <$> arbitrary <*> go bs--instance (RepresentableList xs, All Arbitrary xs) => Arbitrary (LT.Tuple xs) where- arbitrary = go (tList @Type @xs)- where- go :: forall (bs :: [Type])- . All Arbitrary bs- => TypeList bs -> Gen (LT.Tuple bs)- go U = pure U- go (_ :* bs) = (LT.:$) <$> arbitrary <*> go bs--data AnyMatrix-data NonSingular--data SomeSquareMatrix prop t- = forall (n :: Nat)- . (KnownDim n, KnownBackend t '[n], KnownBackend t '[n, n])- => SSM (DataFrame t '[n,n])--instance (Show t, PrimBytes t) => Show (SomeSquareMatrix prop t) where- show (SSM df) = show df--instance (Arbitrary t, PrimBytes t, Num t, Ord t)- => Arbitrary (SomeSquareMatrix AnyMatrix t) where- arbitrary = do- dx <- arbitrary @(Dim (XN 2))- case dx of- Dx (D :: Dim n) -> case inferKnownBackend @t @'[n] of- Dict -> SSM <$> arbitrary @(DataFrame t '[n,n])- shrink (SSM df)= SSM <$> shrink df--instance (Arbitrary t, PrimBytes t, Num t, Ord t)- => Arbitrary (SomeSquareMatrix NonSingular t) where- arbitrary = do- SSM (someMat :: DataFrame t '[n, n]) <- arbitrary @(SomeSquareMatrix AnyMatrix t)- -- https://en.wikipedia.org/wiki/Diagonally_dominant_matrix- return . SSM $- iwmap @t @'[n] @'[n] @'[n,n]- @t @'[n] @'[n,n]- ( \i v ->- let s = ewfoldl (\a -> (a +) . abs) 1 v- in update i s v- ) someMat- shrink _ = []
test/Numeric/DataFrame/BasicTest.hs view
@@ -21,12 +21,11 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-} module Numeric.DataFrame.BasicTest (runTests) where +import Numeric.Arbitraries () import Numeric.DataFrame-import Numeric.DataFrame.Arbitraries () import qualified Numeric.ProductOrd.NonTransitive as NonTransitive import qualified Numeric.ProductOrd.Partial as Partial import Test.QuickCheck
test/Numeric/DataFrame/SubSpaceTest.hs view
@@ -1,55 +1,134 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}+-- overwise GHC dies trying to optimize 100 equal funs in each test+{-# OPTIONS_GHC -O0 #-} module Numeric.DataFrame.SubSpaceTest (runTests) where +import Data.Typeable (typeOf)+import Numeric.Arbitraries import Numeric.DataFrame-import Numeric.DataFrame.Arbitraries () import Numeric.Dimensions import Test.QuickCheck -type SFull = '[2,5,4,3,7]-type SPref = '[2,5,4]-type SSuff = '[3,7]+prop_IndexFrame0 :: Property+prop_IndexFrame0 = property $ oneof $( aFewPropVariants $ do+ t <- someElemTypeQ+ (_, ds) <- someLenAsBsQ 0+ [e| do+ df <- arbitrary @(DataFrame $t $ds)+ return $ conjoin+ [ index U df === df+ , indexOffset 0 df === df ]+ |]+ ) -prop_IndexDimMax :: DataFrame Int SPref -> DataFrame Int SSuff -> Bool-prop_IndexDimMax x y =- (z ! (maxBound `inSpaceOf` x)) == y- where- z = ewgen y :: DataFrame Int SFull+prop_IndexFrame1 :: Property+prop_IndexFrame1 = property $ oneof $( aFewPropVariants $ do+ t <- someElemTypeQ+ (as, bs) <- someLenAsBsQ 1+ [e| do+ i@(Idx ii :* U) <- arbitrary @(Idxs $as)+ df <- arbitrary @(DataFrame $t $(concatDimsQ as bs))+ return $ index i df === df ! ii+ |]+ ) -prop_IndexCustom1 :: DataFrame Word SSuff -> Bool-prop_IndexCustom1 x = (z ! 1:*3:*2) == x- where- z = ewgen x :: DataFrame Word SFull+prop_IndexFrame2 :: Property+prop_IndexFrame2 = property $ oneof $( aFewPropVariants $ do+ t <- someElemTypeQ+ (as, bs) <- someLenAsBsQ 2+ [e| do+ i@((Idx ii1) :* (Idx ii2) :* U) <- arbitrary @(Idxs $as)+ df <- arbitrary @(DataFrame $t $(concatDimsQ as bs))+ return $ index i df === df ! ii1 ! ii2+ |]+ ) --- TODO:!--- prop_IndexCustom2 :: DataFrame Double SSuff -> Bool--- prop_IndexCustom2 x = (z ! 2:*2:*1) %* eye == x--- where--- z = ewgen x :: DataFrame Double SFull+prop_IndexOffset :: Property+prop_IndexOffset = property $ oneof $( aFewPropVariants $ do+ t <- someElemTypeQ+ (as, bs) <- someAsBsNQ+ [e| do+ i <- arbitrary @(Idxs $as)+ df <- arbitrary @(DataFrame $t $(concatDimsQ as bs))+ let asw = listDims (dims @($as))+ bsN = totalDim (dims @($bs))+ isw = listIdxs i+ off = fromIntegral . snd+ . foldr (\(dn, di) (tn, to) -> (tn*dn, to + tn*di) ) (bsN, 0)+ $ zip asw isw+ return $ index i df === indexOffset off df+ |]+ ) -prop_Foldlr :: DataFrame Double SSuff -> Bool-prop_Foldlr x =- abs (ewfoldl (+) 10 z - ewfoldr @_ @SPref (+) 0 z - 10)- <= fromScalar (zmax * 0.0001)- where- z = ewgen x :: DataFrame Double SFull- zmax = ewfoldl @Double @SFull @'[] (max . abs) 0.001 z+prop_Foldlr :: Property+prop_Foldlr = property $ oneof $( aFewPropVariants $ do+ t <- someElemTypeFQ+ (as, bs) <- someAsBsQ+ let asbs = concatDimsQ as bs+ [e| do+ df <- arbitrary @(DataFrame $t $asbs)+ let lim = ewfoldr' @($t) @($asbs) (max.abs) 1 df+ * ewfoldr' @($t) @($as) @($bs) (const (+1)) 0 df+ l = ewfoldl' (+) 10 df+ r = ewfoldr' (+) 0 df + 10 :: DataFrame $t $bs+ return $ approxEq lim l r+ |]+ ) -prop_Ewmap :: DataFrame Double SFull -> Bool-prop_Ewmap x = x * 2 == ewmap @_ @_ @'[Last SFull] (*2) x+prop_LazyFoldlr :: Property+prop_LazyFoldlr = property $ oneof $( aFewPropVariants $ do+ t <- someElemTypeQ+ (as, bs) <- someAsBsQ+ let asbs = concatDimsQ as bs+ [e| do+ n <- fromIntegral <$> choose (0, maxTotalDim `quot` 2)+ df <- arbitrary @(DataFrame $t $asbs)+ let S td = ewfoldr' @($t) @($as) @($bs) (const (+1)) 0 df+ l = ewfoldl @($t) @($as) @($bs) (limitedList) (const []) df n+ l' = ewfoldl' @($t) @($as) @($bs) (flip (:)) [] df+ r = ewfoldr @($t) @($as) @($bs) (flip limitedList) (const []) df n+ r' = ewfoldr' @($t) @($as) @($bs) (:) [] df -prop_ProdTranspose :: DataFrame Double '[2,6] -> DataFrame Double '[6,7] -> Bool-prop_ProdTranspose x y = transpose (x %* y) == transpose y %* transpose x+ return $+ counterexample+ ( unlines+ [ "Failed with dims:"+ , " n: " ++ show n+ , " td: " ++ show td+ , " @as: " ++ show (typeOf $ minimalDims @($as))+ , " @bs: " ++ show (typeOf $ minimalDims @($bs))+ ]+ ) $ conjoin+ [ if n >= td+ then l === reverse r+ else shouldErr (l === l') .&&. shouldErr (r === r')+ , take n l === take n l'+ , take n r === take n r'+ ]+ |]+ ) where+ shouldErr :: Property -> Property+ shouldErr p = ioProperty $ checkErr+ <$> quickCheckWithResult stdArgs { chatty = False } p+ checkErr :: Result -> Property+ checkErr Failure { theException = Just _}+ = property True+ checkErr _+ = counterexample "Should have failed with undefined." False+ limitedList :: (Int -> [a]) -> a -> Int -> [a]+ limitedList f a k | k <= 0 = undefined+ | otherwise = a : f (k - 1) -prop_Eye :: DataFrame Double SFull -> Bool-prop_Eye x = eye %* x == x && x %* eye == x return [] runTests :: Int -> IO Bool
+ test/Numeric/Matrix/BidiagonalTest.hs view
@@ -0,0 +1,144 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Numeric.Matrix.BidiagonalTest (runTests) where+++import Control.Monad (join)+import Data.Kind+import Data.Monoid (All (..))+import Numeric.Arbitraries+import Numeric.DataFrame+import Numeric.Dimensions+import Numeric.Matrix.Bidiagonal+import Test.QuickCheck++validateBidiagonal :: forall (t :: Type) (n :: Nat) (m :: Nat)+ . ( KnownDim n, KnownDim m+ , RealFloatExtras t, Show t+ , MatrixDeterminant t n+ , MatrixDeterminant t m+ )+ => Matrix t n m -> BiDiag t n m -> Property+validateBidiagonal a BiDiag {..} =+ counterexample+ (unlines+ [ "failed a =~= u %* b %* transpose v:"+ , "a: " ++ show a+ , "a': " ++ show a'+ , "u: " ++ show u+ , "b: " ++ show b+ , "v: " ++ show v+ ]+ ) (approxEq mag a a')+ .&&.+ counterexample+ (unlines+ [ "u is not quite orthogonal:"+ , "a: " ++ show a+ , "u: " ++ show u+ , "b: " ++ show b+ , "v: " ++ show v+ ]+ ) (approxEq mag eye (u %* transpose u))+ .&&.+ counterexample+ (unlines+ [ "v is not quite orthogonal:"+ , "a: " ++ show a+ , "u: " ++ show u+ , "b: " ++ show b+ , "v: " ++ show v+ ]+ ) (approxEq mag eye (v %* transpose v))+ .&&.+ counterexample+ (unlines+ [ "b is not upper-bidiagonal"+ , "a: " ++ show a+ , "u: " ++ show u+ , "b: " ++ show b+ , "v: " ++ show v+ ]+ ) (getAll $ iwfoldMap @t @'[n,m]+ (\(Idx i :* Idx j :* U) x -> All (j == i || j == i + 1 || x == 0))+ b+ )+ .&&.+ counterexample+ (unlines+ [ "Incorrect determinant carried out for U"+ , "a: " ++ show a+ , "u: " ++ show u+ , "bdUDet: " ++ show bdUDet+ , "det u: " ++ show (det u)+ ]+ ) (approxEq mag bdUDet (det u))+ .&&.+ counterexample+ (unlines+ [ "Incorrect determinant carried out for V"+ , "a: " ++ show a+ , "v: " ++ show v+ , "bdVDet: " ++ show bdVDet+ , "det v: " ++ show (det v)+ ]+ ) (approxEq mag bdVDet (det v))+ where+ mag = fromIntegral (totalDim $ dims `inSpaceOf` a) ^ (2 :: Int) :: Scalar t+ a' = u %* b %* transpose v+ b = biDiag dims bdAlpha bdBeta+ u = bdU+ v = bdV++prop_bidiagonalSimple :: Property+prop_bidiagonalSimple = once . conjoin $ map prop_bidiagonal $ xs+ where+ mkM :: Dims ([n,m]) -> [Double] -> DataFrame Double '[XN 1, XN 1]+ mkM ds+ | Just (XDims ds'@Dims) <- constrainDims ds :: Maybe (Dims '[XN 1, XN 1])+ = XFrame . fromFlatList ds' 0+ mkM _ = error "prop_qrSimple: bad dims"+ variants :: Num a => [a] -> [[a]]+ variants as = rotateList as ++ rotateList (map negate as)+ xs :: [DataFrame Double '[XN 1, XN 1]]+ xs = join+ [ [mkM $ D2 :* D2 :* U, mkM $ D5 :* D2 :* U, mkM $ D3 :* D7 :* U]+ <*> [repeat 0, repeat 1, repeat 2]+ , mkM (D2 :* D2 :* U) <$> variants [3,2, 4,1]+ , mkM (D3 :* D3 :* U) <$> variants [0,0,1, 3,2,0, 4,1,0]+ , mkM (D4 :* D2 :* U) <$> variants [3,2, 4,1, 0,0, 0,2]+ , mkM (D2 :* D3 :* U) <$> variants [3,2,0, 4,1,2]+ , mkM (D2 :* D2 :* U) <$> variants [2, 0, 0, 0]+ , mkM (D2 :* D2 :* U) <$> variants [4, 1, 0, 0]+ , mkM (D2 :* D2 :* U) <$> variants [3, 1, -2, 0]+ , [mkM $ D2 :* D3 :* U, mkM $ D3 :* D2 :* U]+ <*> join+ [ variants [2, 0, 0, 0, 0, 0]+ , variants [4, 1, 0, 0, 0, 0]+ , variants [3, 1, -2, 0, 0, 0]+ , variants [3, 0, -2, 9, 0, 0]+ ]+ ]++prop_bidiagonal :: DataFrame Double '[XN 1, XN 1] -> Property+prop_bidiagonal (XFrame x)+ | n@D :* m@D :* U <- dims `inSpaceOf` x+ , D <- minDim n m+ = validateBidiagonal x (bidiagonalHouseholder x)+prop_bidiagonal _ = property False+++++return []+runTests :: Int -> IO Bool+runTests n = $forAllProperties+ $ quickCheckWithResult stdArgs { maxSuccess = n }
+ test/Numeric/Matrix/LUTest.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Numeric.Matrix.LUTest (runTests) where+++import Control.Monad (join)+import Data.Kind+import Data.Monoid (All (..))+import Numeric.Arbitraries+import Numeric.DataFrame+import Numeric.Dimensions+import Numeric.Matrix.LU+import Test.QuickCheck+++validateLU :: forall (t :: Type) (n :: Nat)+ . ( KnownDim n, RealFloatExtras t, Show t+ , MatrixDeterminant t n)+ => Matrix t n n -> LU t n -> Property+validateLU x lux@LU {..} =+ counterexample+ (unlines+ [ "failed luPerm m =~= luLower %* luUpper:"+ , "x: " ++ show x+ , "px: " ++ show px+ , "px': " ++ show px'+ , "lu: " ++ show lux+ ]+ ) (px =~= px')+ .&&.+ counterexample+ (unlines+ [ "Failed an extra invariant det luPerm == luPermDet:"+ , "lu:" ++ show lux+ ]+ ) (luPermDet =~= det luPerm)+ .&&.+ counterexample+ (unlines+ [ "luUpper is not upper-triangular"+ , "lu:" ++ show lux+ , "x:" ++ show x+ ]+ ) (getAll $ iwfoldMap @t @'[n,n]+ (\(Idx i :* Idx j :* U) a -> All (j >= i || a == 0))+ luUpper+ )+ .&&.+ counterexample+ (unlines+ [ "luLower is not unit lower triangular,"+ ++ " or some of its elements are greater than one"+ , "lu:" ++ show lux+ , "x:" ++ show x+ ]+ ) (getAll $ iwfoldMap @t @'[n,n]+ (\(Idx i :* Idx j :* U) a -> case compare i j of+ GT -> All (abs a <= 1)+ EQ -> All (a == 1)+ LT -> All (a == 0)+ ) luLower+ )+ where+ px = luPerm %* x+ px' = luLower %* luUpper+++manualMats :: forall t . (PrimBytes t, Num t) => [SomeSquareMatrix AnyMatrix t]+manualMats = join+ [ [mkM D2, mkM D4, mkM D9]+ <*> [repeat 0, repeat 1, repeat 2]+ , mkM D2 <$> variants [3,2, 4,1]+ , mkM D3 <$> variants [0,0,1, 3,2,0, 4,1,0]+ , mkM D2 <$> variants [2, 0, 0, 0]+ , mkM D2 <$> variants [4, 1, 0, 0]+ , mkM D2 <$> variants [3, 1, -2, 0]+ ]+ where+ mkM :: Dim (n :: Nat) -> [t] -> SomeSquareMatrix AnyMatrix t+ mkM (d@D :: Dim n) = SSM . fromFlatList (d :* d :* U) 0+ mkM _ = error "manualMats: bad dims"+ variants :: Num a => [a] -> [[a]]+ variants as = rotateList as ++ rotateList (map negate as)+++prop_luSimple :: Property+prop_luSimple = once . conjoin $ map prop_lu manualMats++prop_lu :: SomeSquareMatrix AnyMatrix Double -> Property+prop_lu (SSM x) = validateLU x (lu x)+++return []+runTests :: Int -> IO Bool+runTests n = $forAllProperties+ $ quickCheckWithResult stdArgs { maxSuccess = n }
+ test/Numeric/Matrix/QRTest.hs view
@@ -0,0 +1,203 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Numeric.Matrix.QRTest (runTests) where+++import Control.Monad (join)+import Data.Kind+import Data.Monoid (All (..))+import Numeric.Arbitraries+import Numeric.DataFrame+import Numeric.Dimensions+import Numeric.Matrix.QR+import Test.QuickCheck+++validateQR :: forall (t :: Type) (n :: Nat) (m :: Nat)+ . ( KnownDim n, KnownDim m+ , RealFloatExtras t, Show t+ )+ => Matrix t n m -> QR t n m -> Property+validateQR x q@QR {..} =+ counterexample+ (unlines+ [ "failed m =~= qrQ %* qrR:"+ , "m: " ++ show x+ , "m': " ++ show x'+ , "qr:" ++ show q+ ]+ ) (x =~= x')+ .&&.+ counterexample+ (unlines+ [ "qrQ is not quite orthogonal:"+ , "qr:" ++ show q+ , "m:" ++ show x+ ]+ ) (eye =~= qrQ %* transpose qrQ)+ .&&.+ counterexample+ (unlines+ [ "qrR is not upper-triangular"+ , "qr:" ++ show q+ , "m:" ++ show x+ ]+ ) (getAll $ iwfoldMap @t @'[n,m]+ (\(Idx i :* Idx j :* U) a -> All (j >= i || a <= tol))+ qrR+ )+ where+ tol = M_EPS * max 1 (maxElem x)+ x' = qrQ %* qrR++validateLQ :: forall (t :: Type) (n :: Nat) (m :: Nat)+ . ( KnownDim n, KnownDim m+ , RealFloatExtras t, Show t+ )+ => Matrix t n m -> LQ t n m -> Property+validateLQ x q@LQ {..} =+ counterexample+ (unlines+ [ "failed m =~= lqL %* lqQ:"+ , "m: " ++ show x+ , "m': " ++ show x'+ , "lq:" ++ show q+ ]+ ) (x =~= x')+ .&&.+ counterexample+ (unlines+ [ "lqQ is not quite orthogonal:"+ , "lq:" ++ show q+ , "m:" ++ show x+ ]+ ) (eye =~= lqQ %* transpose lqQ)+ .&&.+ counterexample+ (unlines+ [ "lqL is not lower-triangular"+ , "lq:" ++ show q+ , "m:" ++ show x+ ]+ ) (getAll $ iwfoldMap @t @'[n,m]+ (\(Idx i :* Idx j :* U) a -> All (j <= i || a <= tol))+ lqL+ )+ where+ tol = M_EPS * max 1 (maxElem x)+ x' = lqL %* lqQ++manualMats :: [DataFrame Double '[XN 1, XN 1]]+manualMats = join+ [ [mkM $ D2 :* D2 :* U, mkM $ D5 :* D2 :* U, mkM $ D3 :* D7 :* U]+ <*> [repeat 0, repeat 1, repeat 2]+ , mkM (D2 :* D2 :* U) <$> variants [3,2, 4,1]+ , mkM (D3 :* D3 :* U) <$> variants [0,0,1, 3,2,0, 4,1,0]+ , mkM (D4 :* D2 :* U) <$> variants [3,2, 4,1, 0,0, 0,2]+ , mkM (D2 :* D3 :* U) <$> variants [3,2,0, 4,1,2]+ , mkM (D2 :* D2 :* U) <$> variants [2, 0, 0, 0]+ , mkM (D2 :* D2 :* U) <$> variants [4, 1, 0, 0]+ , mkM (D2 :* D2 :* U) <$> variants [3, 1, -2, 0]+ , [mkM $ D2 :* D3 :* U, mkM $ D3 :* D2 :* U]+ <*> join+ [ variants [2, 0, 0, 0, 0, 0]+ , variants [4, 1, 0, 0, 0, 0]+ , variants [3, 1, -2, 0, 0, 0]+ , variants [3, 0, -2, 9, 0, 0]+ ]+ ]+ where+ mkM :: Dims ([n,m]) -> [Double] -> DataFrame Double '[XN 1, XN 1]+ mkM ds+ | Just (XDims ds'@Dims) <- constrainDims ds :: Maybe (Dims '[XN 1, XN 1])+ = XFrame . fromFlatList ds' 0+ mkM _ = error "prop_qrSimple: bad dims"+ variants :: Num a => [a] -> [[a]]+ variants as = rotateList as ++ rotateList (map negate as)++prop_lqSimple :: Property+prop_lqSimple = once . conjoin $ map prop_lq manualMats++prop_lq :: DataFrame Double '[XN 1, XN 1] -> Property+prop_lq (XFrame x)+ | n@D :* m@D :* U <- dims `inSpaceOf` x+ , D <- minDim n m+ = validateLQ x (lq x)+prop_lq _ = property False++prop_qrSimple :: Property+prop_qrSimple = once . conjoin $ map prop_qr manualMats++prop_qr :: DataFrame Double '[XN 1, XN 1] -> Property+prop_qr (XFrame x)+ | n@D :* m@D :* U <- dims `inSpaceOf` x+ , D <- minDim n m+ = validateQR x (qr x)+prop_qr _ = property False+++testQRSolve :: forall t n m+ . (MatrixQR t n m, Show t, RealFloatExtras t)+ => Matrix t n m -> Vector t n -> Property+testQRSolve a b =+ counterexample+ (unlines+ [ "failed a' %* (a %* xr - b) =~= 0"+ , "a: " ++ show a+ , "b: " ++ show b+ , "xr: " ++ show xr+ , "zeroR: " ++ show zeroR+ , "zeroL: " ++ show zeroL+ ]+ ) (nonDeficient ==> approxEq mag2 zeroR 0)+ .&&.+ counterexample+ (unlines+ [ "failed (xl %* a' - b) %* a =~= 0"+ , "a': " ++ show a'+ , "b: " ++ show b+ , "xl: " ++ show xl+ ]+ ) (nonDeficient ==> approxEq mag2 zeroL 0)+ .&&.+ counterexample+ (unlines+ [ "failed xr =~= xl"+ , "a': " ++ show a'+ , "b: " ++ show b+ , "xl: " ++ show xl+ ]+ ) (approxEq mag xr xl)+ where+ qrr = qrR (qr a)+ nonDeficient =+ foldl (\r i -> r && abs (qrr ! i ! i) > M_EPS * mag) True+ [0 .. min (dimVal' @n) (dimVal' @m) - 1]+ nm = totalDim (dims `inSpaceOf` a)+ mag = max 1 (maxElem a `max` maxElem b) * fromIntegral nm+ mag2 = mag*mag+ a' = transpose a+ xr = qrSolveR a b+ xl = qrSolveL a' b+ zeroR = a' %* (a %* xr - b)+ zeroL = (xl %* a' - b) %* a+++prop_qrSolve :: Dim (XN 1) -> Dim (XN 1) -> Property+prop_qrSolve (Dx (D :: Dim n)) (Dx (D :: Dim m)) = property $ do+ a <- arbitrary @(DataFrame Double '[n,m])+ b <- arbitrary @(DataFrame Double '[n])+ return $ testQRSolve a b++return []+runTests :: Int -> IO Bool+runTests n = $forAllProperties+ $ quickCheckWithResult stdArgs { maxSuccess = n }
+ test/Numeric/Matrix/SVDTest.hs view
@@ -0,0 +1,182 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Numeric.Matrix.SVDTest (runTests) where+++import Control.Monad+import Data.Kind+import Data.Maybe (isJust)+import Numeric.Arbitraries+import Numeric.DataFrame+import Numeric.Dimensions+import Numeric.Matrix.SVD+import Test.QuickCheck++-- check invariants of SVD+validateSVD :: forall (t :: Type) (n :: Nat) (m :: Nat)+ . ( KnownDim n, KnownDim m, KnownDim (Min n m)+ , RealFloatExtras t, Show t+ , MatrixDeterminant t n+ )+ => t -> Matrix t n m -> SVD t n m -> Property+validateSVD extraTolerance x s@SVD {..}+ | nm <- fromIntegral $ dimVal' @n * dimVal' @m =+ counterexample+ (unlines+ [ "failed m =~= u %* asDiag s %* transpose v:"+ , "m: " ++ show x+ , "m': " ++ show x'+ , "svd:" ++ show s+ ]+ ) (approxEq (S $ nm * nm * extraTolerance) x x')+ .&&.+ counterexample+ (unlines+ [ "SVD left-singular basis is not quite orthogonal:"+ , "svd:" ++ show s+ , "m:" ++ show x+ ]+ ) (eye =~= svdU %* transpose svdU)+ .&&.+ counterexample+ (unlines+ [ "Failed an extra invariant det svdU == 1:"+ , "svd:" ++ show s+ , "m:" ++ show x+ ]+ ) (1 =~= det svdU)+ .&&.+ counterexample+ (unlines+ [ "SVD right-singular basis is not quite orthogonal:"+ , "svd:" ++ show s+ , "m:" ++ show x+ ]+ ) (eye =~= svdV %* transpose svdV)+ .&&.+ counterexample+ (unlines+ [ "Bad singular values (should be decreasing, non-negative):"+ , "svd:" ++ show s+ , "m:" ++ show x+ ]+ )+ (isJust $ ewfoldr @_ @'[Min n m] @'[] checkIncreasing (Just 0) svdS)+ where+ checkIncreasing :: Scalar t -> Maybe (Scalar t) -> Maybe (Scalar t)+ checkIncreasing _ Nothing = Nothing+ checkIncreasing a (Just b)+ | a >= b = Just a+ | otherwise = Nothing+ x' = svdU %* asDiag svdS %* transpose svdV++prop_svd2x :: Property+prop_svd2x = once . conjoin $ map prop_svd2 $ xs ++ map negate xs+ where+ mkM :: [Double] -> Mat22d+ mkM = fromFlatList dims 0+ xs :: [Mat22d]+ xs =+ [ 0, 1, 2, eye]+ ++ map mkM (rotateList [4,0,0,0])+ ++ map mkM (rotateList [4,3,0,0])+ ++ map mkM (rotateList [4,-3,0,0])+ ++ map mkM (rotateList [3,0,-2,0])+ ++ map mkM (rotateList [3,1,-2,0])+ ++ map mkM (rotateList [1,1,3,3])+ ++ map ((eye + ). mkM) (rotateList [4,0,0,0])+ ++ map ((eye - ). mkM) (rotateList [4,0,0,0])++prop_svd2 :: Matrix Double 2 2 -> Property+prop_svd2 m = validateSVD 1 m (svd2 m)++prop_svd3x :: Property+prop_svd3x = once . conjoin $ map prop_svd3 $ xs ++ map negate xs+ where+ mkM :: [Double] -> Mat33d+ mkM = fromFlatList dims 0+ xs :: [Mat33d]+ xs =+ [ 0, 1, 2, eye]+ ++ map mkM (rotateList [4,0,0,0,0,0,0,0,0])+ ++ map ((eye + ). mkM) (rotateList [4,0,0,0,0,0,0,0,0])+ ++ map ((eye - ). mkM) (rotateList [4,0,0,0,0,0,0,0,0])+ ++ map mkM (rotateList [4,1,0,0,0,0,0,0,0])+ ++ map mkM (rotateList [4,2,1,0,0,0,0,0,0])+ ++ map mkM (rotateList [4,2,1,0,0,7,0,0,0])+ ++ map mkM (rotateList [-5,0,1,0,0,7,1,0,10])+ ++ map mkM (rotateList [0,4.237014829207297,-2.0275007228244943+ ,0,-0.9998659636721602,0+ ,0,0,0.34652350292261475])+ ++ map mkM (rotateList [0,0,15.33245854346668+ ,0,-18.99787842921179,0+ ,0,29.953254288817046,0.78897034887446])+ ++ map mkM (rotateList [-306.5504258807584,0,0+ ,-58.78957738886663,0,95.51923744049921+ ,0,0,-292.92428690917154])+ ++ map mkM (rotateList [0,116.15251855804613,-94.95820664249142+ ,0,-69.551119690005,0+ ,0,0,46.77872074296825])++++prop_svd3 :: Matrix Double 3 3 -> Property+prop_svd3 m = validateSVD extraTolerance m (svd3 m)+ where+ -- svd3 is fast, but not particularly precise,+ -- though, 5-7 digits of precision are still there.+ extraTolerance = 10e4 :: Double+++prop_svdSimple :: Property+prop_svdSimple = once . conjoin $ prop_svd <$> dfs++dfs :: [DataFrame Double '[XN 1, XN 1]]+dfs = xs+ where+ mkM :: Dims ([n,m]) -> [Double] -> DataFrame Double '[XN 1, XN 1]+ mkM ds+ | Just (XDims ds'@Dims) <- constrainDims ds :: Maybe (Dims '[XN 1, XN 1])+ = XFrame . fromFlatList ds' 0+ mkM _ = error "prop_qrSimple: bad dims"+ variants :: Num a => [a] -> [[a]]+ variants as = rotateList as ++ rotateList (map negate as)+ xs :: [DataFrame Double '[XN 1, XN 1]]+ xs = join+ [ [mkM $ D2 :* D2 :* U, mkM $ D5 :* D2 :* U, mkM $ D3 :* D7 :* U]+ <*> [repeat 0, repeat 1, repeat 2]+ , mkM (D2 :* D2 :* U) <$> variants [3,2, 4,1]+ , mkM (D3 :* D3 :* U) <$> variants [0,0,1, 3,2,0, 4,1,0]+ , mkM (D4 :* D2 :* U) <$> variants [3,2, 4,1, 0,0, 0,2]+ , mkM (D2 :* D3 :* U) <$> variants [3,2,0, 4,1,2]+ , mkM (D2 :* D2 :* U) <$> variants [2, 0, 0, 0]+ , mkM (D2 :* D2 :* U) <$> variants [4, 1, 0, 0]+ , mkM (D2 :* D2 :* U) <$> variants [3, 1, -2, 0]+ , [mkM $ D2 :* D3 :* U, mkM $ D3 :* D2 :* U]+ <*> join+ [ variants [2, 0, 0, 0, 0, 0]+ , variants [4, 1, 0, 0, 0, 0]+ , variants [3, 1, -2, 0, 0, 0]+ , variants [3, 0, -2, 9, 0, 0]+ ]+ ]++prop_svd :: DataFrame Double '[XN 1, XN 1] -> Property+prop_svd (XFrame x)+ | n@D :* m@D :* U <- dims `inSpaceOf` x+ , D <- minDim n m = validateSVD 1 x (svd x)+prop_svd _ = error "prop_svd: impossible pattern"+++return []+runTests :: Int -> IO Bool+runTests n = $forAllProperties+ $ quickCheckWithResult stdArgs { maxSuccess = n }
test/Numeric/MatrixDoubleTest.hs view
@@ -11,77 +11,48 @@ import Data.Fixed-import Data.Semigroup+import Numeric.Arbitraries import Numeric.DataFrame-import Numeric.DataFrame.Arbitraries import Numeric.Dimensions import Test.QuickCheck type TestElem = Double-type TestDF = DataFrame TestElem -eps :: Scalar TestElem-eps = 0.000000001- dropW :: (SubSpace t '[3] '[] '[3], SubSpace t '[4] '[] '[4]) => Vector t 4 -> Vector t 3 dropW (Vec4 x y z _) = Vec3 x y z --- | Most of the time, the error is proportional to the maginutude of the biggest element-maxElem :: (SubSpace TestElem ds '[] ds)- => TestDF (ds :: [Nat]) -> Scalar TestElem-maxElem = ewfoldl (\a -> max a . abs) 0---- | For operations like @det@, the error is proportional to maximum possible product--- over rows or columns-maxRows :: forall ds- . (SubSpace TestElem '[Head ds] (Tail ds) ds)- => TestDF (ds :: [Nat]) -> Scalar TestElem-maxRows = getProduct . ewfoldMap @_ @'[Head ds] @(Tail ds) @ds (Product . maxElem)--approxEq ::- forall (ds :: [Nat]) .- (- Dimensions ds,- Num (TestDF ds),- PrimBytes (TestDF ds),- PrimArray TestElem (TestDF ds)- ) =>- TestDF ds -> TestDF ds -> Bool-approxEq a b = maxElem (a - b) <= eps * m- where- m = maxElem a `max` maxElem b-infix 4 `approxEq`- prop_detTranspose :: SomeSquareMatrix AnyMatrix TestElem -> Property-prop_detTranspose (SSM m)- = let a = det m- b = det $ transpose m- n = (case dims `inSpaceOf` m of dn :* _ -> dimVal dn) :: Word- mag = eps * maxRows m * fromIntegral (product [1..n])- in counterexample- (unlines- [ "failed det/transpose:"- , "m: " ++ show m- , "mT: " ++ show (transpose m)- , show a ++ " /= " ++ show b- ++ " (tolerance: " ++ show mag ++ ")."- ]- ) $ abs (a - b) <= mag+prop_detTranspose (SSM m') = counterexample+ (unlines+ [ "Failed det/transpose:"+ , "m: " ++ show m+ , "mT: " ++ show (transpose m)+ ]+ ) $ approxEq mag a b+ where+ n2 = totalDim (dims `inSpaceOf` m)+ mag = fromIntegral (n2*n2)+ mm = recip $ maxElem m' `max` 1+ m = ewmap (*mm) m'+ a = det m+ b = det $ transpose m prop_inverse :: SomeSquareMatrix NonSingular TestElem -> Property-prop_inverse (SSM m)- = let mi = inverse m- aeq a b = maxElem (b - a) <= eps * maxRows m- in counterexample ("failed inverse:" ++- show (m, mi, m %* mi, mi %* m)) $- aeq eye (m %* mi) && aeq eye (mi %* m)--prop_LU :: SomeSquareMatrix NonSingular TestElem -> Bool-prop_LU (SSM m)- = let f = lu m- aeq a b = maxElem (b - a) <= eps * maxRows m- in aeq (luPerm f %* m) (luLower f %* luUpper f)+prop_inverse (SSM m) = check (m %* mi) .&&. check (mi %* m)+ where+ n2 = totalDim (dims `inSpaceOf` m)+ mag = maxElem m `max` maxElem mi `max` 1+ * fromIntegral (n2*n2)+ mi = inverse m+ check a = counterexample+ ( unlines+ [ "Failed inverse:"+ , " m: " ++ show m+ , " mi: " ++ show mi+ , " eye': " ++ show a+ ]+ ) $ approxEq mag a eye prop_translate3vs4 :: Vector TestElem 4 -> Bool prop_translate3vs4 v = translate4 v == translate3 (dropW v)@@ -95,92 +66,96 @@ prop_rotateX :: Vector TestElem 4 -> Property prop_rotateX v@(Vec4 x y z w) = conjoin [- v %* rotateX (-2 * pi) `approxEq` v,- v %* rotateX (-1.5 * pi) `approxEq` vec4 x (-z) y w,- v %* rotateX (-pi) `approxEq` vec4 x (-y) (-z) w,- v %* rotateX (-0.5 * pi) `approxEq` vec4 x z (-y) w,- v %* rotateX 0 `approxEq` v,- v %* rotateX (0.5 * pi) `approxEq` vec4 x (-z) y w,- v %* rotateX pi `approxEq` vec4 x (-y) (-z) w,- v %* rotateX (1.5 * pi) `approxEq` vec4 x z (-y) w,- v %* rotateX (2 * pi) `approxEq` v+ v %* rotateX (-2 * pi) =~= v,+ v %* rotateX (-1.5 * pi) =~= vec4 x (-z) y w,+ v %* rotateX (-pi) =~= vec4 x (-y) (-z) w,+ v %* rotateX (-0.5 * pi) =~= vec4 x z (-y) w,+ v %* rotateX 0 =~= v,+ v %* rotateX (0.5 * pi) =~= vec4 x (-z) y w,+ v %* rotateX pi =~= vec4 x (-y) (-z) w,+ v %* rotateX (1.5 * pi) =~= vec4 x z (-y) w,+ v %* rotateX (2 * pi) =~= v ] prop_rotateY :: Vector TestElem 4 -> Property prop_rotateY v@(Vec4 x y z w) = conjoin [- v %* rotateY (-2 * pi) `approxEq` v,- v %* rotateY (-1.5 * pi) `approxEq` vec4 z y (-x) w,- v %* rotateY (-pi) `approxEq` vec4 (-x) y (-z) w,- v %* rotateY (-0.5 * pi) `approxEq` vec4 (-z) y x w,- v %* rotateY 0 `approxEq` v,- v %* rotateY (0.5 * pi) `approxEq` vec4 z y (-x) w,- v %* rotateY pi `approxEq` vec4 (-x) y (-z) w,- v %* rotateY (1.5 * pi) `approxEq` vec4 (-z) y x w,- v %* rotateY (2 * pi) `approxEq` v+ v %* rotateY (-2 * pi) =~= v,+ v %* rotateY (-1.5 * pi) =~= vec4 z y (-x) w,+ v %* rotateY (-pi) =~= vec4 (-x) y (-z) w,+ v %* rotateY (-0.5 * pi) =~= vec4 (-z) y x w,+ v %* rotateY 0 =~= v,+ v %* rotateY (0.5 * pi) =~= vec4 z y (-x) w,+ v %* rotateY pi =~= vec4 (-x) y (-z) w,+ v %* rotateY (1.5 * pi) =~= vec4 (-z) y x w,+ v %* rotateY (2 * pi) =~= v ] prop_rotateZ :: Vector TestElem 4 -> Property prop_rotateZ v@(Vec4 x y z w) = conjoin [- v %* rotateZ (-2 * pi) `approxEq` v,- v %* rotateZ (-1.5 * pi) `approxEq` vec4 (-y) x z w,- v %* rotateZ (-pi) `approxEq` vec4 (-x) (-y) z w,- v %* rotateZ (-0.5 * pi) `approxEq` vec4 y (-x) z w,- v %* rotateZ 0 `approxEq` v,- v %* rotateZ (0.5 * pi) `approxEq` vec4 (-y) x z w,- v %* rotateZ pi `approxEq` vec4 (-x) (-y) z w,- v %* rotateZ (1.5 * pi) `approxEq` vec4 y (-x) z w,- v %* rotateZ (2 * pi) `approxEq` v+ v %* rotateZ (-2 * pi) =~= v,+ v %* rotateZ (-1.5 * pi) =~= vec4 (-y) x z w,+ v %* rotateZ (-pi) =~= vec4 (-x) (-y) z w,+ v %* rotateZ (-0.5 * pi) =~= vec4 y (-x) z w,+ v %* rotateZ 0 =~= v,+ v %* rotateZ (0.5 * pi) =~= vec4 (-y) x z w,+ v %* rotateZ pi =~= vec4 (-x) (-y) z w,+ v %* rotateZ (1.5 * pi) =~= vec4 y (-x) z w,+ v %* rotateZ (2 * pi) =~= v ] prop_rotate :: TestElem -> Property prop_rotate a = conjoin [- rotate (vec3 1 0 0) a `approxEq` rotateX a,- rotate (vec3 0 1 0) a `approxEq` rotateY a,- rotate (vec3 0 0 1) a `approxEq` rotateZ a+ rotate (vec3 1 0 0) a =~= rotateX a,+ rotate (vec3 0 1 0) a =~= rotateY a,+ rotate (vec3 0 0 1) a =~= rotateZ a ] -prop_rotateEuler :: TestElem -> TestElem -> TestElem -> Bool+prop_rotateEuler :: TestElem -> TestElem -> TestElem -> Property prop_rotateEuler pitch yaw roll =- rotateEuler pitch yaw roll `approxEq` rotateZ roll %* rotateY yaw %* rotateX pitch+ rotateEuler pitch yaw roll =~= rotateZ roll %* rotateY yaw %* rotateX pitch prop_lookAt :: Vector TestElem 3 -> Vector TestElem 3 -> Vector TestElem 3 -> Property prop_lookAt up cam foc =- (apart cam foc && apart up cam) ==>+ (apart cam foc && nonColinear up zb && nonColinear zb xb) ==> conjoin [- (normalized . fromHom $ toHomPoint foc %* m) `approxEq` vec3 0 0 (-1),- fromHom (toHomPoint cam %* m) `approxEq` 0,- fromHom (toHomVector xb %* m) `approxEq` vec3 1 0 0,- fromHom (toHomVector yb %* m) `approxEq` vec3 0 1 0,- fromHom (toHomVector zb %* m) `approxEq` vec3 0 0 1+ (normalized . fromHom $ toHomPoint foc %* m) `aeq` vec3 0 0 (-1),+ fromHom (toHomPoint cam %* m) `aeq` 0,+ fromHom (toHomVector xb %* m) `aeq` vec3 1 0 0,+ fromHom (toHomVector yb %* m) `aeq` vec3 0 1 0,+ fromHom (toHomVector zb %* m) `aeq` vec3 0 0 1 ] where+ aeq :: Vector TestElem 3 -> Vector TestElem 3 -> Property+ aeq = approxEq 10 apart :: Vector TestElem 3 -> Vector TestElem 3 -> Bool- apart a b = maxElem (a - b) > 0.01 * (maxElem a `max` maxElem b)+ apart a b = normL2 (a - b) > max (2 * sqrt M_EPS) (0.1 * (normL2 a + normL2 b))+ nonColinear :: Vector TestElem 3 -> Vector TestElem 3 -> Bool+ nonColinear a b = normL2 (cross a b) > 0.1 * abs (dot a b) m = lookAt up cam foc zb = normalized $ cam - foc xb = normalized $ up `cross` zb yb = zb `cross` xb -prop_perspective :: TestElem -> TestElem -> TestElem -> TestElem -> Bool+prop_perspective :: TestElem -> TestElem -> TestElem -> TestElem -> Property prop_perspective a b c d =- and [- projectTo 0 0 n `approxEq` vec3 0 0 (-1),- projectTo 0 0 f `approxEq` vec3 0 0 1,- projectTo 1 1 n `approxEq` vec3 1 1 (-1),- projectTo 1 (-1) n `approxEq` vec3 1 (-1) (-1),- projectTo (-1) 1 n `approxEq` vec3 (-1) 1 (-1),- projectTo (-1) (-1) n `approxEq` vec3 (-1) (-1) (-1),- projectTo 1 1 f `approxEq` vec3 1 1 1,- projectTo 1 (-1) f `approxEq` vec3 1 (-1) 1,- projectTo (-1) 1 f `approxEq` vec3 (-1) 1 1,- projectTo (-1) (-1) f `approxEq` vec3 (-1) (-1) 1+ conjoin [+ projectTo 0 0 n =~= vec3 0 0 (-1),+ projectTo 0 0 f =~= vec3 0 0 1,+ projectTo 1 1 n =~= vec3 1 1 (-1),+ projectTo 1 (-1) n =~= vec3 1 (-1) (-1),+ projectTo (-1) 1 n =~= vec3 (-1) 1 (-1),+ projectTo (-1) (-1) n =~= vec3 (-1) (-1) (-1),+ projectTo 1 1 f =~= vec3 1 1 1,+ projectTo 1 (-1) f =~= vec3 1 (-1) 1,+ projectTo (-1) 1 f =~= vec3 (-1) 1 1,+ projectTo (-1) (-1) f =~= vec3 (-1) (-1) 1 ] where n = 1.0 + mod' a 9.0 -- Near plane in range [1, 10)- f = n + 1.0 + mod' b 99.0 -- Far plane in range [n + 1, n + 100)+ f = 2*n + mod' b 99.0 -- Far plane in range [2*n, 2*n + 100) fovy = (0.1 * pi) + mod' c (0.8 * pi) -- Y-axis field of view in range [0.1*pi, 0.9*pi) aspect = 0.25 + mod' d 4.0 -- Aspect ration in range [1/4, 4/1] hpd = tan (fovy * 0.5) -- height/distance@@ -188,23 +163,23 @@ m = perspective n f fovy aspect projectTo x' y' z = fromHom $ vec4 (x' * wpd * z) (y' * hpd * z) (-z) 1 %* m -prop_orthogonal :: TestElem -> TestElem -> TestElem -> TestElem -> Bool+prop_orthogonal :: TestElem -> TestElem -> TestElem -> TestElem -> Property prop_orthogonal a b c d =- and [- projectTo 0 0 n `approxEq` vec3 0 0 (-1),- projectTo 0 0 f `approxEq` vec3 0 0 1,- projectTo 1 1 n `approxEq` vec3 1 1 (-1),- projectTo 1 (-1) n `approxEq` vec3 1 (-1) (-1),- projectTo (-1) 1 n `approxEq` vec3 (-1) 1 (-1),- projectTo (-1) (-1) n `approxEq` vec3 (-1) (-1) (-1),- projectTo 1 1 f `approxEq` vec3 1 1 1,- projectTo 1 (-1) f `approxEq` vec3 1 (-1) 1,- projectTo (-1) 1 f `approxEq` vec3 (-1) 1 1,- projectTo (-1) (-1) f `approxEq` vec3 (-1) (-1) 1+ conjoin [+ projectTo 0 0 n =~= vec3 0 0 (-1),+ projectTo 0 0 f =~= vec3 0 0 1,+ projectTo 1 1 n =~= vec3 1 1 (-1),+ projectTo 1 (-1) n =~= vec3 1 (-1) (-1),+ projectTo (-1) 1 n =~= vec3 (-1) 1 (-1),+ projectTo (-1) (-1) n =~= vec3 (-1) (-1) (-1),+ projectTo 1 1 f =~= vec3 1 1 1,+ projectTo 1 (-1) f =~= vec3 1 (-1) 1,+ projectTo (-1) 1 f =~= vec3 (-1) 1 1,+ projectTo (-1) (-1) f =~= vec3 (-1) (-1) 1 ] where n = 1.0 + mod' a 9.0 -- Near plane in range [1, 10)- f = n + 1.0 + mod' b 99.0 -- Far plane in range [n + 1, n + 100)+ f = 2*n + mod' b 99.0 -- Far plane in range [2*n, 2*n + 100) w = 1.0 + mod' c 9999.0 -- Width in range [1, 10000) h = 1.0 + mod' d 9999.0 -- Height in range [1, 10000) m = orthogonal n f w h@@ -216,11 +191,11 @@ prop_toHomVector :: Vector TestElem 3 -> Bool prop_toHomVector v@(Vec3 x y z) = toHomVector v == vec4 x y z 0 -prop_fromHom :: Vector TestElem 4 -> Bool+prop_fromHom :: Vector TestElem 4 -> Property prop_fromHom v@(Vec4 x y z w) = case w of- 0 -> fromHom v == vec3 x y z- _ -> fromHom v `approxEq` vec3 (x/w) (y/w) (z/w)+ 0 -> fromHom v === vec3 x y z+ _ -> fromHom v =~= vec3 (x/w) (y/w) (z/w) return [] runTests :: Int -> IO Bool
test/Numeric/MatrixFloatTest.hs view
@@ -11,83 +11,48 @@ import Data.Fixed-import Data.Semigroup+import Numeric.Arbitraries import Numeric.DataFrame-import Numeric.DataFrame.Arbitraries import Numeric.Dimensions import Test.QuickCheck type TestElem = Float-type TestDF = DataFrame TestElem -eps :: Scalar TestElem-eps = 0.00001- dropW :: (SubSpace t '[3] '[] '[3], SubSpace t '[4] '[] '[4]) => Vector t 4 -> Vector t 3 dropW (Vec4 x y z _) = Vec3 x y z --- | Most of the time, the error is proportional to the maginutude of the biggest element-maxElem :: (SubSpace TestElem ds '[] ds)- => TestDF (ds :: [Nat]) -> Scalar TestElem-maxElem = ewfoldl (\a -> max a . abs) 0---- | For operations like @det@, the error is proportional to maximum possible product--- over rows or columns-maxRows :: forall ds- . (SubSpace TestElem '[Head ds] (Tail ds) ds)- => TestDF (ds :: [Nat]) -> Scalar TestElem-maxRows = getProduct . ewfoldMap @_ @'[Head ds] @(Tail ds) @ds (Product . maxElem)--approxEq ::- forall (ds :: [Nat]) .- (- Dimensions ds,- Num (TestDF ds),- PrimBytes (TestDF ds),- PrimArray TestElem (TestDF ds)- ) =>- TestDF ds -> TestDF ds -> Bool-approxEq a b = maxElem (a - b) <= eps * m- where- m = maxElem a `max` maxElem b-infix 4 `approxEq`- prop_detTranspose :: SomeSquareMatrix AnyMatrix TestElem -> Property-prop_detTranspose (SSM m)- = let a = det m- b = det $ transpose m- n = (case dims `inSpaceOf` m of dn :* _ -> dimVal dn) :: Word- mag = eps * maxRows m * fromIntegral (product [1..n])- in counterexample- (unlines- [ "failed det/transpose:"- , "m: " ++ show m- , "mT: " ++ show (transpose m)- , show a ++ " /= " ++ show b- ++ " (tolerance: " ++ show mag ++ ")."- ]- ) $ abs (a - b) <= mag+prop_detTranspose (SSM m') = counterexample+ (unlines+ [ "Failed det/transpose:"+ , "m: " ++ show m+ , "mT: " ++ show (transpose m)+ ]+ ) $ approxEq mag a b+ where+ n2 = totalDim (dims `inSpaceOf` m)+ mag = fromIntegral (n2*n2)+ mm = recip $ maxElem m' `max` 1+ m = ewmap (*mm) m'+ a = det m+ b = det $ transpose m prop_inverse :: SomeSquareMatrix NonSingular TestElem -> Property-prop_inverse (SSM m)- = let mi = inverse m- aeq a b = maxElem (b - a) <= eps * maxRows m- in counterexample ("failed inverse:" ++- show (m, mi, m %* mi, mi %* m)) $- aeq eye (m %* mi) && aeq eye (mi %* m)---- TODO: improve lu!--- lu factorization is very unstable in some cases------ >>> m = unsafeFromFlatList (Dims @'[4,4]) 0 [1,0,1,-2, 0,-1,1,2, 2,-4,2,-2, 4,-8,-2,2 ] :: Mat44f--- >>> det m == 0--- >>> det (transpose m) == 60-prop_LU :: SomeSquareMatrix NonSingular TestElem -> Bool-prop_LU (SSM m)- = let f = lu m- aeq a b = maxElem (b - a) <= eps * maxRows m- in aeq (luPerm f %* m) (luLower f %* luUpper f)+prop_inverse (SSM m) = check (m %* mi) .&&. check (mi %* m)+ where+ n2 = totalDim (dims `inSpaceOf` m)+ mag = maxElem m `max` maxElem mi `max` 1+ * fromIntegral (n2*n2)+ mi = inverse m+ check a = counterexample+ ( unlines+ [ "Failed inverse:"+ , " m: " ++ show m+ , " mi: " ++ show mi+ , " eye': " ++ show a+ ]+ ) $ approxEq mag a eye prop_translate3vs4 :: Vector TestElem 4 -> Bool prop_translate3vs4 v = translate4 v == translate3 (dropW v)@@ -101,92 +66,96 @@ prop_rotateX :: Vector TestElem 4 -> Property prop_rotateX v@(Vec4 x y z w) = conjoin [- v %* rotateX (-2 * pi) `approxEq` v,- v %* rotateX (-1.5 * pi) `approxEq` vec4 x (-z) y w,- v %* rotateX (-pi) `approxEq` vec4 x (-y) (-z) w,- v %* rotateX (-0.5 * pi) `approxEq` vec4 x z (-y) w,- v %* rotateX 0 `approxEq` v,- v %* rotateX (0.5 * pi) `approxEq` vec4 x (-z) y w,- v %* rotateX pi `approxEq` vec4 x (-y) (-z) w,- v %* rotateX (1.5 * pi) `approxEq` vec4 x z (-y) w,- v %* rotateX (2 * pi) `approxEq` v+ v %* rotateX (-2 * pi) =~= v,+ v %* rotateX (-1.5 * pi) =~= vec4 x (-z) y w,+ v %* rotateX (-pi) =~= vec4 x (-y) (-z) w,+ v %* rotateX (-0.5 * pi) =~= vec4 x z (-y) w,+ v %* rotateX 0 =~= v,+ v %* rotateX (0.5 * pi) =~= vec4 x (-z) y w,+ v %* rotateX pi =~= vec4 x (-y) (-z) w,+ v %* rotateX (1.5 * pi) =~= vec4 x z (-y) w,+ v %* rotateX (2 * pi) =~= v ] prop_rotateY :: Vector TestElem 4 -> Property prop_rotateY v@(Vec4 x y z w) = conjoin [- v %* rotateY (-2 * pi) `approxEq` v,- v %* rotateY (-1.5 * pi) `approxEq` vec4 z y (-x) w,- v %* rotateY (-pi) `approxEq` vec4 (-x) y (-z) w,- v %* rotateY (-0.5 * pi) `approxEq` vec4 (-z) y x w,- v %* rotateY 0 `approxEq` v,- v %* rotateY (0.5 * pi) `approxEq` vec4 z y (-x) w,- v %* rotateY pi `approxEq` vec4 (-x) y (-z) w,- v %* rotateY (1.5 * pi) `approxEq` vec4 (-z) y x w,- v %* rotateY (2 * pi) `approxEq` v+ v %* rotateY (-2 * pi) =~= v,+ v %* rotateY (-1.5 * pi) =~= vec4 z y (-x) w,+ v %* rotateY (-pi) =~= vec4 (-x) y (-z) w,+ v %* rotateY (-0.5 * pi) =~= vec4 (-z) y x w,+ v %* rotateY 0 =~= v,+ v %* rotateY (0.5 * pi) =~= vec4 z y (-x) w,+ v %* rotateY pi =~= vec4 (-x) y (-z) w,+ v %* rotateY (1.5 * pi) =~= vec4 (-z) y x w,+ v %* rotateY (2 * pi) =~= v ] prop_rotateZ :: Vector TestElem 4 -> Property prop_rotateZ v@(Vec4 x y z w) = conjoin [- v %* rotateZ (-2 * pi) `approxEq` v,- v %* rotateZ (-1.5 * pi) `approxEq` vec4 (-y) x z w,- v %* rotateZ (-pi) `approxEq` vec4 (-x) (-y) z w,- v %* rotateZ (-0.5 * pi) `approxEq` vec4 y (-x) z w,- v %* rotateZ 0 `approxEq` v,- v %* rotateZ (0.5 * pi) `approxEq` vec4 (-y) x z w,- v %* rotateZ pi `approxEq` vec4 (-x) (-y) z w,- v %* rotateZ (1.5 * pi) `approxEq` vec4 y (-x) z w,- v %* rotateZ (2 * pi) `approxEq` v+ v %* rotateZ (-2 * pi) =~= v,+ v %* rotateZ (-1.5 * pi) =~= vec4 (-y) x z w,+ v %* rotateZ (-pi) =~= vec4 (-x) (-y) z w,+ v %* rotateZ (-0.5 * pi) =~= vec4 y (-x) z w,+ v %* rotateZ 0 =~= v,+ v %* rotateZ (0.5 * pi) =~= vec4 (-y) x z w,+ v %* rotateZ pi =~= vec4 (-x) (-y) z w,+ v %* rotateZ (1.5 * pi) =~= vec4 y (-x) z w,+ v %* rotateZ (2 * pi) =~= v ] prop_rotate :: TestElem -> Property prop_rotate a = conjoin [- rotate (vec3 1 0 0) a `approxEq` rotateX a,- rotate (vec3 0 1 0) a `approxEq` rotateY a,- rotate (vec3 0 0 1) a `approxEq` rotateZ a+ rotate (vec3 1 0 0) a =~= rotateX a,+ rotate (vec3 0 1 0) a =~= rotateY a,+ rotate (vec3 0 0 1) a =~= rotateZ a ] -prop_rotateEuler :: TestElem -> TestElem -> TestElem -> Bool+prop_rotateEuler :: TestElem -> TestElem -> TestElem -> Property prop_rotateEuler pitch yaw roll =- rotateEuler pitch yaw roll `approxEq` rotateZ roll %* rotateY yaw %* rotateX pitch+ rotateEuler pitch yaw roll =~= rotateZ roll %* rotateY yaw %* rotateX pitch prop_lookAt :: Vector TestElem 3 -> Vector TestElem 3 -> Vector TestElem 3 -> Property prop_lookAt up cam foc =- (apart cam foc && apart up cam) ==>+ (apart cam foc && nonColinear up zb && nonColinear zb xb) ==> conjoin [- (normalized . fromHom $ toHomPoint foc %* m) `approxEq` vec3 0 0 (-1),- fromHom (toHomPoint cam %* m) `approxEq` 0,- fromHom (toHomVector xb %* m) `approxEq` vec3 1 0 0,- fromHom (toHomVector yb %* m) `approxEq` vec3 0 1 0,- fromHom (toHomVector zb %* m) `approxEq` vec3 0 0 1+ (normalized . fromHom $ toHomPoint foc %* m) `aeq` vec3 0 0 (-1),+ fromHom (toHomPoint cam %* m) `aeq` 0,+ fromHom (toHomVector xb %* m) `aeq` vec3 1 0 0,+ fromHom (toHomVector yb %* m) `aeq` vec3 0 1 0,+ fromHom (toHomVector zb %* m) `aeq` vec3 0 0 1 ] where+ aeq :: Vector TestElem 3 -> Vector TestElem 3 -> Property+ aeq = approxEq 10 apart :: Vector TestElem 3 -> Vector TestElem 3 -> Bool- apart a b = maxElem (a - b) > 0.01 * (maxElem a `max` maxElem b)+ apart a b = normL2 (a - b) > max (2 * sqrt M_EPS) (0.1 * (normL2 a + normL2 b))+ nonColinear :: Vector TestElem 3 -> Vector TestElem 3 -> Bool+ nonColinear a b = normL2 (cross a b) > 0.1 * abs (dot a b) m = lookAt up cam foc zb = normalized $ cam - foc xb = normalized $ up `cross` zb yb = zb `cross` xb -prop_perspective :: TestElem -> TestElem -> TestElem -> TestElem -> Bool+prop_perspective :: TestElem -> TestElem -> TestElem -> TestElem -> Property prop_perspective a b c d =- and [- projectTo 0 0 n `approxEq` vec3 0 0 (-1),- projectTo 0 0 f `approxEq` vec3 0 0 1,- projectTo 1 1 n `approxEq` vec3 1 1 (-1),- projectTo 1 (-1) n `approxEq` vec3 1 (-1) (-1),- projectTo (-1) 1 n `approxEq` vec3 (-1) 1 (-1),- projectTo (-1) (-1) n `approxEq` vec3 (-1) (-1) (-1),- projectTo 1 1 f `approxEq` vec3 1 1 1,- projectTo 1 (-1) f `approxEq` vec3 1 (-1) 1,- projectTo (-1) 1 f `approxEq` vec3 (-1) 1 1,- projectTo (-1) (-1) f `approxEq` vec3 (-1) (-1) 1+ conjoin [+ projectTo 0 0 n =~= vec3 0 0 (-1),+ projectTo 0 0 f =~= vec3 0 0 1,+ projectTo 1 1 n =~= vec3 1 1 (-1),+ projectTo 1 (-1) n =~= vec3 1 (-1) (-1),+ projectTo (-1) 1 n =~= vec3 (-1) 1 (-1),+ projectTo (-1) (-1) n =~= vec3 (-1) (-1) (-1),+ projectTo 1 1 f =~= vec3 1 1 1,+ projectTo 1 (-1) f =~= vec3 1 (-1) 1,+ projectTo (-1) 1 f =~= vec3 (-1) 1 1,+ projectTo (-1) (-1) f =~= vec3 (-1) (-1) 1 ] where n = 1.0 + mod' a 9.0 -- Near plane in range [1, 10)- f = n + 1.0 + mod' b 99.0 -- Far plane in range [n + 1, n + 100)+ f = 2*n + mod' b 99.0 -- Far plane in range [2*n, 2*n + 100) fovy = (0.1 * pi) + mod' c (0.8 * pi) -- Y-axis field of view in range [0.1*pi, 0.9*pi) aspect = 0.25 + mod' d 4.0 -- Aspect ration in range [1/4, 4/1] hpd = tan (fovy * 0.5) -- height/distance@@ -194,23 +163,23 @@ m = perspective n f fovy aspect projectTo x' y' z = fromHom $ vec4 (x' * wpd * z) (y' * hpd * z) (-z) 1 %* m -prop_orthogonal :: TestElem -> TestElem -> TestElem -> TestElem -> Bool+prop_orthogonal :: TestElem -> TestElem -> TestElem -> TestElem -> Property prop_orthogonal a b c d =- and [- projectTo 0 0 n `approxEq` vec3 0 0 (-1),- projectTo 0 0 f `approxEq` vec3 0 0 1,- projectTo 1 1 n `approxEq` vec3 1 1 (-1),- projectTo 1 (-1) n `approxEq` vec3 1 (-1) (-1),- projectTo (-1) 1 n `approxEq` vec3 (-1) 1 (-1),- projectTo (-1) (-1) n `approxEq` vec3 (-1) (-1) (-1),- projectTo 1 1 f `approxEq` vec3 1 1 1,- projectTo 1 (-1) f `approxEq` vec3 1 (-1) 1,- projectTo (-1) 1 f `approxEq` vec3 (-1) 1 1,- projectTo (-1) (-1) f `approxEq` vec3 (-1) (-1) 1+ conjoin [+ projectTo 0 0 n =~= vec3 0 0 (-1),+ projectTo 0 0 f =~= vec3 0 0 1,+ projectTo 1 1 n =~= vec3 1 1 (-1),+ projectTo 1 (-1) n =~= vec3 1 (-1) (-1),+ projectTo (-1) 1 n =~= vec3 (-1) 1 (-1),+ projectTo (-1) (-1) n =~= vec3 (-1) (-1) (-1),+ projectTo 1 1 f =~= vec3 1 1 1,+ projectTo 1 (-1) f =~= vec3 1 (-1) 1,+ projectTo (-1) 1 f =~= vec3 (-1) 1 1,+ projectTo (-1) (-1) f =~= vec3 (-1) (-1) 1 ] where n = 1.0 + mod' a 9.0 -- Near plane in range [1, 10)- f = n + 1.0 + mod' b 99.0 -- Far plane in range [n + 1, n + 100)+ f = 2*n + mod' b 99.0 -- Far plane in range [2*n, 2*n + 100) w = 1.0 + mod' c 9999.0 -- Width in range [1, 10000) h = 1.0 + mod' d 9999.0 -- Height in range [1, 10000) m = orthogonal n f w h@@ -222,11 +191,11 @@ prop_toHomVector :: Vector TestElem 3 -> Bool prop_toHomVector v@(Vec3 x y z) = toHomVector v == vec4 x y z 0 -prop_fromHom :: Vector TestElem 4 -> Bool+prop_fromHom :: Vector TestElem 4 -> Property prop_fromHom v@(Vec4 x y z w) = case w of- 0 -> fromHom v == vec3 x y z- _ -> fromHom v `approxEq` vec3 (x/w) (y/w) (z/w)+ 0 -> fromHom v === vec3 x y z+ _ -> fromHom v =~= vec3 (x/w) (y/w) (z/w) return [] runTests :: Int -> IO Bool
test/Numeric/PrimBytesTest.hs view
@@ -28,11 +28,11 @@ import GHC.Exts import GHC.Generics import GHC.Word-import Numeric.DataFrame.Arbitraries ()+import Numeric.Arbitraries () import Numeric.Dimensions import Numeric.PrimBytes-import qualified Numeric.Tuple.Lazy as LT-import qualified Numeric.Tuple.Strict as ST+import qualified Numeric.Tuple.Lazy as LT+import qualified Numeric.Tuple.Strict as ST import Spec.Util import Test.QuickCheck
test/Numeric/QuaterDoubleTest.hs view
@@ -2,42 +2,30 @@ {-# LANGUAGE TemplateHaskell #-} module Numeric.QuaterDoubleTest (runTests) where -import Numeric.DataFrame.Arbitraries ()+import Numeric.Arbitraries+import Numeric.Basics import Numeric.Quaternion-import Numeric.Scalar import Numeric.Vector import Test.QuickCheck type T = Double -eps :: T-eps = 1e-7- -- | Some non-linear function are very unstable; -- it would be a downting task to determine the uncertainty precisely.--- Instead, I just make sure the numbers are not so big to find at least+-- Instead, I just make sure the tolerance is small enough to find at least -- the most obvious bugs.-smaller :: Quater T -> Quater T-smaller q@(Quater x y z w)- = if square q >= 1 then Quater (f x) (f y) (f z) (f w) else q+-- This function increases the tolerance by the span of magnitudes in q.+qSpan :: Quater T -> T+qSpan (Quater a b c d) = asSpan . foldl mm (1,1) $ map (\x -> x*x) [a, b, c, d] where- f :: T -> T- f a | abs a > 2 = signum a * log (abs a)- | otherwise = a+ mm :: (T,T) -> T -> (T,T)+ mm (mi, ma) x+ | x > M_EPS = (min mi x, max ma x)+ | otherwise = (mi, ma)+ asSpan :: (T,T) -> T+ asSpan (mi, ma) = ma / mi -(=~=) :: Quater T -> Quater T -> Property-(=~=) = approxEq eps-infix 4 =~= --- Even lower precision - for naturally fragile operations-approxEq :: T -> Quater T -> Quater T -> Property-approxEq e a b = property $ err <= c * scalar e- where- v1 = toVec4 a- v2 = toVec4 b- c = 1 `max` normL1 v1 `max` normL1 v2- err = normL1 $ v1 - v2- prop_Eq :: Quater T -> Bool prop_Eq q = and [ q == q@@ -58,19 +46,20 @@ prop_GetRotScale :: Vector T 3 -> Vector T 3 -> Property prop_GetRotScale a b- = a /= 0 ==> approxEq eps' (fromVecNum (rotScale q a) 0) (fromVecNum b 0)+ = normL2 a * ab > M_EPS * normL2 b+ ==> approxEq (recip ab) b (rotScale q a) where q = getRotScale a b -- when a and b are almost opposite, precision of getRotScale suffers a lot- -- compensate it by increasing eps:- eps' = if cross a b == 0- then eps- else eps / (max eps . min 1 . unScalar $- 1 + normalized a `dot` normalized b)+ -- compensate it by increasing tolerance:+ ab = min 1 $ 1 + normalized a `dot` normalized b + prop_InverseRotScale :: Quater T -> Vector T 3 -> Property prop_InverseRotScale q v- = square q > eps ==> fromVecNum (rotScale (1/q) (rotScale q v)) 0 =~= fromVecNum v 0+ = min (recip s) s > M_EPS ==> v =~= rotScale (1/q) (rotScale q v)+ where+ s = square q prop_NegateToMatrix33 :: Quater T -> Bool prop_NegateToMatrix33 q = toMatrix33 q == toMatrix33 (negate q)@@ -95,53 +84,59 @@ prop_UnitQ :: Quater T -> Property prop_UnitQ q- = square q > eps ==> realToFrac (square (q / q)) =~= 1+ = square q > M_EPS ==> square (q / q) =~= 1 prop_ExpLog :: Quater T -> Property-prop_ExpLog q | square q < eps = log (exp q) =~= q- | otherwise = exp (log q) =~= q+prop_ExpLog q | square q < M_EPS = approxEq (qSpan q) q $ log (exp q)+ | otherwise = approxEq (qSpan q) q $ exp (log q) prop_SinAsin :: Quater T -> Property-prop_SinAsin q = sin (asin q') =~= q'- where- q' = signum q+prop_SinAsin q = approxEq (qSpan q `max` qSpan q') q $ sin q'+ where+ q' = asin q prop_CosAcos :: Quater T -> Property-prop_CosAcos q = cos (acos q') =~= q'- where- q' = signum q+prop_CosAcos q = approxEq (qSpan q `max` qSpan q') q $ cos q'+ where+ q' = acos q prop_TanAtan :: Quater T -> Property-prop_TanAtan q = tan (atan q') =~= q'- where- q' = sqrt (smaller q) -- because this inverse fun is fragile+prop_TanAtan q = approxEq (qSpan q `max` qSpan q') q $ tan q'+ where+ q' = atan q prop_SinhAsinh :: Quater T -> Property-prop_SinhAsinh q = sinh (asinh q') =~= q'- where- q' = sqrt (smaller q) -- because this inverse fun is fragile+prop_SinhAsinh q = approxEq (qSpan q `max` qSpan q') q $ sinh q'+ where+ q' = asinh q prop_CoshAcosh :: Quater T -> Property-prop_CoshAcosh q = cosh (acosh q) =~= q+prop_CoshAcosh q = approxEq (qSpan q `max` qSpan q') q $ cosh q'+ where+ q' = acosh q prop_TanhAtanh :: Quater T -> Property-prop_TanhAtanh q = tanh (atanh q') =~= q'- where- q' = sqrt (smaller q) -- because this inverse fun is fragile+prop_TanhAtanh q = approxEq (qSpan q `max` qSpan q') q $ tanh q'+ where+ q' = atanh q prop_SqrtSqr :: Quater T -> Property-prop_SqrtSqr q = sqrt q * sqrt q =~= q+prop_SqrtSqr q = approxEq (qSpan q) q $ sqrt q * sqrt q prop_SinCos :: Quater T -> Property-prop_SinCos q = sin q' * sin q' + cos q' * cos q' =~= 1- where- q' = sqrt (smaller q) -- because it involves exponents+prop_SinCos q' = approxEq (qSpan s `max` qSpan c) 1 $ c * c + s * s+ where+ q = signum q' -- avoid exploding exponents+ s = sin q+ c = cos q prop_SinhCosh :: Quater T -> Property-prop_SinhCosh q = cosh q' * cosh q' - sinh q' * sinh q' =~= 1- where- q' = sqrt (smaller q) -- because it involves exponents+prop_SinhCosh q' = approxEq (qSpan s `max` qSpan c) 1 $ c * c - s * s+ where+ q = signum q' -- avoid exploding exponents+ s = sinh q+ c = cosh q prop_ReadShow :: Quater T -> Bool prop_ReadShow q = q == read (show q)
test/Numeric/QuaterFloatTest.hs view
@@ -2,42 +2,30 @@ {-# LANGUAGE TemplateHaskell #-} module Numeric.QuaterFloatTest (runTests) where -import Numeric.DataFrame.Arbitraries ()+import Numeric.Arbitraries+import Numeric.Basics import Numeric.Quaternion-import Numeric.Scalar import Numeric.Vector import Test.QuickCheck type T = Float -eps :: T-eps = 1e-3- -- | Some non-linear function are very unstable; -- it would be a downting task to determine the uncertainty precisely.--- Instead, I just make sure the numbers are not so big to find at least+-- Instead, I just make sure the tolerance is small enough to find at least -- the most obvious bugs.-smaller :: Quater T -> Quater T-smaller q@(Quater x y z w)- = if square q >= 1 then Quater (f x) (f y) (f z) (f w) else q+-- This function increases the tolerance by the span of magnitudes in q.+qSpan :: Quater T -> T+qSpan (Quater a b c d) = asSpan . foldl mm (1,1) $ map (\x -> x*x) [a, b, c, d] where- f :: T -> T- f a | abs a > 2 = signum a * log (abs a)- | otherwise = a+ mm :: (T,T) -> T -> (T,T)+ mm (mi, ma) x+ | x > M_EPS = (min mi x, max ma x)+ | otherwise = (mi, ma)+ asSpan :: (T,T) -> T+ asSpan (mi, ma) = ma / mi -(=~=) :: Quater T -> Quater T -> Property-(=~=) = approxEq eps-infix 4 =~= --- Even lower precision - for naturally fragile operations-approxEq :: T -> Quater T -> Quater T -> Property-approxEq e a b = property $ err <= c * scalar e- where- v1 = toVec4 a- v2 = toVec4 b- c = 1 `max` normL1 v1 `max` normL1 v2- err = normL1 $ v1 - v2- prop_Eq :: Quater T -> Bool prop_Eq q = and [ q == q@@ -58,19 +46,20 @@ prop_GetRotScale :: Vector T 3 -> Vector T 3 -> Property prop_GetRotScale a b- = a /= 0 ==> approxEq eps' (fromVecNum (rotScale q a) 0) (fromVecNum b 0)+ = normL2 a * ab > M_EPS * normL2 b+ ==> approxEq (recip ab) b (rotScale q a) where q = getRotScale a b -- when a and b are almost opposite, precision of getRotScale suffers a lot- -- compensate it by increasing eps:- eps' = if cross a b == 0- then eps- else eps / (max eps . min 1 . unScalar $- 1 + normalized a `dot` normalized b)+ -- compensate it by increasing tolerance:+ ab = min 1 $ 1 + normalized a `dot` normalized b + prop_InverseRotScale :: Quater T -> Vector T 3 -> Property prop_InverseRotScale q v- = square q > eps ==> fromVecNum (rotScale (1/q) (rotScale q v)) 0 =~= fromVecNum v 0+ = min (recip s) s > M_EPS ==> v =~= rotScale (1/q) (rotScale q v)+ where+ s = square q prop_NegateToMatrix33 :: Quater T -> Bool prop_NegateToMatrix33 q = toMatrix33 q == toMatrix33 (negate q)@@ -95,53 +84,59 @@ prop_UnitQ :: Quater T -> Property prop_UnitQ q- = square q > eps ==> realToFrac (square (q / q)) =~= 1+ = square q > M_EPS ==> square (q / q) =~= 1 prop_ExpLog :: Quater T -> Property-prop_ExpLog q | square q < eps = log (exp q) =~= q- | otherwise = exp (log q) =~= q+prop_ExpLog q | square q < M_EPS = approxEq (qSpan q) q $ log (exp q)+ | otherwise = approxEq (qSpan q) q $ exp (log q) prop_SinAsin :: Quater T -> Property-prop_SinAsin q = sin (asin q') =~= q'- where- q' = signum q+prop_SinAsin q = approxEq (qSpan q `max` qSpan q') q $ sin q'+ where+ q' = asin q prop_CosAcos :: Quater T -> Property-prop_CosAcos q = cos (acos q') =~= q'- where- q' = signum q+prop_CosAcos q = approxEq (qSpan q `max` qSpan q') q $ cos q'+ where+ q' = acos q prop_TanAtan :: Quater T -> Property-prop_TanAtan q = tan (atan q') =~= q'- where- q' = sqrt (smaller q) -- because this inverse fun is fragile+prop_TanAtan q = approxEq (qSpan q `max` qSpan q') q $ tan q'+ where+ q' = atan q prop_SinhAsinh :: Quater T -> Property-prop_SinhAsinh q = sinh (asinh q') =~= q'- where- q' = sqrt (smaller q) -- because this inverse fun is fragile+prop_SinhAsinh q = approxEq (qSpan q `max` qSpan q') q $ sinh q'+ where+ q' = asinh q prop_CoshAcosh :: Quater T -> Property-prop_CoshAcosh q = cosh (acosh q) =~= q+prop_CoshAcosh q = approxEq (qSpan q `max` qSpan q') q $ cosh q'+ where+ q' = acosh q prop_TanhAtanh :: Quater T -> Property-prop_TanhAtanh q = tanh (atanh q') =~= q'- where- q' = sqrt (smaller q) -- because this inverse fun is fragile+prop_TanhAtanh q = approxEq (qSpan q `max` qSpan q') q $ tanh q'+ where+ q' = atanh q prop_SqrtSqr :: Quater T -> Property-prop_SqrtSqr q = sqrt q * sqrt q =~= q+prop_SqrtSqr q = approxEq (qSpan q) q $ sqrt q * sqrt q prop_SinCos :: Quater T -> Property-prop_SinCos q = sin q' * sin q' + cos q' * cos q' =~= 1- where- q' = sqrt (smaller q) -- because it involves exponents+prop_SinCos q' = approxEq (qSpan s `max` qSpan c) 1 $ c * c + s * s+ where+ q = signum q' -- avoid exploding exponents+ s = sin q+ c = cos q prop_SinhCosh :: Quater T -> Property-prop_SinhCosh q = cosh q' * cosh q' - sinh q' * sinh q' =~= 1- where- q' = sqrt (smaller q) -- because it involves exponents+prop_SinhCosh q' = approxEq (qSpan s `max` qSpan c) 1 $ c * c - s * s+ where+ q = signum q' -- avoid exploding exponents+ s = sinh q+ c = cosh q prop_ReadShow :: Quater T -> Bool prop_ReadShow q = q == read (show q)
+ test/Numeric/Subroutine/SolveTriangularTest.hs view
@@ -0,0 +1,249 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++module Numeric.Subroutine.SolveTriangularTest (runTests) where+++import Control.Monad.ST+import Numeric.Arbitraries+import Numeric.DataFrame+import Numeric.DataFrame.ST+import Numeric.Dimensions+import Numeric.Subroutine.SolveTriangular+import Test.QuickCheck+++arbitraryTriangular ::+ forall (n :: Nat) (m :: Nat)+ . (KnownDim n, KnownDim m, KnownBackend Double '[m])+ => Bool -> Gen (DataFrame Double '[n,m])+arbitraryTriangular upper = iwmap f <$> arbitrary+ where+ f :: Idxs '[n] -> DataFrame Double '[m] -> DataFrame Double '[m]+ f (Idx i :* U) x = iwmap g x+ where+ a = maxElem x+ g :: Idxs '[m] -> Scalar Double -> Scalar Double+ g (Idx j :* U) c+ | if upper then j < i else j > i = 0+ | j == i = if c >= 0 then c + 1 + a else c - 1 - a+ | otherwise = c++-- | Test Rx = b+testSolveUpperTriangularR ::+ forall (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (KnownDim n, KnownDim m, m <= n, Dimensions ds)+ => DataFrame Double '[n,m] -- ^ R+ -> DataFrame Double (n :+ ds) -- ^ b+ -> Property+testSolveUpperTriangularR r b+ | dn <- dim @n+ , dm <- dim @m+ , di@D <- dn `minusDim` dm `plusDim` D1+ , i0 <- (Idx 0 :* U) `inSpaceOf` (di :* U)+ , Just Dict <- sameDim (plusDim dn D1) (plusDim di dm)+ = let bm :: DataFrame Double (m :+ ds)+ bm = slice i0 b+ x :: DataFrame Double (m :+ ds)+ x = runST $ do+ xPtr <- thawDataFrame bm+ solveUpperTriangularR r xPtr+ unsafeFreezeDataFrame xPtr+ in counterexample+ (unlines+ [ "failed Rx = b:"+ , "R: " ++ show r+ , "x: " ++ show x+ , "b: " ++ show b+ , "Rx:" ++ show (r %* x)+ ]+ ) (approxEq (maxElem r) b (r %* x))+testSolveUpperTriangularR _ _ = error "impossible pattern"++prop_SolveUpperTriangularR :: Property+prop_SolveUpperTriangularR = property run+ where+ run :: Gen Property+ run = do+ Dx (m@D :: Dim m) <- arbitrary :: Gen (Dim (XN 1))+ Dx (D :: Dim n) <- arbitrary :: Gen (Dim (XN m))+ justVec <- arbitrary :: Gen Bool+ SomeDims (Dims :: Dims ds)+ <- if justVec+ then pure (SomeDims U)+ else removeDimsAbove 100 <$> arbitrary+ r <- arbitraryTriangular @n @m True+ b' <- arbitrary @(DataFrame Double (n :+ ds))+ let b = iwmap @_ @(n ': ds) @'[]+ (\(Idx i :* _) x -> if i >= dimVal m then 0 else x) b'+ return $ testSolveUpperTriangularR r b++-- | Test xR = b+testSolveUpperTriangularL ::+ forall (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (KnownDim n, KnownDim m, m <= n, Dimensions ds)+ => DataFrame Double (ds +: m) -- ^ b+ -> DataFrame Double '[n,m] -- ^ r+ -> Property+testSolveUpperTriangularL b r+ | dn <- dim @n+ , dm <- dim @m+ , dnm@D <- minusDim dn dm+ , Dims <- Snoc (dims @ds) dn+ , Dims <- Snoc (dims @ds) dm+ , Dict <- Dict @(SnocList ds n _)+ , Dict <- Dict @(SnocList ds m _)+ , Just Dict <- sameDim dn (plusDim dm dnm)+ = let padZeroes :: DataFrame Double (ds +: m) -> DataFrame Double (ds +: n)+ padZeroes = ewmap @_ @ds @'[n] $ \a -> appendDF a (0 `inSpaceOf` (dnm :* U))+ x :: DataFrame Double (ds +: n)+ x = runST $ do+ xPtr <- thawDataFrame b+ solveUpperTriangularL xPtr r+ padZeroes <$> unsafeFreezeDataFrame xPtr+ in counterexample+ (unlines+ [ "failed xR = b:"+ , "R: " ++ show r+ , "x: " ++ show x+ , "b: " ++ show b+ , "xR:" ++ show (x %* r)+ ]+ ) (approxEq (maxElem r) b (x %* r))+testSolveUpperTriangularL _ _ = error "impossible pattern"++prop_SolveUpperTriangularL :: Property+prop_SolveUpperTriangularL = property run+ where+ run :: Gen Property+ run = do+ Dx (m@D :: Dim m) <- arbitrary :: Gen (Dim (XN 1))+ Dx (D :: Dim n) <- arbitrary :: Gen (Dim (XN m))+ justVec <- arbitrary :: Gen Bool+ SomeDims (ds@Dims :: Dims ds)+ <- if justVec+ then pure (SomeDims U)+ else removeDimsAbove 100 <$> arbitrary+ Dims <- pure $ Snoc ds m+ r <- arbitraryTriangular @n @m True+ b <- arbitrary @(DataFrame Double (ds +: m))+ return $ testSolveUpperTriangularL b r+++-- | Test Lx = b+testSolveLowerTriangularR ::+ forall (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (KnownDim n, KnownDim m, n <= m, Dimensions ds)+ => DataFrame Double '[n,m] -- ^ L+ -> DataFrame Double (n :+ ds) -- ^ b+ -> Property+testSolveLowerTriangularR l b+ | dn <- dim @n+ , dm <- dim @m+ , dmn@D <- dm `minusDim` dn+ , Just Dict <- sameDim dm (dn `plusDim` dmn)+ = let padZeroes :: DataFrame Double (n :+ ds) -> DataFrame Double (m :+ ds)+ padZeroes z = appendDF z (0 :: DataFrame Double ((m-n) :+ ds))+ x :: DataFrame Double (m :+ ds)+ x = runST $ do+ xPtr <- thawDataFrame b+ solveLowerTriangularR l xPtr+ padZeroes <$> unsafeFreezeDataFrame xPtr+ in counterexample+ (unlines+ [ "failed Lx = b:"+ , "L: " ++ show l+ , "x: " ++ show x+ , "b: " ++ show b+ , "Lx:" ++ show (l %* x)+ ]+ ) (approxEq (maxElem l) b (l %* x))+testSolveLowerTriangularR _ _ = error "impossible pattern"++prop_SolveLowerTriangularR :: Property+prop_SolveLowerTriangularR = property run+ where+ run :: Gen Property+ run = do+ Dx (D :: Dim n) <- arbitrary :: Gen (Dim (XN 1))+ Dx (D :: Dim m) <- arbitrary :: Gen (Dim (XN n))+ justVec <- arbitrary :: Gen Bool+ SomeDims (Dims :: Dims ds)+ <- if justVec+ then pure (SomeDims U)+ else removeDimsAbove 100 <$> arbitrary+ l <- arbitraryTriangular @n @m False+ b <- arbitrary @(DataFrame Double (n :+ ds))+ return $ testSolveLowerTriangularR l b+++-- | Test xL = b+testSolveLowerTriangularL ::+ forall (n :: Nat) (m :: Nat) (ds :: [Nat])+ . (KnownDim n, KnownDim m, n <= m, Dimensions ds)+ => DataFrame Double (ds +: m) -- ^ b+ -> DataFrame Double '[n,m] -- ^ L+ -> Property+testSolveLowerTriangularL b l+ | dn <- dim @n+ , dm <- dim @m+ , dmn@D <- minusDim dm dn+ , Dims <- Snoc (dims @ds) dn+ , Dims <- Snoc (dims @ds) dm+ , Dict <- Dict @(SnocList ds n _)+ , Dict <- Dict @(SnocList ds m _)+ , Just Dict <- sameDim dm (plusDim dn dmn)+ , di@D <- dm `minusDim` dn `plusDim` D1+ , i0 <- (Idx 0 :* U) `inSpaceOf` (di :* U)+ , Just Dict <- sameDim (plusDim dm D1) (plusDim di dn)+ = let dropLast :: DataFrame Double (ds +: m) -> DataFrame Double (ds +: n)+ dropLast = ewmap @_ @ds @'[n] $ slice i0+ x :: DataFrame Double (ds +: n)+ x = runST $ do+ xPtr <- thawDataFrame b+ solveLowerTriangularL xPtr l+ dropLast <$> unsafeFreezeDataFrame xPtr+ in counterexample+ (unlines+ [ "failed xL = b:"+ , "R: " ++ show l+ , "x: " ++ show x+ , "b: " ++ show b+ , "xL:" ++ show (x %* l)+ ]+ ) (approxEq (maxElem l) b (x %* l))+testSolveLowerTriangularL _ _ = error "impossible pattern"++prop_SolveLowerTriangularL :: Property+prop_SolveLowerTriangularL = property run+ where+ run :: Gen Property+ run = do+ Dx (n@D :: Dim n) <- arbitrary :: Gen (Dim (XN 1))+ Dx (m@D :: Dim m) <- arbitrary :: Gen (Dim (XN n))+ justVec <- arbitrary :: Gen Bool+ SomeDims (ds@Dims :: Dims ds)+ <- if justVec+ then pure (SomeDims U)+ else removeDimsAbove 100 <$> arbitrary+ Dims <- pure $ Snoc ds m+ Dict <- pure $ Dict @(SnocList ds m _)+ l <- arbitraryTriangular @n @m False+ b' <- arbitrary @(DataFrame Double (ds +: m))+ let b = ewmap @_ @ds @'[m]+ (iwmap @_ @'[m] @'[]+ (\(Idx i :* _) x -> if i >= dimVal n then 0 else x)+ ) b'+ return $ testSolveLowerTriangularL b l++return []+runTests :: Int -> IO Bool+runTests n = $forAllProperties+ $ quickCheckWithResult stdArgs { maxSuccess = n }
test/Spec.hs view
@@ -5,11 +5,16 @@ import qualified Numeric.DataFrame.BasicTest import qualified Numeric.DataFrame.SubSpaceTest+import qualified Numeric.Matrix.BidiagonalTest+import qualified Numeric.Matrix.LUTest+import qualified Numeric.Matrix.QRTest+import qualified Numeric.Matrix.SVDTest import qualified Numeric.MatrixDoubleTest import qualified Numeric.MatrixFloatTest import qualified Numeric.PrimBytesTest import qualified Numeric.QuaterDoubleTest import qualified Numeric.QuaterFloatTest+import qualified Numeric.Subroutine.SolveTriangularTest -- | Collection of tests in detailed-0.9 format@@ -22,9 +27,15 @@ , test "QuaterDouble" $ Numeric.QuaterDoubleTest.runTests n , test "QuaterFloat" $ Numeric.QuaterFloatTest.runTests n , test "PrimBytes" $ Numeric.PrimBytesTest.runTests n+ , test "Matrix.LU" $ Numeric.Matrix.LUTest.runTests n+ , test "Matrix.QR" $ Numeric.Matrix.QRTest.runTests n+ , test "Matrix.Bidiagonal" $ Numeric.Matrix.BidiagonalTest.runTests n+ , test "Matrix.SVD" $ Numeric.Matrix.SVDTest.runTests n+ , test "Subroutine.SolveTriangular"+ $ Numeric.Subroutine.SolveTriangularTest.runTests n ] where- n = 1000 :: Int+ n = 1000 :: Int