packages feed

moonlight-homology-0.1.0.0: src-topology/Moonlight/Homology/Pure/Constraint/Algebra.hs

module Moonlight.Homology.Pure.Constraint.Algebra
  ( evaluateTopologicalConstraint,
    evaluateTopologicalConstraints,
  )
where

import Data.Function ((&))
import qualified Data.Map.Strict as Map
import Moonlight.Homology.Pure.Chain
  ( EulerCharacteristic (..),
    HomologicalDegree (..),
    PersistencePair (..),
    TopologyWitness (..),
  )
import Moonlight.Homology.Pure.Constraint.Core
import Moonlight.Homology.Pure.FiniteAbelian
  ( finiteAbelianCardinality,
    finiteAbelianCyclicSummandMultiplicity,
    finiteAbelianExactOrderElementCount,
    finiteAbelianFilteredCardinality,
    finiteAbelianSummandCount,
    isPrime,
    matchesOptional,
    normalizeTorsionOrders,
  )
import Moonlight.Homology.Pure.Graded.Query
  ( degreeSelectionFromMaybe,
    preserveDegreewiseQuery,
    selectAllDegrees,
    selectDegree,
  )
import Moonlight.Homology.Pure.GradedTorsion
  ( GradedTorsionFamily,
    gradedTorsionAtDegree,
    gradedTorsionCombined,
    gradedTorsionOrderSupport,
    gradedTorsionPresent,
    gradedTorsionPrimaryOrderSupport,
  )
import Moonlight.Homology.Pure.Filtration (FiltrationValue (..))
import Moonlight.Homology.Pure.Skeleton
  ( SkeletonSignature (..),
    skeletonSignatureWithinTolerance,
  )
import Moonlight.Homology.Pure.Topology.MacroScaffold (MacroScaffoldIR)
import Moonlight.Homology.Pure.Topology.ScaffoldSummary (mkMacroScaffoldTopologyView)
import Moonlight.Homology.Pure.TopologyObserver
  ( observeBettiVector,
    observeCoefficientRepresentativeCycleCount,
    observeEulerCharacteristic,
    observeExactRepresentativeClassCount,
    observeHarmonicCount,
    observePersistencePairs,
    observeScaffoldSummary,
    observeTorsionFamily,
    runTopologyObserver,
  )

evaluateTopologicalConstraint ::
  TopologyWitness MacroScaffoldIR spectral FiltrationValue coefficient basis ->
  TopologicalConstraint ->
  [TopologicalViolation]
