packages feed

moonlight-triangulation-1.5.0.0: src-cell-complex/Moonlight/Triangulation/LabelledComplex.hs

-- | Homology interpretations of canonical labelled planar complexes.
--
-- 'PlanarComplex' owns simplex identity and downward closure. This module
-- materializes that admitted section once; Homology remains the sole owner of
-- chain-law and filtration-law validation.
module Moonlight.Triangulation.LabelledComplex
  ( PlanarComplexInterpretationError (..)
  , finitePlanarComplex
  , filteredPlanarComplex
  )
where

import Data.Bifunctor (first)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Vector qualified as Vector
import Moonlight.Homology.Boundary
  ( BoundaryIncidence
  , FiniteChainComplex
  , emptyBoundaryIncidence
  , emptyBoundaryIncidenceOf
  , materializeIncidenceBoundary
  , mkFiniteChainComplexChecked
  )
import Moonlight.Homology.Chain
  ( HomologicalDegree (..)
  , HomologyFailure
  )
import Moonlight.Homology.Persistence
  ( FilteredFiniteChainComplex
  , mkFilteredFiniteChainComplex
  )
import Moonlight.Homology.Topology (BasisCellRef (..))
import Moonlight.Triangulation.Simplex
  ( PlanarComplex
  , PlanarSimplex
  , SimplexDimension (..)
  , planarComplexBasisAt
  , planarSimplexBoundary
  )

-- | Typed obstructions at the labelled-complex/Homology boundary.
data PlanarComplexInterpretationError label
  = PlanarComplexChainInvalid !HomologyFailure
  | PlanarComplexBirthUnexpected !(PlanarSimplex label)
  | PlanarComplexFiltrationInvalid !HomologyFailure
  deriving stock (Eq, Show)

-- | Canonical integral chains in ascending labelled-simplex order.
finitePlanarComplex
  :: Ord label
  => PlanarComplex label
  -> Either (PlanarComplexInterpretationError label) (FiniteChainComplex Int)
finitePlanarComplex complexValue = do
  degreeOneBoundary <-
    first PlanarComplexChainInvalid
      ( materializeIncidenceBoundary
          planarSimplexBoundary
          (basisList SimplexDimension1 complexValue)
          (basisList SimplexDimension0 complexValue)
      )
  degreeTwoBoundary <-
    first PlanarComplexChainInvalid
      ( materializeIncidenceBoundary
          planarSimplexBoundary
          (basisList SimplexDimension2 complexValue)
          (basisList SimplexDimension1 complexValue)
      )
  let degreeZeroBoundary :: BoundaryIncidence Int
      degreeZeroBoundary =
        emptyBoundaryIncidenceOf
          (fromIntegral (Vector.length (planarComplexBasisAt SimplexDimension0 complexValue)))
          0
      boundaryAt (HomologicalDegree degreeIndex) =
        case degreeIndex of
          0 -> degreeZeroBoundary
          1 -> degreeOneBoundary
          2 -> degreeTwoBoundary
          _ -> emptyBoundaryIncidence
  first PlanarComplexChainInvalid
    (mkFiniteChainComplexChecked (HomologicalDegree 2) boundaryAt)

-- | Add exact births to the admitted complex. Homology validates uniqueness,
-- total coverage, and every boundary inequality; this interpreter deliberately
-- does not duplicate those laws.
filteredPlanarComplex
  :: (Ord label, Ord filtration)
  => PlanarComplex label
  -> Map (PlanarSimplex label) filtration
  -> Either
      (PlanarComplexInterpretationError label)
      (FilteredFiniteChainComplex filtration Int)
filteredPlanarComplex complexValue births = do
  finite <- finitePlanarComplex complexValue
  assignments <- traverse birthAssignment (Map.toAscList births)
  first PlanarComplexFiltrationInvalid
    (mkFilteredFiniteChainComplex finite assignments)
 where
  basisReferences = planarBasisReferences complexValue
  birthAssignment (simplex, birthValue) =
    maybe
      (Left (PlanarComplexBirthUnexpected simplex))
      (Right . (,birthValue))
      (Map.lookup simplex basisReferences)

planarBasisReferences
  :: Ord label
  => PlanarComplex label
  -> Map (PlanarSimplex label) BasisCellRef
planarBasisReferences complexValue =
  Map.unions
    [ referencesAt 0 SimplexDimension0
    , referencesAt 1 SimplexDimension1
    , referencesAt 2 SimplexDimension2
    ]
 where
  referencesAt degreeValue dimension =
    Map.fromDistinctAscList
      ( zipWith
          (\basisIndex simplex ->
              (simplex, BasisCellRef (HomologicalDegree degreeValue) basisIndex)
          )
          [0 ..]
          (basisList dimension complexValue)
      )

basisList
  :: SimplexDimension
  -> PlanarComplex label
  -> [PlanarSimplex label]
basisList dimension = Vector.toList . planarComplexBasisAt dimension