easytensor-2.1.1.0: test/Numeric/Arbitraries.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# 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
-- See dimensions:Numeric.Dimensions.Dim
#define IS_UNSOUND_MATCHING_810_900 (MIN_VERSION_GLASGOW_HASKELL(8,10,0,0) && !MIN_VERSION_GLASGOW_HASKELL(9,1,0,0))
-- | 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)
#if IS_UNSOUND_MATCHING_810_900
_ -> error "Numeric.Arbitraries.Arbitrary.arbitratry/ds: impossible pattern"
#endif
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)
#if IS_UNSOUND_MATCHING_810_900
_ -> error "Numeric.Arbitraries.Arbitrary.arbitratry/ds: impossible pattern"
#endif
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 _ = []