evaluateTopologicalConstraint witnessValue constraintValue =
  let topologyView = mkMacroScaffoldTopologyView witnessValue
      torsionFamily = runTopologyObserver observeTorsionFamily topologyView
   in case constraintValue of
    TargetBettiConstraint targetValue ->
      let observedBetti = runTopologyObserver observeBettiVector topologyView
       in if observedBetti == targetBettiVector targetValue
        then []
        else [BettiViolation targetValue observedBetti]
    PersistenceBudgetConstraint budgetValue ->
      let observedCount =
            countPersistentFeatures
              budgetValue
              (runTopologyObserver
                (observePersistencePairs (degreeSelectionFromMaybe (persistenceBudgetDegree budgetValue)))
                topologyView)
       in checkBound (persistenceBudgetCountBound budgetValue) observedCount
            (PersistenceBudgetViolation budgetValue observedCount)
    EulerBoundConstraint eulerBoundValue ->
      case runTopologyObserver observeEulerCharacteristic topologyView of
        Nothing -> [EulerWitnessMissing eulerBoundValue]
        Just observedEuler ->
          checkBound (requiredEulerBound eulerBoundValue) (unEulerCharacteristic observedEuler)
            (EulerBoundViolation eulerBoundValue observedEuler)
    LoopRoleConstraint loopRoleValue ->
      let degreeSelectionValue = selectDegree (loopTargetDegree loopRoleValue)
          observedCount =
            if runTopologyObserver (observeExactRepresentativeClassCount selectAllDegrees) topologyView == 0
              then
                runTopologyObserver (observeCoefficientRepresentativeCycleCount degreeSelectionValue) topologyView
              else
                runTopologyObserver (observeExactRepresentativeClassCount degreeSelectionValue) topologyView
       in checkBound (loopCountBound loopRoleValue) observedCount
            (LoopRoleViolation loopRoleValue observedCount)
    RequireTorsionInvariantConstraint torsionConstraintValue ->
      case gradedTorsionAtDegree
        (requiredTorsionDegree torsionConstraintValue)
        torsionFamily of
        Nothing ->
          [IntegralHomologyWitnessMissing constraintValue]
        Just torsionValue ->
          let observedCount =
                torsionValue
                  & finiteAbelianCyclicSummandMultiplicity
                    (requiredTorsionInvariant torsionConstraintValue)
           in checkBound (requiredTorsionMultiplicity torsionConstraintValue) observedCount
                (RequireTorsionInvariantViolation torsionConstraintValue observedCount)
    RequireElementOrderConstraint elementConstraintValue ->
      withTorsionPresent constraintValue torsionFamily $ \_ ->
        let observedCount =
              gradedTorsionCombined
                (selectDegree (requiredElementDegree elementConstraintValue))
                torsionFamily
                & finiteAbelianExactOrderElementCount
                  (requiredElementOrder elementConstraintValue)
         in checkBound (requiredElementMultiplicity elementConstraintValue) observedCount
              (RequireElementOrderViolation elementConstraintValue observedCount)
    RequireOrderSupportConstraint supportConstraintValue ->
      withTorsionPresent constraintValue torsionFamily $ \_ ->
        let observedSupport =
              gradedTorsionOrderSupport
                (preserveDegreewiseQuery (degreeSelectionFromMaybe (requiredOrderSupportDegree supportConstraintValue)))
                torsionFamily
            missingOrders =
              requiredSupportedOrders supportConstraintValue
                & normalizeTorsionOrders
                & filter (`notElem` observedSupport)
            forbiddenOrders =
              requiredForbiddenOrders supportConstraintValue
                & normalizeTorsionOrders
                & filter (`elem` observedSupport)
         in if null missingOrders && null forbiddenOrders
              then []
              else [RequireOrderSupportViolation supportConstraintValue missingOrders forbiddenOrders]
    PrimaryOrderSupportBudgetConstraint budgetValue ->
      if not (isPrime (primarySupportBudgetPrime budgetValue))
        then [InvalidPrimaryPrime constraintValue (primarySupportBudgetPrime budgetValue)]
        else
          withTorsionPresent constraintValue torsionFamily $ \_ ->
            let observedCount =
                  maybe [] id
                    (gradedTorsionPrimaryOrderSupport
                    (primarySupportBudgetPrime budgetValue)
                    (preserveDegreewiseQuery (degreeSelectionFromMaybe (primarySupportBudgetDegree budgetValue)))
                    torsionFamily)
                    & length
                    & toInteger
             in checkBound (primarySupportBudgetBound budgetValue) observedCount
                  (PrimaryOrderSupportBudgetViolation budgetValue observedCount)
    RequirePrimaryOrderSupportConstraint supportConstraintValue ->
      if not (isPrime (requiredPrimarySupportPrime supportConstraintValue))
        then [InvalidPrimaryPrime constraintValue (requiredPrimarySupportPrime supportConstraintValue)]
        else
          withTorsionPresent constraintValue torsionFamily $ \_ ->
            let observedSupport =
                  maybe [] id
                    (gradedTorsionPrimaryOrderSupport
                    (requiredPrimarySupportPrime supportConstraintValue)
                    (preserveDegreewiseQuery (degreeSelectionFromMaybe (requiredPrimarySupportDegree supportConstraintValue)))
                    torsionFamily)
                missingOrders =
                  requiredPrimarySupportedOrders supportConstraintValue
                    & normalizeTorsionOrders
                    & filter (`notElem` observedSupport)
                forbiddenOrders =
                  requiredPrimaryForbiddenOrders supportConstraintValue
                    & normalizeTorsionOrders
                    & filter (`elem` observedSupport)
             in if null missingOrders && null forbiddenOrders
                  then []
                  else [RequirePrimaryOrderSupportViolation supportConstraintValue missingOrders forbiddenOrders]
    TorsionBudgetConstraint budgetValue ->
      withTorsionPresent constraintValue torsionFamily $ \_ ->
        let observedCount =
              observeTorsionBudget
                (torsionBudgetDegree budgetValue)
                (torsionBudgetOrder budgetValue)
                (torsionBudgetMeasure budgetValue)
                torsionFamily
         in checkBound (torsionBudgetBound budgetValue) observedCount
              (TorsionBudgetViolation budgetValue observedCount)
    RequireCyclicOrderConstraint cyclicOrderValue ->
      withTorsionPresent constraintValue torsionFamily $ \_ ->
        case gradedTorsionAtDegree
          (requiredCyclicDegree cyclicOrderValue)
          torsionFamily of
          Nothing ->
            [IntegralHomologyWitnessMissing constraintValue]
          Just torsionValue ->
            let observedCount =
                  torsionValue
                    & finiteAbelianCyclicSummandMultiplicity
                      (requiredCyclicOrder cyclicOrderValue)
             in checkBound (requiredCyclicMultiplicity cyclicOrderValue) observedCount
                  (RequireCyclicOrderViolation cyclicOrderValue observedCount)
    SingularityBudgetConstraint singularityBudgetValue ->
      case runTopologyObserver observeScaffoldSummary topologyView of
        Nothing -> [MacroScaffoldMissing constraintValue]
        Just scaffoldSummaryValue ->
          singularityBounds singularityBudgetValue
            & Map.toList
            >>= ( \(criticalKindValue, boundValue) ->
                    let observedCount =
                          signatureCriticalCounts scaffoldSummaryValue
                            & Map.findWithDefault 0 criticalKindValue
                     in checkBound boundValue observedCount
                          (SingularityBudgetViolation criticalKindValue boundValue observedCount)
               )
    HarmonicLoopBudgetConstraint harmonicBudgetValue ->
      let observedCount =
            runTopologyObserver
              (observeHarmonicCount (selectDegree (harmonicLoopDegree harmonicBudgetValue)))
              topologyView
       in checkBound (harmonicLoopCountBound harmonicBudgetValue) observedCount
            (HarmonicLoopBudgetViolation harmonicBudgetValue observedCount)
    SkeletonAdherenceConstraint adherenceValue ->
      case runTopologyObserver observeScaffoldSummary topologyView of
        Nothing -> [MacroScaffoldMissing constraintValue]
        Just scaffoldSummaryValue ->
          if skeletonWithinTolerance adherenceValue scaffoldSummaryValue
                then []
                else [SkeletonAdherenceViolation adherenceValue scaffoldSummaryValue]

