packages feed

moonlight-homology-0.1.0.0: src-chain/Moonlight/Homology/Pure/Chain.hs

module Moonlight.Homology.Pure.Chain
  ( HomologicalDegree (..),
    incrementDegree,
    decrementDegree,
    EulerCharacteristic (..),
    PersistencePair (..),
    RepresentativeChain (..),
    RepresentativeCycle,
    RepresentativeCocycle,
    HarmonicBasisElement (..),
    ExactRepresentativeClass (..),
    TopologyWitness (..),
    emptyTopologyWitness,
    mergeTopologyWitness,
    mergeTopologyWitnessChecked,
    topologyRepresentativeCycles,
    topologyRepresentativeCocycles,
    topologyWitnessFromBetti,
  )
where

import Control.Applicative ((<|>))
import Data.Kind (Type)
import Moonlight.Homology.Pure.Degree (HomologicalDegree (..), decrementDegree, incrementDegree)
import Moonlight.Homology.Pure.Failure (HomologyFailure (..))
import Moonlight.Homology.Pure.Group (HomologyGroup (..))

type EulerCharacteristic :: Type
newtype EulerCharacteristic = EulerCharacteristic
  { unEulerCharacteristic :: Int
  }
  deriving stock (Eq, Ord, Show, Read)

type PersistencePair :: Type -> Type
data PersistencePair scalar = PersistencePair
  { persistenceDegree :: HomologicalDegree,
    persistenceBirth :: scalar,
    persistenceDeath :: Maybe scalar
  }
  deriving stock (Eq, Show)

type RepresentativeChain :: Type -> Type -> Type
data RepresentativeChain coefficient basis = RepresentativeChain
  { representativeDegree :: HomologicalDegree,
    representativeTerms :: [(coefficient, basis)]
  }
  deriving stock (Eq, Show)

type RepresentativeCycle :: Type -> Type -> Type
type RepresentativeCycle coefficient basis = RepresentativeChain coefficient basis

type RepresentativeCocycle :: Type -> Type -> Type
type RepresentativeCocycle coefficient basis = RepresentativeChain coefficient basis

type HarmonicBasisElement :: Type -> Type -> Type
data HarmonicBasisElement coefficient basis = HarmonicBasisElement
  { harmonicDegree :: HomologicalDegree,
    harmonicRepresentative :: RepresentativeCocycle coefficient basis
  }
  deriving stock (Eq, Show)

type ExactRepresentativeClass :: Type -> Type
data ExactRepresentativeClass basis = ExactRepresentativeClass
  { exactClassDegree :: HomologicalDegree,
    exactClassOrder :: Maybe Integer,
    exactClassRepresentative :: RepresentativeCycle Integer basis
  }
  deriving stock (Eq, Show)

type TopologyWitness :: Type -> Type -> Type -> Type -> Type -> Type
data TopologyWitness scaffold spectral persistence coefficient basis = TopologyWitness
  { topologyEulerCharacteristic :: Maybe EulerCharacteristic,
    topologyBettiVector :: [Int],
    topologyIntegralHomologyGroups :: [HomologyGroup Integer],
    topologyExactRepresentativeClasses :: [ExactRepresentativeClass basis],
    topologyPersistencePairs :: [PersistencePair persistence],
    topologyCoefficientRepresentativeCycles :: [RepresentativeCycle coefficient basis],
    topologyCoefficientRepresentativeCocycles :: [RepresentativeCocycle coefficient basis],
    topologyHarmonicBasis :: [HarmonicBasisElement coefficient basis],
    topologyMacroScaffold :: Maybe scaffold,
    topologyLowSpectralModes :: [spectral]
  }
  deriving stock (Eq, Show)

emptyTopologyWitness :: TopologyWitness scaffold spectral persistence coefficient basis
emptyTopologyWitness =
  TopologyWitness
    { topologyEulerCharacteristic = Nothing,
      topologyBettiVector = [],
      topologyIntegralHomologyGroups = [],
      topologyExactRepresentativeClasses = [],
      topologyPersistencePairs = [],
      topologyCoefficientRepresentativeCycles = [],
      topologyCoefficientRepresentativeCocycles = [],
      topologyHarmonicBasis = [],
      topologyMacroScaffold = Nothing,
      topologyLowSpectralModes = []
    }

mergeTopologyWitness ::
  TopologyWitness scaffold spectral persistence coefficient basis ->
  TopologyWitness scaffold spectral persistence coefficient basis ->
  TopologyWitness scaffold spectral persistence coefficient basis
