moonlight-core-0.1.0.0: bench/solver/SolverBench.hs
{-# LANGUAGE RankNTypes #-}
module SolverBench
( solverBenchmarks,
)
where
import Control.DeepSeq
( NFData (..),
)
import Control.Monad.ST
( ST,
)
import Data.Equivalence.Monad qualified as Equivalence
import Data.Foldable
( traverse_,
)
import Data.Graph qualified as Graph
import Data.IntMap.Strict
( IntMap,
)
import Data.IntMap.Strict qualified as IntMap
import Data.IntSet
( IntSet,
)
import Data.IntSet qualified as IntSet
import Data.Vector qualified as Vector
import BenchSupport
( caseLabel,
foundationSizes,
keys,
sampleKeys,
showLength,
unionFindSizes,
)
import Moonlight.Core
( DeltaDomain (..),
EquationId (..),
Evaluation,
closureUnderInt,
fixpointBounded,
readEquationValue,
resultValues,
solveDenseMonotone,
traverseOnceIntSet,
)
import Moonlight.Core.Fixpoint.Dense
( Csr,
csrOffsets,
csrTargets,
csrTranspose,
csrVertexCount,
FrozenDigraph,
graphBackward,
graphForward,
graphSccPlan,
SccClosureCache,
SccPlan,
condensation,
condensationBackward,
sccMembers,
sccOfVertex,
sccClosureCacheFor,
Edge (..),
frozenDigraphFromSuccessors,
frozenReachabilityFrom,
frozenReachabilityWithCache,
snapshotFromFrozen,
snapshotReachabilityFrom,
insertSnapshotEdge,
)
import Moonlight.Core
( ClassId (..),
classIdKey,
)
import Moonlight.Core qualified as UF
import Moonlight.Core qualified as UFT
import Test.Tasty.Bench
( Benchmark,
bench,
bgroup,
env,
nf,
)
import Prelude
newtype ContainersUnionFind = ContainersUnionFind (IntMap Int)
newtype ForcedFrozenDigraph = ForcedFrozenDigraph FrozenDigraph
instance NFData ForcedFrozenDigraph where
rnf (ForcedFrozenDigraph graph) =
rnfFrozenDigraph graph
newtype ForcedGraph = ForcedGraph Graph.Graph
instance NFData ForcedGraph where
rnf (ForcedGraph graph) =
rnf (Graph.vertices graph, Graph.edges graph)
data ForcedSccClosureCache = ForcedSccClosureCache
{ forcedCacheReceipt :: !Int,
forcedCache :: !SccClosureCache
}
instance NFData ForcedSccClosureCache where
rnf cache =
rnf (forcedCacheReceipt cache)
solverBenchmarks :: Benchmark
solverBenchmarks =
bgroup
"solver"
[ unionFindBenchmarks,
fixpointBenchmarks
]
unionFindBenchmarks :: Benchmark
unionFindBenchmarks =
bgroup
"union-find"
( (unionFindSizes >>= unionFindBenchmarksForSize)
<> (unionFindSizes >>= unionFindKeyDistributionBenchmarksForSize)
)
unionFindBenchmarksForSize :: Int -> [Benchmark]
unionFindBenchmarksForSize size =
[ bench (caseLabel "insert+canonical compress" size) (nf unionFindBuildWeight size),
env (pure (keys size)) $ \ids ->
bench (caseLabel "union chain/compress" size) (nf unionFindChainWeight ids),
bench (caseLabel "balanced union/compress" size) (nf balancedUnionFindCompressSizeWeight size),
bench (caseLabel "transaction balanced union/compress" size) (nf transactionBalancedUnionCompressWeight size),
bench (caseLabel "transaction prefix+sparse-outlier" size) (nf transactionSparseOutlierWeight size),
bench (caseLabel "balanced build+cold find sweep" size) (nf balancedColdFindSweepSizeWeight size),
bench (caseLabel "transaction build+cold find sweep" size) (nf transactionColdFindSweepSizeWeight size),
bench (caseLabel "balanced build+compressed find sweep" size) (nf balancedCompressedFindSweepSizeWeight size),
bench (caseLabel "paired build/equivalence sweep" size) (nf pairedUnionFindEquivalenceWeight size),
bench (caseLabel "world: containers parent-map balanced union/canonical" size) (nf containersUnionFindBalancedWeight size),
bench (caseLabel "hackage: equivalence balanced union/equivalence sweep" size) (nf hackageEquivalenceUnionFindWeight size)
]
unionFindKeyDistributionBenchmarksForSize :: Int -> [Benchmark]
unionFindKeyDistributionBenchmarksForSize size =
[ env (pure (holeyKeys size)) $ \ids ->
bench (caseLabel "canonical sweep/50pct holes" size) (nf unionFindCanonicalSweepWeight ids),
env (pure (negativeKeys size)) $ \ids ->
bench (caseLabel "canonical sweep/negative keys" size) (nf unionFindCanonicalSweepWeight ids),
env (pure (densePrefixWithGiantOutlier size)) $ \ids ->
bench (caseLabel "canonical sweep/dense prefix + giant outlier" size) (nf unionFindCanonicalSweepWeight ids),
env (pure (mixedDenseSparseKeys size)) $ \ids ->
bench (caseLabel "canonical sweep/mixed dense sparse" size) (nf unionFindCanonicalSweepWeight ids)
]
fixpointBenchmarks :: Benchmark
fixpointBenchmarks =
bgroup
"fixpoint-worklists"
( (foundationSizes >>= fixpointBenchmarksForSize)
<> [bench (caseLabel "solveDenseMonotone one-read chain" 16000) (nf denseMonotoneSolverWeight 16000)]
<> [ env (pure (hotSingletonCache 100000)) $ \cache ->
bench (caseLabel "frozenReachabilityWithCache hot singleton isolated" 100000) (nf hotSingletonCacheWeight cache)
]
<> (foundationSizes >>= variableDegreeReachabilityBenchmarksForSize)
)
fixpointBenchmarksForSize :: Int -> [Benchmark]
fixpointBenchmarksForSize size =
[ bench (caseLabel "solveDenseMonotone one-read chain" size) (nf denseMonotoneSolverWeight size),
bench (caseLabel "closureUnderInt fanout=2" size) (nf closureWeight size),
bench (caseLabel "traverseOnceIntSet fanout=2" size) (nf worklistWeight size),
bench (caseLabel "frozenDigraph build+reach fanout=2" size) (nf denseBuildClosureWeight size),
env (pure (forcedFrozenDigraphForSize size)) $ \denseRelation ->
bench (caseLabel "csrTranspose/prebuilt fanout=2" size) (nf denseTransposeWeight denseRelation),
env (pure (forcedFrozenDigraphForSize size)) $ \denseRelation ->
bench (caseLabel "frozenReachabilityFrom/prebuilt fanout=2" size) (nf denseClosureWeight denseRelation),
env (pure (forcedFrozenDigraphForSize size)) $ \denseRelation ->
bench (caseLabel "frozenReachabilityFrom/prebuilt repeated fanout=2" size) (nf denseRepeatedClosureWeight denseRelation),
bench (caseLabel "frozenDigraph build+reach cyclic fanout=2" size) (nf denseCyclicBuildClosureWeight size),
env (pure (forcedFrozenCyclicDigraphForSize size)) $ \denseRelation ->
bench (caseLabel "frozenReachabilityFrom/prebuilt cyclic fanout=2" size) (nf denseClosureWeight denseRelation),
bench (caseLabel "frozenDigraph build+reach scc-heavy" size) (nf denseSccHeavyBuildClosureWeight size),
env (pure (forcedFrozenSccHeavyDigraphForSize size)) $ \denseRelation ->
bench (caseLabel "frozenReachabilityFrom/prebuilt scc-heavy" size) (nf denseClosureWeight denseRelation),
env (pure (forcedFrozenSccHeavyDigraphForSize size)) $ \denseRelation ->
bench (caseLabel "frozenReachabilityWithCache cold/hot scc-heavy" size) (nf denseCachedClosureWeight denseRelation),
bench (caseLabel "graphSnapshot overlay reachability" size) (nf graphSnapshotOverlayReachabilityWeight size),
bench (caseLabel "world: containers IntSet reachability fanout=2" size) (nf containersReachabilityWeight size),
bench (caseLabel "hackage: containers Data.Graph build+reachable fanout=2" size) (nf hackageDataGraphBuildReachabilityWeight size),
env (pure (forcedHackageDataGraphForSize size)) $ \graph ->
bench (caseLabel "hackage: containers Data.Graph reachable/prebuilt fanout=2" size) (nf hackageDataGraphReachabilityWeight graph),
bench (caseLabel "fixpointBounded decrement" size) (nf boundedFixpointWeight size)
]
variableDegreeReachabilityBenchmarksForSize :: Int -> [Benchmark]
variableDegreeReachabilityBenchmarksForSize size =
[ bench (caseLabel "frozenDigraph build+reach var-degree" size) (nf (denseBuildClosureWith variableDegreeSuccessors) size),
env (pure (forcedFrozenDigraphWith variableDegreeSuccessors size)) $ \denseRelation ->
bench (caseLabel "csrTranspose/prebuilt var-degree" size) (nf denseTransposeWeight denseRelation),
env (pure (forcedFrozenDigraphWith variableDegreeSuccessors size)) $ \denseRelation ->
bench (caseLabel "frozenReachabilityFrom/prebuilt var-degree" size) (nf denseClosureWeight denseRelation),
bench (caseLabel "world: containers IntSet reachability var-degree" size) (nf (containersReachabilityWith variableDegreeSuccessors) size),
bench (caseLabel "hackage: containers Data.Graph build+reachable var-degree" size) (nf (hackageDataGraphBuildReachabilityWith variableDegreeSuccessors) size),
env (pure (forcedHackageDataGraphWith variableDegreeSuccessors size)) $ \graph ->
bench (caseLabel "hackage: containers Data.Graph reachable/prebuilt var-degree" size) (nf hackageDataGraphReachabilityWeight graph)
]
denseMonotoneSolverWeight :: Int -> Int
denseMonotoneSolverWeight size =
either
(const (-1))
(Vector.sum . resultValues)
(solveDenseMonotone intGrowthDeltaDomain size denseEvaluation (const 0))
denseEvaluation :: Int -> Evaluation Int Int
denseEvaluation key
| key <= 0 =
pure 1
| otherwise =
readEquationValue (EquationId (key - 1))
intGrowthDeltaDomain :: DeltaDomain Int Int
intGrowthDeltaDomain =
DeltaDomain
{ deltaEmpty = 0,
deltaNull = (== 0),
deltaMerge = max,
deltaApply = (+),
deltaBetween = \oldValue newValue -> max 0 (newValue - oldValue)
}
unionFindBuildWeight :: Int -> Int
unionFindBuildWeight =
canonicalMapDigest . fst . UF.canonicalMapAndCompress . UF.fromClassIds . classIds
unionFindChainWeight :: [Int] -> Int
unionFindChainWeight ids =
canonicalMapDigest (fst (UF.canonicalMapAndCompress (unionChain (fmap ClassId ids))))
unionFindCanonicalSweepWeight :: [Int] -> Int
unionFindCanonicalSweepWeight =
canonicalMapDigest . fst . UF.canonicalMapAndCompress . UF.fromClassIds . fmap ClassId
balancedUnionFindCompressSizeWeight :: Int -> Int
balancedUnionFindCompressSizeWeight =
canonicalMapDigest . fst . UF.canonicalMapAndCompress . balancedUnionFind
transactionBalancedUnionCompressWeight :: Int -> Int
transactionBalancedUnionCompressWeight size =
transactionCanonicalDigest UF.emptyUnionFind $ \editor ->
traverse_
(\(leftClassId, rightClassId) -> UFT.transactionUnion editor leftClassId rightClassId)
(balancedUnionPairs size)
transactionSparseOutlierWeight :: Int -> Int
transactionSparseOutlierWeight size =
let ids = classIds size
outlier = ClassId 1000000000
in transactionCanonicalDigest UF.emptyUnionFind $ \editor -> do
traverse_ (UFT.transactionInsertClassId editor) ids
UFT.transactionInsertClassId editor outlier
case ids of
firstClassId : _ -> do
_ <- UFT.transactionUnion editor outlier firstClassId
pure ()
[] ->
pure ()
transactionColdFindSweepSizeWeight :: Int -> Int
transactionColdFindSweepSizeWeight size =
transactionCanonicalDigest (balancedUnionFind size) $ \editor ->
traverse_ (UFT.transactionFind editor) (classIds size)
transactionCanonicalDigest :: UF.UnionFind -> (forall state. UFT.UnionFindEditor state -> ST state ()) -> Int
transactionCanonicalDigest unionFind action =
let (digest, _) =
UFT.runUnionFindTransaction unionFind $ \editor -> do
action editor
canonicalParents <- UFT.transactionCanonicalMapAndCompress editor
pure (canonicalMapDigest canonicalParents)
in digest
canonicalMapDigest :: IntMap ClassId -> Int
canonicalMapDigest =
IntMap.foldlWithKey'
(\digest key classId -> digest * 16777619 + key * 31 + classIdKey classId)
146959810
balancedColdFindSweepSizeWeight :: Int -> Int
balancedColdFindSweepSizeWeight size =
unionFindFindSweepWeight size (balancedUnionFind size)
balancedCompressedFindSweepSizeWeight :: Int -> Int
balancedCompressedFindSweepSizeWeight size =
unionFindFindSweepWeight size (snd (UF.canonicalMapAndCompress (balancedUnionFind size)))
unionFindFindSweepWeight :: Int -> UF.UnionFind -> Int
unionFindFindSweepWeight size unionFind =
canonicalMapDigest (snd (foldl' findSweepStep (unionFind, IntMap.empty) (classIds size)))
findSweepStep :: (UF.UnionFind, IntMap ClassId) -> ClassId -> (UF.UnionFind, IntMap ClassId)
findSweepStep (unionFind, roots) classId =
let (rootClassId, compressedUnionFind) = UF.find classId unionFind
in (compressedUnionFind, IntMap.insert (classIdKey classId) rootClassId roots)
pairedUnionFindEquivalenceWeight :: Int -> Int
pairedUnionFindEquivalenceWeight size =
let unionFind = pairedUnionFind size
in length
[ ()
| (leftKey, rightKey) <- equivalenceQueryPairs size,
UF.equivalent (ClassId leftKey) (ClassId rightKey) unionFind
]
containersUnionFindBalancedWeight :: Int -> Int
containersUnionFindBalancedWeight =
containersParentDigest . containersCanonicalParents . containersBalancedUnionFind
hackageEquivalenceUnionFindWeight :: Int -> Int
hackageEquivalenceUnionFindWeight size =
Equivalence.runEquivM' $ do
traverse_ Equivalence.getClass (keys size)
traverse_
( \(ClassId leftKey, ClassId rightKey) ->
Equivalence.equate leftKey rightKey
)
(balancedUnionPairs size)
equivalenceResults <-
traverse
(uncurry Equivalence.equivalent)
(equivalenceQueryPairs size)
pure (length (filter id equivalenceResults))
equivalenceQueryPairs :: Int -> [(Int, Int)]
equivalenceQueryPairs size =
[ (leftKey, (leftKey + max 1 (size `div` 2)) `mod` max 1 size)
| leftKey <- keys size
]
containersBalancedUnionFind :: Int -> ContainersUnionFind
containersBalancedUnionFind size =
containersUnionPairsFrom (containersFromKeys (keys size)) (balancedUnionPairs size)
containersFromKeys :: [Int] -> ContainersUnionFind
containersFromKeys =
ContainersUnionFind . IntMap.fromAscList . fmap (\key -> (key, key))
containersUnionPairsFrom :: ContainersUnionFind -> [(ClassId, ClassId)] -> ContainersUnionFind
containersUnionPairsFrom =
foldl'
( \unionFind (ClassId leftKey, ClassId rightKey) ->
containersUnion leftKey rightKey unionFind
)
containersUnion :: Int -> Int -> ContainersUnionFind -> ContainersUnionFind
containersUnion leftKey rightKey unionFind@(ContainersUnionFind parents)
| leftRoot == rightRoot = unionFind
| otherwise = ContainersUnionFind (IntMap.insert rightRoot leftRoot parents)
where
leftRoot =
containersFindRoot leftKey unionFind
rightRoot =
containersFindRoot rightKey unionFind
containersCanonicalParents :: ContainersUnionFind -> IntMap Int
containersCanonicalParents unionFind@(ContainersUnionFind parents) =
IntMap.mapWithKey (\key _parent -> containersFindRoot key unionFind) parents
containersFindRoot :: Int -> ContainersUnionFind -> Int
containersFindRoot key (ContainersUnionFind parents) =
case IntMap.lookup key parents of
Just parentKey
| parentKey /= key -> containersFindRoot parentKey (ContainersUnionFind parents)
_ -> key
containersParentDigest :: IntMap Int -> Int
containersParentDigest =
IntMap.foldlWithKey'
(\digest key rootKey -> digest * 16777619 + key * 31 + rootKey)
146959810
unionChain :: [ClassId] -> UF.UnionFind
unionChain ids =
foldl'
(\unionFind (leftClassId, rightClassId) -> UF.union leftClassId rightClassId unionFind)
(UF.fromClassIds ids)
(zip ids (drop 1 ids))
balancedUnionFind :: Int -> UF.UnionFind
balancedUnionFind =
unionPairsFrom UF.emptyUnionFind . balancedUnionPairs
unionPairsFrom :: UF.UnionFind -> [(ClassId, ClassId)] -> UF.UnionFind
unionPairsFrom =
foldl' (\unionFind (leftClassId, rightClassId) -> UF.union leftClassId rightClassId unionFind)
balancedUnionPairs :: Int -> [(ClassId, ClassId)]
balancedUnionPairs size =
foldMap (balancedPairsForStride size) (takeWhile (< size) (iterate (* 2) 1))
balancedPairsForStride :: Int -> Int -> [(ClassId, ClassId)]
balancedPairsForStride size stride =
fmap
(\leftKey -> (ClassId leftKey, ClassId (leftKey + stride)))
[0, 2 * stride .. size - stride - 1]
pairedUnionFind :: Int -> UF.UnionFind
pairedUnionFind size =
unionPairsFrom (UF.fromClassIds (classIds size)) (pairedClassIds size)
pairedClassIds :: Int -> [(ClassId, ClassId)]
pairedClassIds size =
fmap
(\key -> (ClassId key, ClassId (key + 1)))
[0, 2 .. size - 2]
closureWeight :: Int -> Int
closureWeight size =
IntSet.size (closureUnderInt (successors size) (IntSet.singleton 0))
worklistWeight :: Int -> Int
worklistWeight size =
IntSet.size
( traverseOnceIntSet
(\visited item -> (IntSet.insert item visited, IntSet.difference (successors size item) visited))
IntSet.empty
(IntSet.singleton 0)
)
denseClosureWeight :: ForcedFrozenDigraph -> Int
denseClosureWeight (ForcedFrozenDigraph relation) =
IntSet.size (frozenReachabilityFrom relation (IntSet.singleton 0))
denseTransposeWeight :: ForcedFrozenDigraph -> Int
denseTransposeWeight (ForcedFrozenDigraph relation) =
let transposed = csrTranspose (graphForward relation)
in rnfCsr transposed `seq` csrVertexCount transposed
denseRepeatedClosureWeight :: ForcedFrozenDigraph -> Int
denseRepeatedClosureWeight (ForcedFrozenDigraph relation) =
sum
[ IntSet.size (frozenReachabilityFrom relation (IntSet.singleton rootKey))
| rootKey <- repeatedQueryRoots (csrVertexCount (graphForward relation))
]
repeatedQueryRoots :: Int -> [Int]
repeatedQueryRoots size =
[0, 4 .. size - 1]
denseBuildClosureWeight :: Int -> Int
denseBuildClosureWeight =
denseBuildClosureWith successors
denseBuildClosureWith :: (Int -> Int -> IntSet) -> Int -> Int
denseBuildClosureWith successorsOf size =
denseClosureWeight (forcedFrozenDigraphWith successorsOf size)
forcedFrozenDigraphForSize :: Int -> ForcedFrozenDigraph
forcedFrozenDigraphForSize =
forcedFrozenDigraphWith successors
forcedFrozenDigraphWith :: (Int -> Int -> IntSet) -> Int -> ForcedFrozenDigraph
forcedFrozenDigraphWith successorsOf size =
ForcedFrozenDigraph (frozenDigraphFromSuccessors size (successorsOf size))
denseCyclicBuildClosureWeight :: Int -> Int
denseCyclicBuildClosureWeight size =
denseClosureWeight (forcedFrozenCyclicDigraphForSize size)
forcedFrozenCyclicDigraphForSize :: Int -> ForcedFrozenDigraph
forcedFrozenCyclicDigraphForSize size =
ForcedFrozenDigraph (frozenDigraphFromSuccessors size (cyclicSuccessors size))
denseSccHeavyBuildClosureWeight :: Int -> Int
denseSccHeavyBuildClosureWeight size =
denseClosureWeight (forcedFrozenSccHeavyDigraphForSize size)
forcedFrozenSccHeavyDigraphForSize :: Int -> ForcedFrozenDigraph
forcedFrozenSccHeavyDigraphForSize size =
ForcedFrozenDigraph (frozenDigraphFromSuccessors size (sccHeavySuccessors size))
denseCachedClosureWeight :: ForcedFrozenDigraph -> Int
denseCachedClosureWeight (ForcedFrozenDigraph relation) =
let seeds = IntSet.singleton 0
coldCache = sccClosureCacheFor relation
(coldReachable, warmedCache) =
frozenReachabilityWithCache coldCache seeds
(hotReachable, _hotCache) =
frozenReachabilityWithCache warmedCache seeds
in IntSet.size coldReachable + IntSet.size hotReachable
hotSingletonCache :: Int -> ForcedSccClosureCache
hotSingletonCache size =
let coldCache =
sccClosureCacheFor (frozenDigraphFromSuccessors size (const IntSet.empty))
seeds =
IntSet.singleton 0
(_firstReachable, onceQueriedCache) =
frozenReachabilityWithCache coldCache seeds
(secondReachable, hotCache) =
frozenReachabilityWithCache onceQueriedCache seeds
in ForcedSccClosureCache
{ forcedCacheReceipt = IntSet.size secondReachable,
forcedCache = hotCache
}
hotSingletonCacheWeight :: ForcedSccClosureCache -> Int
hotSingletonCacheWeight cache =
IntSet.size (fst (frozenReachabilityWithCache (forcedCache cache) (IntSet.singleton 0)))
graphSnapshotOverlayReachabilityWeight :: Int -> Int
graphSnapshotOverlayReachabilityWeight size =
IntSet.size (snapshotReachabilityFrom snapshot (IntSet.singleton 0))
where
snapshot =
insertSnapshotEdge
(Edge (max 0 (size - 1)) 0)
(snapshotFromFrozen (frozenDigraphFromSuccessors size (successors size)))
rnfFrozenDigraph :: FrozenDigraph -> ()
rnfFrozenDigraph graph =
rnfCsr (graphForward graph)
`seq` rnfCsr (graphBackward graph)
`seq` rnfSccPlan (graphSccPlan graph)
rnfSccPlan :: SccPlan -> ()
rnfSccPlan plan =
rnf (sccOfVertex plan)
`seq` rnfCsr (sccMembers plan)
`seq` rnfCsr (condensation plan)
`seq` rnfCsr (condensationBackward plan)
rnfCsr :: Csr shape -> ()
rnfCsr csr =
rnf (csrVertexCount csr, csrOffsets csr, csrTargets csr)
boundedFixpointWeight :: Int -> Either Int Int
boundedFixpointWeight size =
case fixpointBounded (fromIntegral size) decrementToZero size of
Left divergence -> Left (showLength (show divergence))
Right result -> Right result
containersReachabilityWeight :: Int -> Int
containersReachabilityWeight =
containersReachabilityWith successors
containersReachabilityWith :: (Int -> Int -> IntSet) -> Int -> Int
containersReachabilityWith successorsOf size =
IntSet.size (containersReachability (successorsOf size) (IntSet.singleton 0))
hackageDataGraphBuildReachabilityWeight :: Int -> Int
hackageDataGraphBuildReachabilityWeight =
hackageDataGraphBuildReachabilityWith successors
hackageDataGraphBuildReachabilityWith :: (Int -> Int -> IntSet) -> Int -> Int
hackageDataGraphBuildReachabilityWith successorsOf =
hackageDataGraphReachabilityWeight . forcedHackageDataGraphWith successorsOf
hackageDataGraphReachabilityWeight :: ForcedGraph -> Int
hackageDataGraphReachabilityWeight (ForcedGraph graph) =
length (Graph.reachable graph 0)
forcedHackageDataGraphForSize :: Int -> ForcedGraph
forcedHackageDataGraphForSize =
forcedHackageDataGraphWith successors
forcedHackageDataGraphWith :: (Int -> Int -> IntSet) -> Int -> ForcedGraph
forcedHackageDataGraphWith successorsOf size =
ForcedGraph
( Graph.buildG
(0, max 0 (size - 1))
[ (key, target)
| key <- keys size,
target <- IntSet.toAscList (successorsOf size key)
]
)
containersReachability :: (Int -> IntSet) -> IntSet -> IntSet
containersReachability nextItems =
containersReachabilityStep nextItems IntSet.empty
containersReachabilityStep :: (Int -> IntSet) -> IntSet -> IntSet -> IntSet
containersReachabilityStep nextItems visited pending =
case IntSet.minView pending of
Nothing -> visited
Just (item, remainingPending)
| IntSet.member item visited ->
containersReachabilityStep nextItems visited remainingPending
| otherwise ->
let nextPending =
IntSet.union
remainingPending
(IntSet.difference (nextItems item) visited)
in containersReachabilityStep nextItems (IntSet.insert item visited) nextPending
decrementToZero :: Int -> Int
decrementToZero value
| value <= 0 = 0
| otherwise = value - 1
successors :: Int -> Int -> IntSet
successors size key =
IntSet.fromAscList
(filter (< size) [key + 1, key + 2])
variableDegreeSuccessors :: Int -> Int -> IntSet
variableDegreeSuccessors size key
| size <= 0 = IntSet.empty
| otherwise =
IntSet.fromList
(filter (< size) [key + step | step <- [1 .. 1 + (key `mod` 6)]])
sccHeavySuccessors :: Int -> Int -> IntSet
sccHeavySuccessors size key
| size <= 0 = IntSet.empty
| otherwise =
IntSet.fromList
[ (key + 1) `mod` size,
(key + 2) `mod` size,
(key - 1) `mod` size
]
cyclicSuccessors :: Int -> Int -> IntSet
cyclicSuccessors size key
| size <= 0 = IntSet.empty
| otherwise = IntSet.fromList [(key + 1) `mod` size, (key + 2) `mod` size]
classIds :: Int -> [ClassId]
classIds size =
fmap ClassId (keys size)
holeyKeys :: Int -> [Int]
holeyKeys =
fmap (* 2) . keys
negativeKeys :: Int -> [Int]
negativeKeys =
fmap negate . keys
densePrefixWithGiantOutlier :: Int -> [Int]
densePrefixWithGiantOutlier size =
keys size <> [1_000_000_000]
mixedDenseSparseKeys :: Int -> [Int]
mixedDenseSparseKeys size =
keys size <> fmap (+ 1_000_000_000) (sampleKeys size)