packages feed

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

module Rows
  ( contextRowComparisonBenchmarks,
  )
where

import Control.DeepSeq
  ( NFData (..),
  )
import Data.Bifunctor
  ( first,
  )
import Data.IntSet qualified as IntSet
import Data.Vector qualified as Vector
import Fixtures
  ( Shape,
    assertFiniteFixture,
    caseLabel,
    compileLatticeEnv,
    keys,
    querySizes,
    rawRelationRows,
    shapeLabel,
    shapes,
  )
import Kernels
  ( leqSweepWeight,
  )
import Moonlight.FiniteLattice.Core
  ( ContextLattice,
    leqContext,
  )
import Test.Tasty.Bench
  ( Benchmark,
    bench,
    bgroup,
    env,
    nf,
  )

data RowOwner
  = PackedRows
  | IntSetRows
  deriving stock (Eq, Ord, Show)

data PreparedRows
  = PreparedPackedRows !Int !(ContextLattice Int)
  | PreparedIntSetRows !Int !(Vector.Vector IntSet.IntSet)

instance NFData PreparedRows where
  rnf prepared =
    case prepared of
      PreparedPackedRows size lattice ->
        rnf (size, lattice)
      PreparedIntSetRows size rows ->
        rnf (size, rows)

contextRowComparisonBenchmarks :: Benchmark
contextRowComparisonBenchmarks =
  bgroup
    "context-row-membership-baseline"
    [ bgroup
        (shapeLabel shape)
        [ bgroup
            (rowOwnerLabel rowOwner)
            (fmap (membershipBenchmark shape rowOwner) querySizes)
        | rowOwner <- rowOwners
        ]
    | shape <- shapes
    ]

membershipBenchmark :: Shape -> RowOwner -> Int -> Benchmark
membershipBenchmark shape rowOwner size =
  env (prepareRows shape rowOwner size) $ \rows ->
    bench (caseLabel "membership sweep" size) (nf membershipCount rows)

prepareRows :: Shape -> RowOwner -> Int -> IO PreparedRows
prepareRows shape rowOwner size =
  case rowOwner of
    PackedRows ->
      PreparedPackedRows size <$> compileLatticeEnv shape size
    IntSetRows -> do
      lattice <- compileLatticeEnv shape size
      let !rows = rawRelationRows shape size
      assertFiniteFixture "IntSet membership rows" (assertMembershipRowsAgree size lattice rows)
      pure (PreparedIntSetRows size rows)

assertMembershipRowsAgree :: Int -> ContextLattice Int -> Vector.Vector IntSet.IntSet -> Either String ()
assertMembershipRowsAgree size lattice rows
  | Vector.length rows /= size =
      Left ("membership row count " <> show (Vector.length rows) <> " /= " <> show size)
  | otherwise =
      fmap (const ()) (traverse checkMembership memberships)
  where
    memberships =
      [ (leftValue, rightValue, IntSet.member rightValue row)
      | (leftValue, row) <- zip (keys size) (Vector.toList rows),
        rightValue <- keys size
      ]

    checkMembership (leftValue, rightValue, baseline) = do
      actual <- first show (leqContext lattice leftValue rightValue)
      if actual == baseline
        then Right ()
        else
          Left
            ( "membership row mismatch for "
                <> show (leftValue, rightValue)
                <> ": baseline "
                <> show baseline
                <> ", lattice "
                <> show actual
            )

membershipCount :: PreparedRows -> Int
membershipCount prepared =
  case prepared of
    PreparedPackedRows size lattice ->
      leqSweepWeight size lattice
    PreparedIntSetRows size rows ->
      length
        [ ()
        | row <- Vector.toList rows,
          rightKey <- keys size,
          IntSet.member rightKey row
        ]

rowOwners :: [RowOwner]
rowOwners = [PackedRows, IntSetRows]

rowOwnerLabel :: RowOwner -> String
rowOwnerLabel rowOwner =
  case rowOwner of
    PackedRows -> "moonlight: packed context-key rows"
    IntSetRows -> "world: containers IntSet rows"