packages feed

moonlight-linalg-0.1.0.0: src-laws/Moonlight/LinAlg/Effect/Harness/Domain.hs

module Moonlight.LinAlg.Effect.Harness.Domain
  ( smithDiagonalReconstructsInputLaw,
    smithDivisibilityChainLaw,
    smithWitnessesUnimodularLaw,
    smithDiagonalOnlyAgreesWithFullLaw,
    bareissRankAgreesWithFieldRankLaw,
    bareissDeterminantAgreesWithExteriorLaw,
  )
where

import Data.Bifunctor (first)
import Moonlight.LinAlg
  ( bareissDeterminant,
    bareissRank,
    exteriorPowerMatrix,
    fromListMatrix,
    mult,
    rank,
    smithDiagonal,
    smithDiagonalForm,
    smithDiagonalMatrix,
    smithLeft,
    smithLeftInverse,
    smithNormalForm,
    smithRight,
    smithRightInverse,
    toListMatrix,
  )
import Moonlight.LinAlg.Effect.Harness.Core (assertRightProperty, matrixRows3)
import Test.Tasty.QuickCheck qualified as QC

newtype IntegerMatrix3 = IntegerMatrix3 [Integer]
  deriving stock (Eq, Show)

instance QC.Arbitrary IntegerMatrix3 where
  arbitrary =
    IntegerMatrix3
      <$> QC.vectorOf 9 (fromIntegral <$> QC.chooseInt (-8, 8))

smithDiagonalReconstructsInputLaw :: QC.Property
smithDiagonalReconstructsInputLaw =
  QC.property smithDiagonalReconstructsInputLawProperty

smithDivisibilityChainLaw :: QC.Property
smithDivisibilityChainLaw =
  QC.property smithDivisibilityChainLawProperty

smithWitnessesUnimodularLaw :: QC.Property
smithWitnessesUnimodularLaw =
  QC.property smithWitnessesUnimodularLawProperty

smithDiagonalOnlyAgreesWithFullLaw :: QC.Property
smithDiagonalOnlyAgreesWithFullLaw =
  QC.property smithDiagonalOnlyAgreesWithFullLawProperty

bareissRankAgreesWithFieldRankLaw :: QC.Property
bareissRankAgreesWithFieldRankLaw =
  QC.property bareissRankAgreesWithFieldRankLawProperty

bareissDeterminantAgreesWithExteriorLaw :: QC.Property
bareissDeterminantAgreesWithExteriorLaw =
  QC.property bareissDeterminantAgreesWithExteriorLawProperty

smithDiagonalReconstructsInputLawProperty :: IntegerMatrix3 -> QC.Property
smithDiagonalReconstructsInputLawProperty (IntegerMatrix3 entries) =
  assertRightProperty $ do
    matrixValue <- fromListMatrix @3 @3 entries
    smithValue <- smithNormalForm matrixValue
    leftTimesInput <- mult (smithLeft smithValue) matrixValue
    reconstructed <- mult leftTimesInput (smithRight smithValue)
    pure (toListMatrix reconstructed == toListMatrix (smithDiagonal smithValue))

smithDivisibilityChainLawProperty :: IntegerMatrix3 -> QC.Property
smithDivisibilityChainLawProperty (IntegerMatrix3 entries) =
  assertRightProperty $ do
    matrixValue <- fromListMatrix @3 @3 entries
    smithValue <- smithNormalForm matrixValue
    pure (diagonalDivisibility (toListMatrix (smithDiagonal smithValue)))

smithWitnessesUnimodularLawProperty :: IntegerMatrix3 -> QC.Property
smithWitnessesUnimodularLawProperty (IntegerMatrix3 entries) =
  assertRightProperty $ do
    matrixValue <- fromListMatrix @3 @3 entries
    smithValue <- smithNormalForm matrixValue
    leftInverseLeft <- mult (smithLeftInverse smithValue) (smithLeft smithValue)
    leftLeftInverse <- mult (smithLeft smithValue) (smithLeftInverse smithValue)
    rightInverseRight <- mult (smithRightInverse smithValue) (smithRight smithValue)
    rightRightInverse <- mult (smithRight smithValue) (smithRightInverse smithValue)
    let identityEntries = [1, 0, 0, 0, 1, 0, 0, 0, 1]
    pure
      ( toListMatrix leftInverseLeft == identityEntries
          && toListMatrix leftLeftInverse == identityEntries
          && toListMatrix rightInverseRight == identityEntries
          && toListMatrix rightRightInverse == identityEntries
      )

smithDiagonalOnlyAgreesWithFullLawProperty :: IntegerMatrix3 -> QC.Property
smithDiagonalOnlyAgreesWithFullLawProperty (IntegerMatrix3 entries) =
  assertRightProperty $ do
    matrixValue <- fromListMatrix @3 @3 entries
    fullValue <- smithNormalForm matrixValue
    diagonalOnlyValue <- smithDiagonalForm matrixValue
    pure (toListMatrix (smithDiagonal fullValue) == toListMatrix (smithDiagonalMatrix diagonalOnlyValue))

bareissRankAgreesWithFieldRankLawProperty :: IntegerMatrix3 -> QC.Property
bareissRankAgreesWithFieldRankLawProperty (IntegerMatrix3 entries) =
  assertRightProperty $ do
    integerMatrix <- fromListMatrix @3 @3 entries
    rationalMatrix <- fromListMatrix @3 @3 (fromInteger <$> entries :: [Rational])
    integerRank <- bareissRank integerMatrix
    rationalRank <- rank rationalMatrix
    pure (integerRank == rationalRank)

bareissDeterminantAgreesWithExteriorLawProperty :: IntegerMatrix3 -> QC.Property
bareissDeterminantAgreesWithExteriorLawProperty (IntegerMatrix3 entries) =
  assertRightProperty $ do
    integerMatrix <- first show (fromListMatrix @3 @3 entries)
    integerDeterminant <- first show (bareissDeterminant integerMatrix)
    exteriorDeterminant <- first show (exteriorPowerMatrix 3 (matrixRows3 (fromInteger <$> entries :: [Rational])))
    pure (exteriorDeterminant == [[fromInteger integerDeterminant]])

diagonalDivisibility :: [Integer] -> Bool
diagonalDivisibility entries =
  case entries of
    [d0, z01, z02, z10, d1, z12, z20, z21, d2] ->
      all (== 0) [z01, z02, z10, z12, z20, z21]
        && divides d0 d1
        && divides d1 d2
    _ -> False

divides :: Integer -> Integer -> Bool
divides left right =
  left == 0 || right == 0 || right `rem` left == 0