packages feed

moonlight-algebra-0.1.0.0: src-finite-lattice/Moonlight/FiniteLattice/Support.hs

-- | Generator supports over a finite 'ContextLattice': semantic value-boundary
-- supports plus resident antichain kernels over branded lattice keys.
module Moonlight.FiniteLattice.Support
  ( SupportBasis,
    supportBasis,
    supportBasisWithOrder,
    emptySupport,
    supportGenerators,
    principalSupport,
    supportContains,
    supportReachableContexts,
    supportReachableLatticeContexts,
    normalizeSupport,
    supportUnion,
    supportMeet,
    ResidentSupport,
    residentSupportFromElements,
    residentSupportFromKeys,
    residentSupportKeys,
    residentSupportWithClosure,
    residentSupportContainsKey,
    residentSupportContainsElement,
    residentSupportReachableElements,
    residentSupportUnion,
    residentSupportMeet,
  )
where

import Data.Kind (Type)
import Data.Maybe (catMaybes)
import Data.Set (Set)
import Data.Set qualified as Set
import Moonlight.FiniteLattice.Internal.Key
  ( ContextKey (..),
    ContextKeySet,
    contextKeySetChunkCount,
    contextKeySetFilter,
    contextKeySetFromKeys,
    contextKeySetImage2,
    contextKeySetIntersects,
    contextKeySetIntersectsExcept,
    contextKeySetUnion,
    contextKeySetUnionImages,
  )
import Moonlight.FiniteLattice.Internal.Plan
  ( contextPlanJoinKey,
    contextPlanLowerKeys,
    contextPlanUpperKeys,
  )
import Moonlight.FiniteLattice.Internal.Types
  ( ContextLattice (..),
    ResidentContext (..),
    ResidentContextKeySet (..),
    contextKeyFromResidentKey,
  )
import Moonlight.FiniteLattice.Core
  ( ContextLatticeLookupError (..),
  )
import Moonlight.FiniteLattice.Resident
  ( ResidentContextElement,
    ResidentContextKey,
    checkResidentContext,
    residentContextElementForKey,
    residentContextElementKey,
    residentContextElementValue,
    residentContextElements,
    residentContextKeyOrdinal,
    residentContextKeySetMember,
    residentContextKeySetToAscList,
    withResidentContext,
  )

type SupportBasis :: Type -> Type
newtype SupportBasis c = SupportBasis
  { unSupportBasis :: Set c
  }
  deriving stock (Eq, Ord, Show)

type ResidentSupport :: Type -> Type -> Type
data ResidentSupport s c = ResidentSupport
  { rsMinimalKeys :: !(ResidentContextKeySet s),
    rsClosure :: !(Maybe (ResidentContextKeySet s))
  }
  deriving stock (Eq, Show)

supportBasis :: Ord c => ContextLattice c -> [c] -> Either (ContextLatticeLookupError c) (SupportBasis c)
supportBasis contextLatticeValue =
  normalizeSupport contextLatticeValue . SupportBasis . Set.fromList

supportBasisWithOrder :: Ord c => (c -> c -> Bool) -> [c] -> SupportBasis c
supportBasisWithOrder leqValue contexts =
  SupportBasis (Set.filter isMinimal candidates)
  where
    candidates =
      Set.fromList contexts
    isMinimal candidateValue =
      not
        ( any
            (\otherValue -> otherValue /= candidateValue && leqValue otherValue candidateValue)
            (Set.toAscList candidates)
        )

emptySupport :: SupportBasis c
emptySupport =
  SupportBasis Set.empty

supportGenerators :: SupportBasis c -> [c]
supportGenerators =
  Set.toAscList . unSupportBasis

principalSupport :: c -> SupportBasis c
principalSupport =
  SupportBasis . Set.singleton

supportContains :: Ord c => ContextLattice c -> SupportBasis c -> c -> Either (ContextLatticeLookupError c) Bool
supportContains contextLatticeValue supportValue contextValue =
  withResidentSupport contextLatticeValue supportValue $ \residentContext support ->
    residentSupportContainsElement residentContext support
      <$> checkResidentContext residentContext contextValue

supportReachableContexts :: Ord c => ContextLattice c -> [c] -> SupportBasis c -> Either (ContextLatticeLookupError c) [c]
supportReachableContexts contextLatticeValue candidateContexts supportValue =
  withResidentSupport contextLatticeValue supportValue $ \residentContext support ->
    let cachedSupport = residentSupportWithClosure residentContext support
     in catMaybes
          <$>
          traverse
            (reachableCandidate residentContext cachedSupport)
            candidateContexts