evaluateTopologicalConstraints ::
  TopologyWitness MacroScaffoldIR spectral FiltrationValue coefficient basis ->
  [TopologicalConstraint] ->
  [TopologicalViolation]
evaluateTopologicalConstraints witnessValue =
  foldMap (evaluateTopologicalConstraint witnessValue)

countPersistentFeatures :: PersistenceBudget -> [PersistencePair FiltrationValue] -> Int
countPersistentFeatures budgetValue persistencePairsValue =
  persistencePairsValue
    & filter (meetsLifetimeBudget (persistenceBudgetMinimumLifetime budgetValue))
    & length

meetsLifetimeBudget :: FiltrationValue -> PersistencePair FiltrationValue -> Bool
meetsLifetimeBudget minimumLifetime pairValue =
  case persistenceDeath pairValue of
    Nothing -> True
    Just deathValue ->
      filtrationDifference deathValue (persistenceBirth pairValue) >= unFiltrationValue minimumLifetime

filtrationDifference :: FiltrationValue -> FiltrationValue -> Double
filtrationDifference endValue startValue =
  unFiltrationValue endValue - unFiltrationValue startValue

observeTorsionBudget ::
  Maybe HomologicalDegree ->
  Maybe Integer ->
  TorsionBudgetMeasure ->
  GradedTorsionFamily ->
  Integer
observeTorsionBudget degreeConstraint orderConstraint measure torsionFamily =
  let selectionValue = degreeSelectionFromMaybe degreeConstraint
      observedTorsion =
        gradedTorsionCombined selectionValue torsionFamily
   in case measure of
        TorsionSummandCount ->
          finiteAbelianSummandCount orderConstraint observedTorsion
        TorsionTotalCardinality ->
          finiteAbelianFilteredCardinality orderConstraint observedTorsion
        TorsionElementOrderCount ->
          case orderConstraint of
            Nothing -> finiteAbelianCardinality observedTorsion
            Just orderValue ->
              finiteAbelianExactOrderElementCount orderValue observedTorsion
        TorsionOrderSupportCount ->
          gradedTorsionOrderSupport (preserveDegreewiseQuery selectionValue) torsionFamily
            & filter (matchesOptional (fmap abs orderConstraint) . abs)
            & length
            & toInteger

withTorsionPresent ::
  TopologicalConstraint ->
  GradedTorsionFamily ->
  (GradedTorsionFamily -> [TopologicalViolation]) ->
  [TopologicalViolation]
withTorsionPresent constraintValue torsionFamily evaluator =
  if gradedTorsionPresent torsionFamily
    then evaluator torsionFamily
    else [IntegralHomologyWitnessMissing constraintValue]

skeletonWithinTolerance :: SkeletonAdherence -> SkeletonSignature -> Bool
skeletonWithinTolerance adherenceValue observedSignature =
  skeletonSignatureWithinTolerance
    (skeletonTolerance adherenceValue)
    (skeletonTargetSignature adherenceValue)
    observedSignature