moonlight-linalg-0.1.0.0: src-laws/Moonlight/LinAlg/Effect/Harness/Sparse.hs
module Moonlight.LinAlg.Effect.Harness.Sparse
( cooCsrRoundTripLaw,
cooCscRoundTripLaw,
csrCscTransposeAgreementLaw,
csrMatVecAgreesWithDenseLaw,
canonicalCsrCombinesDuplicatesLaw,
graphLaplacianSymmetricRowSumsZeroLaw,
selfAdjointCsrRejectsAsymmetryLaw,
)
where
import Data.Vector.Unboxed qualified as U
import Moonlight.LinAlg
( GraphEdge (..),
canonicalCSRFromEntries,
cooToCSC,
cooToCSR,
cooToDense,
cscToCSR,
cscToDense,
csrMatVecVector,
csrToCSC,
csrToDense,
fromListMatrix,
graphLaplacianCSR,
mkSparseCOO,
selfAdjointCSRLinearOperator,
toListMatrix,
)
import Moonlight.LinAlg.Effect.Harness.Core (assertApproxList, assertRightProperty, matrix3VectorProduct)
import Test.Tasty.QuickCheck qualified as QC
newtype SparseEntries3 = SparseEntries3 [(Int, Int, Double)]
deriving stock (Eq, Show)
newtype DenseVector3 = DenseVector3 [Double]
deriving stock (Eq, Show)
instance QC.Arbitrary SparseEntries3 where
arbitrary =
SparseEntries3
<$> QC.listOf
( (,,)
<$> QC.chooseInt (0, 2)
<*> QC.chooseInt (0, 2)
<*> (fromIntegral <$> QC.chooseInt (-5, 5))
)
instance QC.Arbitrary DenseVector3 where
arbitrary =
DenseVector3
<$> QC.vectorOf 3 (fromIntegral <$> QC.chooseInt (-5, 5))
cooCsrRoundTripLaw :: QC.Property
cooCsrRoundTripLaw =
QC.property cooCsrRoundTripLawProperty
cooCscRoundTripLaw :: QC.Property
cooCscRoundTripLaw =
QC.property cooCscRoundTripLawProperty
csrCscTransposeAgreementLaw :: QC.Property
csrCscTransposeAgreementLaw =
QC.property csrCscTransposeAgreementLawProperty
csrMatVecAgreesWithDenseLaw :: QC.Property
csrMatVecAgreesWithDenseLaw =
QC.property csrMatVecAgreesWithDenseLawProperty
cooCsrRoundTripLawProperty :: SparseEntries3 -> QC.Property
cooCsrRoundTripLawProperty (SparseEntries3 entries) =
assertRightProperty $ do
cooValue <- mkSparseCOO 3 3 entries
csrValue <- cooToCSR cooValue
originalDense <- cooToDense @3 @3 cooValue
roundTripDense <- csrToDense @3 @3 csrValue
pure (toListMatrix originalDense == toListMatrix roundTripDense)
cooCscRoundTripLawProperty :: SparseEntries3 -> QC.Property
cooCscRoundTripLawProperty (SparseEntries3 entries) =
assertRightProperty $ do
cooValue <- mkSparseCOO 3 3 entries
cscValue <- cooToCSC cooValue
originalDense <- cooToDense @3 @3 cooValue
roundTripDense <- cscToDense @3 @3 cscValue
pure (toListMatrix originalDense == toListMatrix roundTripDense)
csrCscTransposeAgreementLawProperty :: SparseEntries3 -> QC.Property
csrCscTransposeAgreementLawProperty (SparseEntries3 entries) =
assertRightProperty $ do
cooValue <- mkSparseCOO 3 3 entries
csrValue <- cooToCSR cooValue
cscValue <- csrToCSC csrValue
csrRoundTrip <- cscToCSR cscValue
originalDense <- csrToDense @3 @3 csrValue
roundTripDense <- csrToDense @3 @3 csrRoundTrip
pure (toListMatrix originalDense == toListMatrix roundTripDense)
csrMatVecAgreesWithDenseLawProperty :: SparseEntries3 -> DenseVector3 -> QC.Property
csrMatVecAgreesWithDenseLawProperty (SparseEntries3 entries) (DenseVector3 vectorEntries) =
assertRightProperty $ do
cooValue <- mkSparseCOO 3 3 entries
csrValue <- cooToCSR cooValue
denseMatrix <- csrToDense @3 @3 csrValue
csrProduct <- csrMatVecVector csrValue (U.fromList vectorEntries)
pure (assertApproxList (matrix3VectorProduct (rows3 (toListMatrix denseMatrix)) vectorEntries) (U.toList csrProduct))
canonicalCsrCombinesDuplicatesLaw :: QC.Property
canonicalCsrCombinesDuplicatesLaw =
assertRightProperty $ do
csrValue <-
canonicalCSRFromEntries
2
3
([(0, 1, 2.0), (0, 1, 3.0), (0, 2, 0.0), (1, 0, 5.0), (1, 0, -5.0), (1, 2, 4.0)] :: [(Int, Int, Double)])
denseMatrix <- csrToDense @2 @3 csrValue
pure (toListMatrix denseMatrix == [0.0, 5.0, 0.0, 0.0, 0.0, 4.0])
graphLaplacianSymmetricRowSumsZeroLaw :: QC.Property
graphLaplacianSymmetricRowSumsZeroLaw =
assertRightProperty $ do
csrValue <-
graphLaplacianCSR
["a", "b", "c"]
[GraphEdge "a" "b" 1.0, GraphEdge "b" "c" 2.0, GraphEdge "a" "c" 3.0]
denseMatrix <- csrToDense @3 @3 csrValue
let rowsValue = rows3 (toListMatrix denseMatrix)
pure (symmetricRows rowsValue && all (\rowValue -> assertApproxList [0.0] [sum rowValue]) rowsValue)
selfAdjointCsrRejectsAsymmetryLaw :: QC.Property
selfAdjointCsrRejectsAsymmetryLaw =
assertRightProperty $ do
matrixValue <- fromListMatrix @3 @3 ([0.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0] :: [Double])
let csrValue = cooToCSR =<< mkSparseCOO 3 3 [(0, 1, 1.0), (1, 2, 2.0)]
directValue = case csrValue of
Left _ -> False
Right value -> case selfAdjointCSRLinearOperator value of
Left _ -> True
Right _ -> False
pure (directValue && toListMatrix matrixValue == [0.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0])
rows3 :: [a] -> [[a]]
rows3 values =
case values of
[a00, a01, a02, a10, a11, a12, a20, a21, a22] ->
[[a00, a01, a02], [a10, a11, a12], [a20, a21, a22]]
_ -> []
symmetricRows :: [[Double]] -> Bool
symmetricRows rowsValue =
case rowsValue of
[[a00, a01, a02], [a10, a11, a12], [a20, a21, a22]] ->
and
[ a00 == a00,
assertApproxList [a01] [a10],
assertApproxList [a02] [a20],
a11 == a11,
assertApproxList [a12] [a21],
a22 == a22
]
_ -> False