moonlight-homology-0.1.0.0: src-sequence/Moonlight/Homology/Pure/Sequence/Spectral/Build.hs
module Moonlight.Homology.Pure.Sequence.Spectral.Build
( advanceSpectralPage,
buildEntryFromBases,
buildInitialSpectralPage,
buildConstantFiltrationSpectralFamily,
buildSpectralFamily,
buildSpectralPage,
buildSpectralPages,
filtrationWidth,
homologicalPageBound,
mkRationalSpectralSource,
)
where
import Data.Function ((&))
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Moonlight.Homology.Boundary.Finite
( FiniteChainComplex,
maxHomologicalDegree,
)
import Moonlight.Homology.Pure.Chain (HomologicalDegree (..), RepresentativeCocycle)
import Moonlight.Homology.Pure.Failure (HomologyFailure (..))
import Moonlight.Homology.Pure.Group (HomologyGroup (..))
import Moonlight.Homology.Pure.Sequence.Spectral.Bidegree
( Bidegree,
bidegreeCoordinates,
bidegreeFromTotalDegree,
bidegreeTotalDegree,
mkBidegree,
targetBidegreeAfterDifferential,
)
import Moonlight.Homology.Pure.Sequence.Spectral.Linear
( FiltrationPivot (..),
FiltrationReducedColumn (..),
FiltrationReduction (..),
boundaryMatrixAtRational,
filtrationOrderedReduction,
firstVectorOutsideSpan,
independentModuloBasis,
reduceBasisChecked,
reshapeSparseMatrix,
zeroSparseMatrix,
)
import Moonlight.Homology.Pure.Sequence.Spectral.Support
( supportWindowBidegree,
supportWindows,
)
import Moonlight.Homology.Pure.Sequence.Spectral.Types
( AmbientVector,
FiltrationFunction,
FormalMap (..),
SpectralChain (..),
SpectralEntry (..),
SpectralFamily (..),
SpectralPage (..),
SpectralSource,
mkSpectralSource,
spectralBaseComplex,
spectralLevelsByDegree,
spectralMaxLevel,
spectralMinLevel,
spectralSupportRegistry,
)
import Moonlight.Homology.Pure.Topology.Algebra
( QuotientPresentation,
mkQuotientPresentation,
vectorToRepresentative,
)
import Moonlight.Homology.Pure.Matrix.Shape (cellCountAtDegree)
import Moonlight.Homology.Pure.Matrix.SparseLinAlg
( SparseMatrix,
sparseRowToDense,
sparseTransposeMatrix,
)
data SpectralResolution = SpectralResolution
{ spectralResolutionSource :: !SpectralSource,
spectralResolutionPairs :: ![SpectralPair],
spectralResolutionUnpaired :: ![SpectralUnpairedGenerator]
}
data SpectralDegreeReduction = SpectralDegreeReduction
{ spectralDegreePairs :: ![SpectralPair],
spectralDegreeZeroColumns :: ![SpectralUnpairedGenerator]
}
data SpectralPairId = SpectralPairId
{ spectralPairIdDegree :: !HomologicalDegree,
spectralPairIdSourceIndex :: !Int
}
deriving stock (Eq, Ord, Show)
data SpectralPair = SpectralPair
{ spectralPairIdentifier :: !SpectralPairId,
spectralPairSourceLevel :: !Int,
spectralPairSourceVector :: !AmbientVector,
spectralPairTargetDegree :: !HomologicalDegree,
spectralPairTargetIndex :: !Int,
spectralPairTargetLevel :: !Int,
spectralPairTargetVector :: !AmbientVector,
spectralPairPage :: !Int
}
data SpectralUnpairedGenerator = SpectralUnpairedGenerator
{ spectralUnpairedDegree :: !HomologicalDegree,
spectralUnpairedIndex :: !Int,
spectralUnpairedLevel :: !Int,
spectralUnpairedVector :: !AmbientVector
}
newtype SpectralPairedTargetCell = SpectralPairedTargetCell (HomologicalDegree, Int)
deriving stock (Eq, Ord, Show)
data SpectralEndpointRole
= SpectralSourceEndpoint !SpectralPairId
| SpectralTargetEndpoint !SpectralPairId
| SpectralUnpairedEndpoint !HomologicalDegree !Int
deriving stock (Eq, Ord, Show)
data SpectralPageBasisVector = SpectralPageBasisVector
{ spectralPageBasisRole :: !SpectralEndpointRole,
spectralPageBasisDegree :: !HomologicalDegree,
spectralPageBasisLevel :: !Int,
spectralPageBasisAnchor :: !Int,
spectralPageBasisVector :: !AmbientVector
}
deriving stock (Eq, Show)
buildInitialSpectralPage :: SpectralSource -> Either HomologyFailure (SpectralPage Rational)
buildInitialSpectralPage source =
buildSpectralPage source 0
buildConstantFiltrationSpectralFamily :: SpectralSource -> Either HomologyFailure (SpectralFamily Rational)
buildConstantFiltrationSpectralFamily =
buildSpectralFamily
buildSpectralFamily :: SpectralSource -> Either HomologyFailure (SpectralFamily Rational)
buildSpectralFamily source = do
resolution <- buildSpectralResolution source
let pages = spectralPagesFromResolution resolution
case List.reverse pages of
limitPage : _ ->
Right
SpectralFamily
{ spectralFamilyPages = pages,
spectralFamilyStableFrom = stableFromResolution resolution,
spectralFamilyLimitPage = limitPage
}
[] ->
Left (BackendFailure "spectral family unexpectedly empty")
buildSpectralPages :: SpectralSource -> Either HomologyFailure [SpectralPage Rational]
buildSpectralPages source =
spectralPagesFromResolution <$> buildSpectralResolution source
spectralPagesFromResolution :: SpectralResolution -> [SpectralPage Rational]
spectralPagesFromResolution resolution =
fmap
(spectralPageFromResolution resolution)
[0 .. homologicalPageBound (spectralResolutionSource resolution)]
buildSpectralPage :: SpectralSource -> Int -> Either HomologyFailure (SpectralPage Rational)
buildSpectralPage source pageNumber =
spectralPageFromResolution <$> buildSpectralResolution source <*> pure pageNumber
advanceSpectralPage :: SpectralPage Rational -> Either HomologyFailure (SpectralPage Rational)
advanceSpectralPage page =
case pageAdvanceSource page of
Just source -> buildSpectralPage source (pageIndex page + 1)
Nothing ->
case pageAdvanceState page of
Just chain -> buildSpectralPage (spectralChainSource chain) (pageIndex page + 1)
Nothing -> Left (BackendFailure "spectral page cannot advance without source data")
spectralPageFromResolution :: SpectralResolution -> Int -> SpectralPage Rational
spectralPageFromResolution resolution pageNumber =
let source = spectralResolutionSource resolution
basisByBidegree = pageBasisByBidegree resolution pageNumber
entries = buildEntriesFromPageBasis source basisByBidegree
differentials = buildDifferentialsFromPageBasis resolution pageNumber basisByBidegree
in spectralPageFromEntries source pageNumber entries differentials
spectralPageFromEntries ::
SpectralSource ->
Int ->
Map.Map Bidegree (SpectralEntry Rational) ->
Map.Map Bidegree (FormalMap Rational) ->
SpectralPage Rational
spectralPageFromEntries source pageNumber entries differentials =
let zeroGroup :: HomologyGroup Rational
zeroGroup = HomologyGroup {freeRank = 0, torsionInvariants = []}
zeroFormalMap :: FormalMap Rational
zeroFormalMap = FormalMap [] [] []
in SpectralPage
{ pageIndex = pageNumber,
groupAt =
\filtrationDegreeValue complementaryDegreeValue ->
Map.lookup (mkBidegree filtrationDegreeValue complementaryDegreeValue) entries
& maybe zeroGroup entryGroupValue,
diffMap =
\filtrationDegreeValue complementaryDegreeValue ->
Map.findWithDefault zeroFormalMap (mkBidegree filtrationDegreeValue complementaryDegreeValue) differentials,
pageEntryMap = entries,
pageDifferentialMap = differentials,
pageAdvanceSource = Just source,
pageAdvanceState = Nothing
}
buildSpectralResolution :: SpectralSource -> Either HomologyFailure SpectralResolution
buildSpectralResolution source = do
degreeReductions <- traverse (buildSpectralDegreeReduction source) (Map.keys (spectralLevelsByDegree source))
let pairs = degreeReductions >>= spectralDegreePairs
pairedTargets = Set.fromList (fmap pairedTargetCell pairs)
unpairedGenerators =
degreeReductions
>>= spectralDegreeZeroColumns
& filter (not . (`Set.member` pairedTargets) . unpairedTargetCell)
pure
SpectralResolution
{ spectralResolutionSource = source,
spectralResolutionPairs = pairs,
spectralResolutionUnpaired = unpairedGenerators
}
buildSpectralDegreeReduction :: SpectralSource -> HomologicalDegree -> Either HomologyFailure SpectralDegreeReduction
buildSpectralDegreeReduction source sourceDegree = do
matrixValue <- safeCoboundaryMatrixAt source sourceDegree
reductionValue <-
filtrationOrderedReduction
(levelsAtDegree source sourceDegree)
(levelsAtDegree source (successorDegree sourceDegree))
matrixValue
pure
SpectralDegreeReduction
{ spectralDegreePairs = filtrationReductionColumns reductionValue >>= spectralPairFromReducedColumn sourceDegree,
spectralDegreeZeroColumns = filtrationReductionColumns reductionValue >>= spectralZeroColumnFromReducedColumn sourceDegree
}
spectralPairFromReducedColumn :: HomologicalDegree -> FiltrationReducedColumn -> [SpectralPair]
spectralPairFromReducedColumn sourceDegree columnValue =
case filtrationColumnPivot columnValue of
Nothing -> []
Just pivotValue ->
[ SpectralPair
{ spectralPairIdentifier =
SpectralPairId
{ spectralPairIdDegree = sourceDegree,
spectralPairIdSourceIndex = filtrationColumnSourceIndex columnValue
},
spectralPairSourceLevel = filtrationColumnSourceLevel columnValue,
spectralPairSourceVector = filtrationColumnSourceVector columnValue,
spectralPairTargetDegree = successorDegree sourceDegree,
spectralPairTargetIndex = filtrationPivotTargetIndex pivotValue,
spectralPairTargetLevel = filtrationPivotTargetLevel pivotValue,
spectralPairTargetVector = filtrationColumnTargetVector columnValue,
spectralPairPage = filtrationPivotDistance pivotValue
}
]
spectralZeroColumnFromReducedColumn :: HomologicalDegree -> FiltrationReducedColumn -> [SpectralUnpairedGenerator]
spectralZeroColumnFromReducedColumn degreeValue columnValue =
case filtrationColumnPivot columnValue of
Just _ -> []
Nothing ->
[ SpectralUnpairedGenerator
{ spectralUnpairedDegree = degreeValue,
spectralUnpairedIndex = filtrationColumnSourceIndex columnValue,
spectralUnpairedLevel = filtrationColumnSourceLevel columnValue,
spectralUnpairedVector = filtrationColumnSourceVector columnValue
}
]
pairedTargetCell :: SpectralPair -> SpectralPairedTargetCell
pairedTargetCell pairValue =
SpectralPairedTargetCell
(spectralPairTargetDegree pairValue, spectralPairTargetIndex pairValue)
unpairedTargetCell :: SpectralUnpairedGenerator -> SpectralPairedTargetCell
unpairedTargetCell generatorValue =
SpectralPairedTargetCell
(spectralUnpairedDegree generatorValue, spectralUnpairedIndex generatorValue)
stableFromResolution :: SpectralResolution -> Int
stableFromResolution resolution
| filtrationWidth (spectralResolutionSource resolution) == 0 = 1
| otherwise =
case fmap spectralPairPage (spectralResolutionPairs resolution) of
[] -> 0
firstPage : remainingPages -> List.foldl' max firstPage remainingPages + 1
pageBasisByBidegree :: SpectralResolution -> Int -> Map.Map Bidegree [SpectralPageBasisVector]
pageBasisByBidegree resolution pageNumber =
let source = spectralResolutionSource resolution
groupedBasis =
activePageBasis resolution pageNumber
& fmap (\basisValue -> (pageBasisBidegree basisValue, [basisValue]))
& Map.fromListWith (<>)
& fmap sortPageBasis
in supportWindows (spectralSupportRegistry source)
& fmap
( \windowValue ->
let bidegreeValue = supportWindowBidegree windowValue
in (bidegreeValue, Map.findWithDefault [] bidegreeValue groupedBasis)
)
& Map.fromList
activePageBasis :: SpectralResolution -> Int -> [SpectralPageBasisVector]
activePageBasis resolution pageNumber =
sortPageBasis
( (spectralResolutionPairs resolution >>= activePairEndpoints pageNumber)
<> fmap unpairedPageBasis (spectralResolutionUnpaired resolution)
)
activePairEndpoints :: Int -> SpectralPair -> [SpectralPageBasisVector]
activePairEndpoints pageNumber pairValue =
if pageNumber <= spectralPairPage pairValue
then
[ SpectralPageBasisVector
{ spectralPageBasisRole = SpectralSourceEndpoint (spectralPairIdentifier pairValue),
spectralPageBasisDegree = spectralPairIdDegree (spectralPairIdentifier pairValue),
spectralPageBasisLevel = spectralPairSourceLevel pairValue,
spectralPageBasisAnchor = spectralPairIdSourceIndex (spectralPairIdentifier pairValue),
spectralPageBasisVector = spectralPairSourceVector pairValue
},
SpectralPageBasisVector
{ spectralPageBasisRole = SpectralTargetEndpoint (spectralPairIdentifier pairValue),
spectralPageBasisDegree = spectralPairTargetDegree pairValue,
spectralPageBasisLevel = spectralPairTargetLevel pairValue,
spectralPageBasisAnchor = spectralPairTargetIndex pairValue,
spectralPageBasisVector = spectralPairTargetVector pairValue
}
]
else []
unpairedPageBasis :: SpectralUnpairedGenerator -> SpectralPageBasisVector
unpairedPageBasis generatorValue =
SpectralPageBasisVector
{ spectralPageBasisRole = SpectralUnpairedEndpoint (spectralUnpairedDegree generatorValue) (spectralUnpairedIndex generatorValue),
spectralPageBasisDegree = spectralUnpairedDegree generatorValue,
spectralPageBasisLevel = spectralUnpairedLevel generatorValue,
spectralPageBasisAnchor = spectralUnpairedIndex generatorValue,
spectralPageBasisVector = spectralUnpairedVector generatorValue
}
pageBasisBidegree :: SpectralPageBasisVector -> Bidegree
pageBasisBidegree basisValue =
bidegreeFromTotalDegree
(spectralPageBasisLevel basisValue)
(spectralPageBasisDegree basisValue)
sortPageBasis :: [SpectralPageBasisVector] -> [SpectralPageBasisVector]
sortPageBasis =
List.sortOn
( \basisValue ->
( spectralPageBasisDegree basisValue,
spectralPageBasisLevel basisValue,
spectralPageBasisAnchor basisValue,
spectralPageBasisRole basisValue
)
)
buildEntriesFromPageBasis :: SpectralSource -> Map.Map Bidegree [SpectralPageBasisVector] -> Map.Map Bidegree (SpectralEntry Rational)
buildEntriesFromPageBasis source basisByBidegree =
supportWindows (spectralSupportRegistry source)
& fmap
( \windowValue ->
let bidegreeValue = supportWindowBidegree windowValue
in ( bidegreeValue,
buildEntryFromPageBasis
source
bidegreeValue
(Map.findWithDefault [] bidegreeValue basisByBidegree)
)
)
& Map.fromList
buildEntryFromPageBasis :: SpectralSource -> Bidegree -> [SpectralPageBasisVector] -> SpectralEntry Rational
buildEntryFromPageBasis source bidegreeValue basisVectors =
let ambientDimension = degreeDimension source (bidegreeTotalDegree bidegreeValue)
denseBasis = fmap (sparseRowToDense ambientDimension . spectralPageBasisVector) basisVectors
representatives = fmap (basisRepresentativeValue source) basisVectors
presentation = mkQuotientPresentation ambientDimension denseBasis representatives []
in SpectralEntry
{ entryPresentation = presentation,
entryGroupValue =
HomologyGroup
{ freeRank = length basisVectors,
torsionInvariants = []
}
}
buildDifferentialsFromPageBasis :: SpectralResolution -> Int -> Map.Map Bidegree [SpectralPageBasisVector] -> Map.Map Bidegree (FormalMap Rational)
buildDifferentialsFromPageBasis resolution pageNumber basisByBidegree =
supportWindows (spectralSupportRegistry (spectralResolutionSource resolution))
& fmap
( \windowValue ->
let bidegreeValue = supportWindowBidegree windowValue
in (bidegreeValue, buildDifferentialFromPageBasis resolution pageNumber basisByBidegree bidegreeValue)
)
& Map.fromList
buildDifferentialFromPageBasis ::
SpectralResolution ->
Int ->
Map.Map Bidegree [SpectralPageBasisVector] ->
Bidegree ->
FormalMap Rational
buildDifferentialFromPageBasis resolution pageNumber basisByBidegree sourceBidegree =
let source = spectralResolutionSource resolution
targetBidegree = targetBidegreeAfterDifferential pageNumber sourceBidegree
domainBasis = Map.findWithDefault [] sourceBidegree basisByBidegree
codomainBasis = Map.findWithDefault [] targetBidegree basisByBidegree
livePairIds = differentialPairIdsAt resolution pageNumber sourceBidegree
in FormalMap
{ formalMatrix =
fmap
( \targetBasisValue ->
fmap
(\domainBasisValue -> differentialCoefficient livePairIds domainBasisValue targetBasisValue)
domainBasis
)
codomainBasis,
formalDomainBasis = fmap (basisRepresentativeValue source) domainBasis,
formalCodomainBasis = fmap (basisRepresentativeValue source) codomainBasis
}
differentialPairIdsAt :: SpectralResolution -> Int -> Bidegree -> Set.Set SpectralPairId
differentialPairIdsAt resolution pageNumber sourceBidegree =
spectralResolutionPairs resolution
& filter
( \pairValue ->
spectralPairPage pairValue == pageNumber
&& pairSourceBidegree pairValue == sourceBidegree
)
& fmap spectralPairIdentifier
& Set.fromList
differentialCoefficient :: Set.Set SpectralPairId -> SpectralPageBasisVector -> SpectralPageBasisVector -> Rational
differentialCoefficient livePairIds domainBasisValue targetBasisValue =
case (spectralPageBasisRole domainBasisValue, spectralPageBasisRole targetBasisValue) of
(SpectralSourceEndpoint sourcePairId, SpectralTargetEndpoint targetPairId)
| sourcePairId == targetPairId && Set.member sourcePairId livePairIds -> 1
_ -> 0
pairSourceBidegree :: SpectralPair -> Bidegree
pairSourceBidegree pairValue =
bidegreeFromTotalDegree
(spectralPairSourceLevel pairValue)
(spectralPairIdDegree (spectralPairIdentifier pairValue))
basisRepresentativeValue :: SpectralSource -> SpectralPageBasisVector -> RepresentativeCocycle Rational Int
basisRepresentativeValue source basisValue =
let degreeValue = spectralPageBasisDegree basisValue
ambientDimension = degreeDimension source degreeValue
in vectorToRepresentative
degreeValue
(sparseRowToDense ambientDimension (spectralPageBasisVector basisValue))
buildEntryFromBases ::
Bidegree ->
Int ->
[AmbientVector] ->
[AmbientVector] ->
Either HomologyFailure (SpectralEntry Rational)
buildEntryFromBases bidegreeValue ambientDimension numeratorBasis denominatorBasis = do
reducedNumerator <- reduceBasisChecked ambientDimension numeratorBasis
reducedDenominator <- reduceBasisChecked ambientDimension denominatorBasis
assertDenominatorSubset bidegreeValue ambientDimension reducedNumerator reducedDenominator
let quotientBasis = independentModuloBasis ambientDimension reducedDenominator reducedNumerator
presentation =
presentationFromSparseBases
(bidegreeTotalDegree bidegreeValue)
ambientDimension
quotientBasis
reducedDenominator
groupValue =
HomologyGroup
{ freeRank = length quotientBasis,
torsionInvariants = []
}
pure
SpectralEntry
{ entryPresentation = presentation,
entryGroupValue = groupValue
}
presentationFromSparseBases ::
HomologicalDegree ->
Int ->
[AmbientVector] ->
[AmbientVector] ->
QuotientPresentation Rational
presentationFromSparseBases degreeValue ambientDimension quotientBasis denominatorBasis =
let basisVectors = fmap (sparseRowToDense ambientDimension) quotientBasis
denominatorVectors = fmap (sparseRowToDense ambientDimension) denominatorBasis
basisRepresentatives = fmap (vectorToRepresentative degreeValue) basisVectors
in mkQuotientPresentation
ambientDimension
basisVectors
basisRepresentatives
denominatorVectors
assertDenominatorSubset ::
Bidegree ->
Int ->
[AmbientVector] ->
[AmbientVector] ->
Either HomologyFailure ()
assertDenominatorSubset bidegreeValue ambientDimension numeratorBasis denominatorBasis =
case firstVectorOutsideSpan ambientDimension numeratorBasis denominatorBasis of
Nothing -> Right ()
Just denominatorVector ->
Left
( SpectralQuotientDenominatorNotSubspace
(bidegreeCoordinates bidegreeValue)
ambientDimension
(sparseRowToDense ambientDimension denominatorVector)
)
filtrationWidth :: SpectralSource -> Int
filtrationWidth source =
max 0 (spectralMaxLevel source - spectralMinLevel source)
homologicalPageBound :: SpectralSource -> Int
homologicalPageBound source =
filtrationWidth source + 1
mkRationalSpectralSource :: FiniteChainComplex Rational -> FiltrationFunction -> Either HomologyFailure SpectralSource
mkRationalSpectralSource =
mkSpectralSource
degreeDimension :: SpectralSource -> HomologicalDegree -> Int
degreeDimension source degreeValue =
cellCountAtDegree (spectralBaseComplex source) degreeValue
levelsAtDegree :: SpectralSource -> HomologicalDegree -> [Int]
levelsAtDegree source degreeValue =
Map.findWithDefault [] degreeValue (spectralLevelsByDegree source)
successorDegree :: HomologicalDegree -> HomologicalDegree
successorDegree (HomologicalDegree degreeValue) =
HomologicalDegree (degreeValue + 1)
safeCoboundaryMatrixAt ::
SpectralSource ->
HomologicalDegree ->
Either HomologyFailure SparseMatrix
safeCoboundaryMatrixAt source degreeValue
| unHomologicalDegree degreeValue < 0 =
zeroSparseMatrix
(degreeDimension source (successorDegree degreeValue))
0
| unHomologicalDegree degreeValue >= unHomologicalDegree (maxHomologicalDegree (spectralBaseComplex source)) =
zeroSparseMatrix
0
(degreeDimension source degreeValue)
| otherwise = do
let targetDegreeValue = successorDegree degreeValue
boundaryRowCount = degreeDimension source degreeValue
boundaryColumnCount = degreeDimension source targetDegreeValue
boundaryMatrix <-
boundaryMatrixAtRational (spectralBaseComplex source) targetDegreeValue
>>= reshapeSparseMatrix boundaryRowCount boundaryColumnCount
pure (sparseTransposeMatrix boundaryMatrix)