supportReachableLatticeContexts :: Ord c => ContextLattice c -> SupportBasis c -> Either (ContextLatticeLookupError c) [c]
supportReachableLatticeContexts contextLatticeValue supportValue =
  withResidentSupport contextLatticeValue supportValue $ \residentContext support ->
    pure
      ( residentContextElementValue
          <$> residentSupportReachableElements residentContext support
      )

normalizeSupport :: Ord c => ContextLattice c -> SupportBasis c -> Either (ContextLatticeLookupError c) (SupportBasis c)
normalizeSupport contextLatticeValue supportValue =
  withResidentSupport contextLatticeValue supportValue $ \residentContext support ->
    pure (supportFromResidentSupport residentContext support)

supportUnion :: Ord c => ContextLattice c -> SupportBasis c -> SupportBasis c -> Either (ContextLatticeLookupError c) (SupportBasis c)
supportUnion contextLatticeValue leftSupport rightSupport =
  withResidentSupports contextLatticeValue leftSupport rightSupport $ \residentContext residentLeft residentRight ->
    pure
      ( supportFromResidentSupport
          residentContext
          (residentSupportUnion residentContext residentLeft residentRight)
      )

supportMeet :: Ord c => ContextLattice c -> SupportBasis c -> SupportBasis c -> Either (ContextLatticeLookupError c) (SupportBasis c)
supportMeet contextLatticeValue leftSupport rightSupport =
  withResidentSupports contextLatticeValue leftSupport rightSupport $ \residentContext residentLeft residentRight -> do
    pure
      ( supportFromResidentSupport
          residentContext
          (residentSupportMeet residentContext residentLeft residentRight)
      )

residentSupportFromElements :: Ord c => ResidentContext s c -> [c] -> Either (ContextLatticeLookupError c) (ResidentSupport s c)
residentSupportFromElements contextValue contextValues =
  residentSupportFromKeys contextValue
    . fmap residentContextElementKey
    <$> traverse (checkResidentContext contextValue) contextValues

residentSupportFromKeys :: ResidentContext s c -> [ResidentContextKey s] -> ResidentSupport s c
residentSupportFromKeys (ResidentContext lattice) keys =
  ResidentSupport
    { rsMinimalKeys =
        ResidentContextKeySet
          ( minimalResidentKeySet
              lattice
              ( contextKeySetFromKeys
                  (contextKeySetChunkCount (clSize lattice))
                  (fmap residentContextKeyOrdinal keys)
              )
          ),
      rsClosure = Nothing
    }

residentSupportKeys :: ResidentContext s c -> ResidentSupport s c -> [ResidentContextKey s]
residentSupportKeys _ supportValue =
  residentContextKeySetToAscList (rsMinimalKeys supportValue)

residentSupportWithClosure :: ResidentContext s c -> ResidentSupport s c -> ResidentSupport s c
residentSupportWithClosure contextValue supportValue =
  case rsClosure supportValue of
    Just _ ->
      supportValue
    Nothing ->
      supportValue
        { rsClosure = Just (residentSupportClosure contextValue supportValue)
        }

residentSupportContainsKey :: ResidentContext s c -> ResidentSupport s c -> ResidentContextKey s -> Bool
residentSupportContainsKey (ResidentContext lattice) supportValue candidateKey =
  case rsClosure supportValue of
    Just closure ->
      residentContextKeySetMember candidateKey closure
    Nothing ->
      case rsMinimalKeys supportValue of
        ResidentContextKeySet minimalKeys ->
          contextKeySetIntersects
            minimalKeys
            ( contextPlanLowerKeys
                (clPlan lattice)
                (contextKeyFromResidentKey candidateKey)
            )

residentSupportContainsElement :: ResidentContext s c -> ResidentSupport s c -> ResidentContextElement s c -> Bool
residentSupportContainsElement contextValue supportValue =
  residentSupportContainsKey contextValue supportValue . residentContextElementKey

residentSupportReachableElements :: ResidentContext s c -> ResidentSupport s c -> [ResidentContextElement s c]
residentSupportReachableElements contextValue supportValue =
  let closure =
        case rsClosure supportValue of
          Just cachedClosure -> cachedClosure
          Nothing -> residentSupportClosure contextValue supportValue
   in [ contextElement
      | contextElement <- residentContextElements contextValue,
        residentContextKeySetMember (residentContextElementKey contextElement) closure
      ]

