packages feed

gargamelle-sheaf-0.1.0.0: src/Gargamelle/Sheaf/Complex.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Gargamelle.Sheaf.Complex
  ( -- * Cell Complex
    CellComplex(..)
  , emptyComplex
  , fromEdges
  , addVertex
  , addEdge
    -- * Cells & Poset Structure
  , Cell(..)
  , cellDimension
  , cellFaces
  , isFaceOf
  , Orientation(..)
  , orientationSign
  , incidenceSign
    -- * Complex Queries
  , incidentEdges
  , star
  , boundary
  , numVertices
  , numEdges
  , eulerCharacteristic
  ) where

import Data.List (nub)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map

-- | A finite 1-dimensional cell complex (graph complex) G = (V, E).
-- 0-cells represent component spaces/loci, and 1-cells represent interface boundaries.
data CellComplex vId eId = CellComplex
  { vertices :: [vId]
  , edges    :: Map eId (vId, vId) -- ^ Map from edge ID to (source, target)
  } deriving (Eq, Show)

-- | Orientation of an incident edge relative to a vertex.
data Orientation
  = Forward   -- ^ Directed away from the vertex (vertex is source: sign -1 in boundary)
  | Backward  -- ^ Directed into the vertex (vertex is target: sign +1 in boundary)
  deriving (Eq, Ord, Show)

-- | Numeric homological sign for an orientation.
orientationSign :: Orientation -> Int
orientationSign = \case
  Forward  -> -1
  Backward -> 1

-- | The cells of a 1-dimensional cell complex, forming a graded face poset.
data Cell vId eId
  = Cell0 vId  -- ^ 0-cell (vertex)
  | Cell1 eId  -- ^ 1-cell (edge)
  deriving (Eq, Ord, Show)

-- | Dimension of a cell in the complex.
cellDimension :: Cell vId eId -> Int
cellDimension = \case
  Cell0 _ -> 0
  Cell1 _ -> 1

-- | Empty cell complex.
emptyComplex :: CellComplex vId eId
emptyComplex = CellComplex
  { vertices = []
  , edges    = Map.empty
  }

-- | Construct a cell complex from a list of directed edges (eId, source, target).
-- Vertices are automatically collected and deduplicated.
fromEdges :: forall vId eId. (Eq vId, Ord eId) => [(eId, vId, vId)] -> CellComplex vId eId
fromEdges edgeList = CellComplex
  { vertices = nub [ v | (_, u, w) <- edgeList, v <- [u, w] ]
  , edges    = Map.fromList [ (e, (u, w)) | (e, u, w) <- edgeList ]
  }

-- | Add a vertex to the cell complex if not already present.
addVertex :: (Eq vId) => vId -> CellComplex vId eId -> CellComplex vId eId
addVertex v complex
  | v `elem` vertices complex = complex
  | otherwise = complex { vertices = v : vertices complex }

-- | Add an edge and ensure its endpoints are in the vertex set.
addEdge :: (Ord eId, Eq vId) => eId -> vId -> vId -> CellComplex vId eId -> CellComplex vId eId
addEdge e u w complex = complex
  { vertices = nub (u : w : vertices complex)
  , edges    = Map.insert e (u, w) (edges complex)
  }

-- | Boundary of a 1-cell (source, target).
boundary :: (Ord eId) => CellComplex vId eId -> eId -> Maybe (vId, vId)
boundary complex e = Map.lookup e (edges complex)

-- | Incident edges for a given vertex, including orientation and adjacent vertex.
incidentEdges :: (Eq vId) => CellComplex vId eId -> vId -> [(eId, Orientation, vId)]
incidentEdges complex v =
  [ (e, orient, other)
  | (e, (u, w)) <- Map.toList (edges complex)
  , (orient, other) <-
      let isSrc = u == v
          isTgt = w == v
      in case (isSrc, isTgt) of
           (True, False) -> [(Forward, w)]
           (False, True) -> [(Backward, u)]
           (True, True)  -> [(Forward, w), (Backward, u)]
           (False, False)-> []
  ]

-- | Star (co-faces) of a vertex: all edges incident to v.
star :: (Eq vId) => CellComplex vId eId -> vId -> [eId]
star complex v = [ e | (e, _, _) <- incidentEdges complex v ]

-- | Faces of a cell in the cell complex.
cellFaces :: (Ord eId) => CellComplex vId eId -> Cell vId eId -> [Cell vId eId]
cellFaces complex = \case
  Cell0 v -> [Cell0 v]
  Cell1 e -> case boundary complex e of
    Nothing     -> [Cell1 e]
    Just (u, w) -> [Cell1 e, Cell0 u, Cell0 w]

-- | Partial order predicate: checks whether sigma is a face of tau (sigma <= tau).
isFaceOf :: (Ord eId, Eq vId) => CellComplex vId eId -> Cell vId eId -> Cell vId eId -> Bool
isFaceOf complex sigma tau = case (sigma, tau) of
  (Cell0 u, Cell0 v) -> u == v
  (Cell1 a, Cell1 b) -> a == b
  (Cell0 u, Cell1 e) -> case boundary complex e of
    Just (src, tgt) -> u == src || u == tgt
    Nothing         -> False
  (Cell1 _, Cell0 _) -> False

-- | Homological incidence sign [e : v] of vertex v in edge e.
-- Returns -1 if v is source, +1 if v is target, 0 if not incident.
incidenceSign :: (Ord eId, Eq vId) => CellComplex vId eId -> eId -> vId -> Int
incidenceSign complex e v = case boundary complex e of
  Just (src, tgt) -> case (v == src, v == tgt) of
    (True, False) -> -1
    (False, True) -> 1
    (True, True)  -> 0 -- Self-loop cancels in standard homology
    (False, False)-> 0
  Nothing -> 0

-- | Count of 0-cells.
numVertices :: CellComplex vId eId -> Int
numVertices = length . vertices

-- | Count of 1-cells.
numEdges :: CellComplex vId eId -> Int
numEdges = Map.size . edges

-- | Euler characteristic chi = |V| - |E| = beta_0 - beta_1.
eulerCharacteristic :: CellComplex vId eId -> Int
eulerCharacteristic c = numVertices c - numEdges c