mergeTopologyWitness left right =
  TopologyWitness
    { topologyEulerCharacteristic =
        topologyEulerCharacteristic left <|> topologyEulerCharacteristic right,
      topologyBettiVector =
        preferNonEmpty (topologyBettiVector left) (topologyBettiVector right),
      topologyIntegralHomologyGroups =
        preferNonEmpty (topologyIntegralHomologyGroups left) (topologyIntegralHomologyGroups right),
      topologyExactRepresentativeClasses =
        preferNonEmpty (topologyExactRepresentativeClasses left) (topologyExactRepresentativeClasses right),
      topologyPersistencePairs =
        topologyPersistencePairs left <> topologyPersistencePairs right,
      topologyCoefficientRepresentativeCycles =
        topologyCoefficientRepresentativeCycles left <> topologyCoefficientRepresentativeCycles right,
      topologyCoefficientRepresentativeCocycles =
        topologyCoefficientRepresentativeCocycles left <> topologyCoefficientRepresentativeCocycles right,
      topologyHarmonicBasis =
        topologyHarmonicBasis left <> topologyHarmonicBasis right,
      topologyMacroScaffold =
        topologyMacroScaffold left <|> topologyMacroScaffold right,
      topologyLowSpectralModes =
        topologyLowSpectralModes left <> topologyLowSpectralModes right
    }
  where
    preferNonEmpty :: [a] -> [a] -> [a]
    preferNonEmpty preferred fallback =
      case preferred of
        [] -> fallback
        _ -> preferred

mergeTopologyWitnessChecked ::
  (Eq scaffold, Eq basis) =>
  TopologyWitness scaffold spectral persistence coefficient basis ->
  TopologyWitness scaffold spectral persistence coefficient basis ->
  Either HomologyFailure (TopologyWitness scaffold spectral persistence coefficient basis)
mergeTopologyWitnessChecked left right = do
  mergedEuler <- mergeOptional "Euler characteristic" (topologyEulerCharacteristic left) (topologyEulerCharacteristic right)
  mergedBetti <- mergePreferNonEmpty "Betti vector" (topologyBettiVector left) (topologyBettiVector right)
  mergedGroups <- mergePreferNonEmpty "integral homology groups" (topologyIntegralHomologyGroups left) (topologyIntegralHomologyGroups right)
  mergedExact <- mergePreferNonEmpty "exact representative classes" (topologyExactRepresentativeClasses left) (topologyExactRepresentativeClasses right)
  mergedScaffold <- mergeOptional "macro scaffold" (topologyMacroScaffold left) (topologyMacroScaffold right)
  pure
    TopologyWitness
      { topologyEulerCharacteristic = mergedEuler,
        topologyBettiVector = mergedBetti,
        topologyIntegralHomologyGroups = mergedGroups,
        topologyExactRepresentativeClasses = mergedExact,
        topologyPersistencePairs = topologyPersistencePairs left <> topologyPersistencePairs right,
        topologyCoefficientRepresentativeCycles = topologyCoefficientRepresentativeCycles left <> topologyCoefficientRepresentativeCycles right,
        topologyCoefficientRepresentativeCocycles = topologyCoefficientRepresentativeCocycles left <> topologyCoefficientRepresentativeCocycles right,
        topologyHarmonicBasis = topologyHarmonicBasis left <> topologyHarmonicBasis right,
        topologyMacroScaffold = mergedScaffold,
        topologyLowSpectralModes = topologyLowSpectralModes left <> topologyLowSpectralModes right
      }

mergeOptional :: Eq a => String -> Maybe a -> Maybe a -> Either HomologyFailure (Maybe a)
mergeOptional fieldName leftValue rightValue =
  case (leftValue, rightValue) of
    (Nothing, _) -> Right rightValue
    (_, Nothing) -> Right leftValue
    (Just leftInner, Just rightInner)
      | leftInner == rightInner -> Right leftValue
      | otherwise -> Left (InvalidTopologyInput ("conflicting topology witness data for " <> fieldName))

mergePreferNonEmpty :: Eq a => String -> [a] -> [a] -> Either HomologyFailure [a]
mergePreferNonEmpty fieldName leftValue rightValue =
  case (leftValue, rightValue) of
    ([], _) -> Right rightValue
    (_, []) -> Right leftValue
    _
      | leftValue == rightValue -> Right leftValue
      | otherwise -> Left (InvalidTopologyInput ("conflicting topology witness data for " <> fieldName))

topologyWitnessFromBetti ::
  [HomologyGroup r] ->
  TopologyWitness scaffold spectral persistence coefficient basis
topologyWitnessFromBetti groups =
  emptyTopologyWitness
    { topologyBettiVector = fmap freeRank groups
    }

topologyRepresentativeCycles ::
  TopologyWitness scaffold spectral persistence coefficient basis ->
  [RepresentativeCycle coefficient basis]
topologyRepresentativeCycles =
  topologyCoefficientRepresentativeCycles

topologyRepresentativeCocycles ::
  TopologyWitness scaffold spectral persistence coefficient basis ->
  [RepresentativeCocycle coefficient basis]
topologyRepresentativeCocycles =
  topologyCoefficientRepresentativeCocycles