residentSupportUnion :: ResidentContext s c -> ResidentSupport s c -> ResidentSupport s c -> ResidentSupport s c
residentSupportUnion (ResidentContext lattice) leftSupport rightSupport =
  case (rsMinimalKeys leftSupport, rsMinimalKeys rightSupport) of
    (ResidentContextKeySet leftKeys, ResidentContextKeySet rightKeys) ->
      ResidentSupport
        { rsMinimalKeys =
            ResidentContextKeySet
              (minimalResidentKeySet lattice (contextKeySetUnion leftKeys rightKeys)),
          rsClosure = Nothing
        }

residentSupportMeet :: ResidentContext s c -> ResidentSupport s c -> ResidentSupport s c -> ResidentSupport s c
residentSupportMeet (ResidentContext lattice) leftSupport rightSupport =
  case (rsMinimalKeys leftSupport, rsMinimalKeys rightSupport) of
    (ResidentContextKeySet leftKeys, ResidentContextKeySet rightKeys) ->
      let chunkCount =
            contextKeySetChunkCount (clSize lattice)
          pairwiseJoins =
            contextKeySetImage2
              chunkCount
              ( \leftOrdinal rightOrdinal ->
                  contextKeyOrdinal
                    ( contextPlanJoinKey
                        (clPlan lattice)
                        (ContextKey leftOrdinal)
                        (ContextKey rightOrdinal)
                    )
              )
              leftKeys
              rightKeys
       in ResidentSupport
          { rsMinimalKeys =
              ResidentContextKeySet
                (minimalResidentKeySet lattice pairwiseJoins),
            rsClosure = Nothing
          }

withResidentSupport ::
  Ord c =>
  ContextLattice c ->
  SupportBasis c ->
  (forall s. ResidentContext s c -> ResidentSupport s c -> Either (ContextLatticeLookupError c) result) ->
  Either (ContextLatticeLookupError c) result
withResidentSupport contextLatticeValue supportValue continuation =
  withResidentContext contextLatticeValue $ \residentContext -> do
    residentSupport <-
      residentSupportFromElements
        residentContext
        (supportGenerators supportValue)
    continuation residentContext residentSupport

withResidentSupports ::
  Ord c =>
  ContextLattice c ->
  SupportBasis c ->
  SupportBasis c ->
  (forall s. ResidentContext s c -> ResidentSupport s c -> ResidentSupport s c -> Either (ContextLatticeLookupError c) result) ->
  Either (ContextLatticeLookupError c) result
withResidentSupports contextLatticeValue leftSupport rightSupport continuation =
  withResidentContext contextLatticeValue $ \residentContext -> do
    residentLeft <-
      residentSupportFromElements
        residentContext
        (supportGenerators leftSupport)
    residentRight <-
      residentSupportFromElements
        residentContext
        (supportGenerators rightSupport)
    continuation residentContext residentLeft residentRight

supportFromResidentSupport ::
  Ord c =>
  ResidentContext s c ->
  ResidentSupport s c ->
  SupportBasis c
supportFromResidentSupport residentContext supportValue =
  SupportBasis
    ( Set.fromList
        ( residentContextElementValue
            . residentContextElementForKey residentContext
            <$> residentSupportKeys residentContext supportValue
        )
    )

reachableCandidate ::
  Ord c =>
  ResidentContext s c ->
  ResidentSupport s c ->
  c ->
  Either (ContextLatticeLookupError c) (Maybe c)
reachableCandidate residentContext supportValue contextValue = do
  candidateElement <- checkResidentContext residentContext contextValue
  pure
    ( if residentSupportContainsElement residentContext supportValue candidateElement
        then Just contextValue
        else Nothing
    )

minimalResidentKeySet :: ContextLattice c -> ContextKeySet -> ContextKeySet
minimalResidentKeySet lattice candidates =
  contextKeySetFilter isMinimal candidates
  where
    isMinimal candidateOrdinal =
      not
        ( contextKeySetIntersectsExcept
            candidateOrdinal
            candidates
            (contextPlanLowerKeys (clPlan lattice) (ContextKey candidateOrdinal))
        )

residentSupportClosure :: ResidentContext s c -> ResidentSupport s c -> ResidentContextKeySet s
residentSupportClosure (ResidentContext lattice) supportValue =
  case rsMinimalKeys supportValue of
    ResidentContextKeySet minimalKeys ->
      ResidentContextKeySet
        ( contextKeySetUnionImages
            (contextKeySetChunkCount (clSize lattice))
            ( \generatorOrdinal ->
                contextPlanUpperKeys
                  (clPlan lattice)
                  (ContextKey generatorOrdinal)
            )
            minimalKeys
        )