moonlight-homology-0.1.0.0: src-public/Moonlight/Homology/Boundary/GraphGF2.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE StandaloneKindSignatures #-}
module Moonlight.Homology.Boundary.GraphGF2
( GraphBoundaryGF2,
gbgf2VertexIndex,
gbgf2Edges,
gbgf2Boundary,
GraphBoundaryGF2Failure (..),
prepareGraphBoundaryGF2,
graphBoundaryRankDefectGF2,
)
where
import Data.Bifunctor
( first,
)
import Data.IntMap.Strict
( IntMap,
)
import Data.IntMap.Strict qualified as IntMap
import Data.Kind
( Type,
)
import Data.Map.Strict
( Map,
)
import Data.Map.Strict qualified as Map
import Data.Set
( Set,
)
import Data.Set qualified as Set
import Moonlight.Homology.Boundary.LinAlg
( BoundaryEntry,
BoundaryIncidenceShapeError,
mkBoundaryEntry,
mkBoundaryIncidenceFromOrderedEntries,
)
import Moonlight.Homology.Rank.GF2
( GF2RankFailure,
PreparedGF2Boundary,
prepareGF2Boundary,
rankPreparedGF2Boundary,
)
import Moonlight.LinAlg.Pure.Dense.GF2
( GF2 (..),
)
type GraphBoundaryGF2 :: Type -> Type
data GraphBoundaryGF2 cell = GraphBoundaryGF2
{ graphBoundaryVertexIndex :: !(Map cell Int),
graphBoundaryEdges :: !(IntMap (cell, cell)),
graphBoundaryPrepared :: !PreparedGF2Boundary
}
deriving stock (Eq, Show)
gbgf2VertexIndex :: GraphBoundaryGF2 cell -> Map cell Int
gbgf2VertexIndex =
graphBoundaryVertexIndex
gbgf2Edges :: GraphBoundaryGF2 cell -> IntMap (cell, cell)
gbgf2Edges =
graphBoundaryEdges
gbgf2Boundary :: GraphBoundaryGF2 cell -> PreparedGF2Boundary
gbgf2Boundary =
graphBoundaryPrepared
type GraphBoundaryGF2Failure :: Type -> Type
data GraphBoundaryGF2Failure cell
= GraphBoundaryGF2EndpointMissing !Int !cell !(cell, cell)
| GraphBoundaryGF2BoundaryShapeFailed !BoundaryIncidenceShapeError
| GraphBoundaryGF2RankFailed !GF2RankFailure
deriving stock (Eq, Show)
prepareGraphBoundaryGF2 ::
Ord cell =>
Set cell ->
[(cell, cell)] ->
Either (GraphBoundaryGF2Failure cell) (GraphBoundaryGF2 cell)
prepareGraphBoundaryGF2 vertices edges = do
edgeTerms <- traverse edgeBoundaryTerms indexedEdges
incidence <-
first GraphBoundaryGF2BoundaryShapeFailed $
mkBoundaryIncidenceFromOrderedEntries
(fromIntegral edgeCount)
(fromIntegral vertexCount)
(concat edgeTerms)
preparedBoundary <-
first GraphBoundaryGF2RankFailed $
prepareGF2Boundary incidence
Right
GraphBoundaryGF2
{ graphBoundaryVertexIndex = vertexIndex,
graphBoundaryEdges = IntMap.fromAscList indexedEdges,
graphBoundaryPrepared = preparedBoundary
}
where
vertexIndex =
Map.fromList (zip (Set.toAscList vertices) [0 :: Int ..])
vertexCount =
Set.size vertices
indexedEdges =
zip [0 :: Int ..] edges
edgeCount =
length edges
edgeBoundaryTerms (edgeIndex, edgeValue@(sourceCell, targetCell)) = do
sourceOffset <- endpointOffset edgeIndex sourceCell edgeValue
targetOffset <- endpointOffset edgeIndex targetCell edgeValue
Right (boundaryTermsForEdge edgeIndex sourceOffset targetOffset)
endpointOffset edgeIndex endpointCell edgeValue =
maybe
(Left (GraphBoundaryGF2EndpointMissing edgeIndex endpointCell edgeValue))
Right
(Map.lookup endpointCell vertexIndex)
graphBoundaryRankDefectGF2 :: GraphBoundaryGF2 cell -> Int
graphBoundaryRankDefectGF2 boundaryValue =
IntMap.size (graphBoundaryEdges boundaryValue)
- rankPreparedGF2Boundary (graphBoundaryPrepared boundaryValue)
{-# INLINE graphBoundaryRankDefectGF2 #-}
boundaryTermsForEdge :: Int -> Int -> Int -> [BoundaryEntry GF2]
boundaryTermsForEdge edgeIndex sourceOffset targetOffset =
fmap
(\targetOffsetValue -> mkBoundaryEntry (fromIntegral edgeIndex) (fromIntegral targetOffsetValue) GF2One)
[min sourceOffset targetOffset, max sourceOffset targetOffset]