packages feed

moonlight-algebra-0.1.0.0: bench/finite-lattice/Atomic.hs

module Atomic
  ( atomicOperationComparisonBenchmarks,
  )
where

import Algebra.Lattice
  ( joinLeq,
    (/\),
    (\/),
  )
import Control.DeepSeq
  ( NFData (..),
  )
import Data.Bifunctor
  ( first,
  )
import Data.IntSet qualified as IntSet
import Fixtures
  ( Shape (BooleanCube),
    assertFiniteFixture,
    booleanCubeBits,
    caseLabel,
    compileLatticeEnv,
    hackageBooleanCubeElement,
    hackageBooleanCubeElements,
    keys,
    querySizes,
  )
import Kernels
  ( leqSweepWeight,
    residentJoinMeetKeySweepWeight,
  )
import Moonlight.FiniteLattice.Core
  ( ContextLattice,
    joinContext,
    leqContext,
    meetContext,
  )
import Moonlight.FiniteLattice.Resident
  ( residentContextKeys,
    withResidentContext,
  )
import Test.Tasty.Bench
  ( Benchmark,
    bench,
    bgroup,
    env,
    nf,
  )

data PreparedHackageAtomicComparison = PreparedHackageAtomicComparison !Int !(ContextLattice Int) ![IntSet.IntSet]

instance NFData PreparedHackageAtomicComparison where
  rnf (PreparedHackageAtomicComparison size lattice elements) =
    rnf (size, lattice, elements)

atomicOperationComparisonBenchmarks :: Benchmark
atomicOperationComparisonBenchmarks =
  bgroup
    "atomic-operation-baseline"
    [ bgroup
        "boolean-cube"
        (fmap hackageAtomicComparisonBenchmark querySizes)
    ]

hackageAtomicComparisonBenchmark :: Int -> Benchmark
hackageAtomicComparisonBenchmark size =
  env (prepareHackageAtomicComparison size) $ \prepared ->
    bgroup
      (caseLabel "hackage-compatible query sweep" size)
      [ bench "moonlight: resident key <= sweep" (nf moonlightAtomicLeqWeight prepared),
        bench "hackage lattices: IntSet joinLeq sweep" (nf hackageAtomicLeqWeight prepared),
        bench "moonlight: public join sweep" (nf moonlightAtomicJoinWeight prepared),
        bench "hackage lattices: IntSet \\/ sweep" (nf hackageAtomicJoinWeight prepared),
        bench "moonlight: public meet sweep" (nf moonlightAtomicMeetWeight prepared),
        bench "hackage lattices: IntSet /\\ sweep" (nf hackageAtomicMeetWeight prepared),
        bench "moonlight: resident key join/meet sweep" (nf moonlightAtomicJoinMeetWeight prepared),
        bench "hackage lattices: IntSet join/meet sweep" (nf hackageAtomicJoinMeetWeight prepared)
      ]

prepareHackageAtomicComparison :: Int -> IO PreparedHackageAtomicComparison
prepareHackageAtomicComparison size = do
  lattice <- compileLatticeEnv BooleanCube size
  let !elements = hackageBooleanCubeElements size
  assertFiniteFixture "hackage BooleanCube atomic baseline" (assertHackageAtomicAgrees size lattice elements)
  pure (PreparedHackageAtomicComparison size lattice elements)

assertHackageAtomicAgrees :: Int -> ContextLattice Int -> [IntSet.IntSet] -> Either String ()
assertHackageAtomicAgrees size lattice elements
  | length elements /= size =
      Left ("hackage element count " <> show (length elements) <> " /= " <> show size)
  | otherwise =
      fmap (const ()) (traverse checkPair pairs)
  where
    bits =
      booleanCubeBits size

    keyedElements =
      zip (keys size) elements

    pairs =
      [ (leftValue, leftElement, rightValue, rightElement)
      | (leftValue, leftElement) <- keyedElements,
        (rightValue, rightElement) <- keyedElements
      ]

    checkPair (leftValue, leftElement, rightValue, rightElement) = do
      actualLeq <- first show (leqContext lattice leftValue rightValue)
      joined <- first show (joinContext lattice leftValue rightValue)
      met <- first show (meetContext lattice leftValue rightValue)
      let !hackageLeq = joinLeq leftElement rightElement
          !hackageJoin = leftElement \/ rightElement
          !hackageMeet = leftElement /\ rightElement
          !moonlightJoin = hackageBooleanCubeElement bits joined
          !moonlightMeet = hackageBooleanCubeElement bits met
      if actualLeq == hackageLeq && moonlightJoin == hackageJoin && moonlightMeet == hackageMeet
        then Right ()
        else
          Left ("hackage BooleanCube mismatch for " <> show (leftValue, rightValue))

moonlightAtomicLeqWeight :: PreparedHackageAtomicComparison -> Int
moonlightAtomicLeqWeight (PreparedHackageAtomicComparison size lattice _) =
  leqSweepWeight size lattice

moonlightAtomicJoinWeight :: PreparedHackageAtomicComparison -> Either String Int
moonlightAtomicJoinWeight (PreparedHackageAtomicComparison size lattice _) =
  fmap sum
    ( traverse
        joinPairWeight
        [ (leftValue, rightValue)
        | leftValue <- keys size,
          rightValue <- keys size
        ]
    )
  where
    joinPairWeight (leftValue, rightValue) =
      first show (joinContext lattice leftValue rightValue)

moonlightAtomicMeetWeight :: PreparedHackageAtomicComparison -> Either String Int
moonlightAtomicMeetWeight (PreparedHackageAtomicComparison size lattice _) =
  fmap sum
    ( traverse
        meetPairWeight
        [ (leftValue, rightValue)
        | leftValue <- keys size,
          rightValue <- keys size
        ]
    )
  where
    meetPairWeight (leftValue, rightValue) =
      first show (meetContext lattice leftValue rightValue)

moonlightAtomicJoinMeetWeight :: PreparedHackageAtomicComparison -> Either String Int
moonlightAtomicJoinMeetWeight (PreparedHackageAtomicComparison _size lattice _) =
  withResidentContext lattice $ \contextValue ->
    residentJoinMeetKeySweepWeight contextValue (residentContextKeys contextValue)

hackageAtomicLeqWeight :: PreparedHackageAtomicComparison -> Int
hackageAtomicLeqWeight (PreparedHackageAtomicComparison _size _lattice elements) =
  length
    [ ()
    | leftElement <- elements,
      rightElement <- elements,
      joinLeq leftElement rightElement
    ]

hackageAtomicJoinWeight :: PreparedHackageAtomicComparison -> Int
hackageAtomicJoinWeight (PreparedHackageAtomicComparison _size _lattice elements) =
  sum
    [ IntSet.size (leftElement \/ rightElement)
    | leftElement <- elements,
      rightElement <- elements
    ]

hackageAtomicMeetWeight :: PreparedHackageAtomicComparison -> Int
hackageAtomicMeetWeight (PreparedHackageAtomicComparison _size _lattice elements) =
  sum
    [ IntSet.size (leftElement /\ rightElement)
    | leftElement <- elements,
      rightElement <- elements
    ]

hackageAtomicJoinMeetWeight :: PreparedHackageAtomicComparison -> Int
hackageAtomicJoinMeetWeight (PreparedHackageAtomicComparison _size _lattice elements) =
  sum
    [ IntSet.size (leftElement \/ rightElement)
        + IntSet.size (leftElement /\ rightElement)
    | leftElement <- elements,
      rightElement <- elements
    ]