packages feed

moonlight-homology-0.1.0.0: src-topology/Moonlight/Homology/Pure/Topology/SparseAlgebra.hs

module Moonlight.Homology.Pure.Topology.SparseAlgebra
  ( sparseHomologyBasisAt,
    sparseCohomologyBasisAt,
    sparseFreeBettiVector,
    sparseQuotientRepresentatives,
  )
where

import Data.Function ((&))
import Data.IntMap.Strict qualified as IntMap
import Data.Maybe (mapMaybe)
import Data.Set qualified as Set
import Moonlight.Homology.Boundary.Finite (FiniteChainComplex, incidenceMatrixAt)
import Moonlight.Homology.Pure.Chain
  ( HomologicalDegree (..),
    RepresentativeChain (..),
    RepresentativeCocycle,
    RepresentativeCycle,
  )
import Moonlight.Homology.Pure.Matrix.Shape
  ( cellCountAtDegree,
    dimensionsOf,
  )
import Moonlight.Homology.Pure.Topology.Graph
  ( GraphOneComplex (..),
    graphOneComplexFromComplex,
  )
import Moonlight.Homology.Pure.Matrix.SparseLinAlg
  ( SparseMatrix (..),
    SparseRow,
    sparseBoundaryMatrix,
    sparseIndependentModulo,
    sparseKernelBasisOf,
    sparseTransposeMatrix,
  )

sparseHomologyBasisAt ::
  Integral r =>
  FiniteChainComplex r ->
  HomologicalDegree ->
  [RepresentativeCycle Rational Int]
sparseHomologyBasisAt finite degreeValue@(HomologicalDegree degreeIndex) =
  case degreeValue of
    HomologicalDegree 0 ->
      maybe
        (genericSparseHomologyBasisAt finite degreeValue degreeIndex)
        graphHomologyZeroRepresentatives
        (graphOneComplexFromComplex finite)
    _ ->
      genericSparseHomologyBasisAt finite degreeValue degreeIndex

sparseCohomologyBasisAt ::
  Integral r =>
  FiniteChainComplex r ->
  HomologicalDegree ->
  [RepresentativeCocycle Rational Int]
sparseCohomologyBasisAt finite degreeValue@(HomologicalDegree degreeIndex) =
  case degreeValue of
    HomologicalDegree 0 ->
      maybe
        (genericSparseCohomologyBasisAt finite degreeValue degreeIndex)
        graphCohomologyZeroRepresentatives
        (graphOneComplexFromComplex finite)
    _ ->
      genericSparseCohomologyBasisAt finite degreeValue degreeIndex

genericSparseHomologyBasisAt ::
  Integral r =>
  FiniteChainComplex r ->
  HomologicalDegree ->
  Int ->
  [RepresentativeCycle Rational Int]
genericSparseHomologyBasisAt finite degreeValue degreeIndex =
  sparseQuotientRepresentatives
    degreeValue
    (cellCountAtDegree finite degreeValue)
    (sparseBoundaryMatrix (incidenceMatrixAt finite degreeValue))
    (sparseBoundaryMatrix (incidenceMatrixAt finite (HomologicalDegree (degreeIndex + 1))))

genericSparseCohomologyBasisAt ::
  Integral r =>
  FiniteChainComplex r ->
  HomologicalDegree ->
  Int ->
  [RepresentativeCocycle Rational Int]
genericSparseCohomologyBasisAt finite degreeValue degreeIndex =
  sparseQuotientRepresentatives
    degreeValue
    (cellCountAtDegree finite degreeValue)
    (sparseTransposeMatrix (sparseBoundaryMatrix (incidenceMatrixAt finite (HomologicalDegree (degreeIndex + 1)))))
    (sparseTransposeMatrix (sparseBoundaryMatrix (incidenceMatrixAt finite degreeValue)))

graphHomologyZeroRepresentatives :: GraphOneComplex -> [RepresentativeCycle Rational Int]
graphHomologyZeroRepresentatives graph =
  graphOneComponents graph
    & mapMaybe Set.lookupMin
    & fmap
      ( \vertexIndex ->
          RepresentativeChain
            { representativeDegree = HomologicalDegree 0,
              representativeTerms = [(1, vertexIndex)]
            }
      )

graphCohomologyZeroRepresentatives :: GraphOneComplex -> [RepresentativeCocycle Rational Int]
graphCohomologyZeroRepresentatives graph =
  graphOneComponents graph
    & fmap
      ( \component ->
          RepresentativeChain
            { representativeDegree = HomologicalDegree 0,
              representativeTerms = fmap (\vertexIndex -> (1, vertexIndex)) (Set.toAscList component)
            }
      )

sparseFreeBettiVector :: Integral r => FiniteChainComplex r -> [Int]
sparseFreeBettiVector finite =
  dimensionsOf finite
    & fmap (length . sparseHomologyBasisAt finite)

sparseQuotientRepresentatives ::
  HomologicalDegree ->
  Int ->
  SparseMatrix ->
  SparseMatrix ->
  [RepresentativeChain Rational Int]
sparseQuotientRepresentatives degreeValue ambientDimension currentMatrix incomingMatrix =
  let kernelBasis = sparseKernelBasisOf ambientDimension currentMatrix
      imageGenerators = smRows (sparseTransposeMatrix incomingMatrix)
      quotientBasis = sparseIndependentModulo ambientDimension imageGenerators kernelBasis
   in fmap (sparseVectorToRepresentative degreeValue) quotientBasis

sparseVectorToRepresentative :: HomologicalDegree -> SparseRow -> RepresentativeChain Rational Int
sparseVectorToRepresentative degreeValue rowValue =
  RepresentativeChain
    { representativeDegree = degreeValue,
      representativeTerms =
        IntMap.toAscList rowValue
          & filter (\(_, coefficientValue) -> coefficientValue /= 0)
          & fmap (\(basisIndexValue, coefficientValue) -> (coefficientValue, basisIndexValue))
    }