packages feed

moonlight-delta-0.1.0.0: bench/repair/RepairBench.hs

module RepairBench
  ( repairBenchmarks,
  )
where

import Data.List.NonEmpty
  ( NonEmpty (..),
  )
import BenchSupport
  ( caseLabel,
    naturalWeight,
    repairSizes,
  )
import Moonlight.Delta.Repair
  ( Config (..),
    Kernel (..),
    Result (..),
    Round,
    Step (..),
    Trace (..),
    boundedRepair,
    boundedRepairTraced,
    applied,
    irreducible,
    obstructions,
  )
import Test.Tasty.Bench
  ( Benchmark,
    bench,
    bgroup,
    nf,
  )

data RepairObstruction = BelowTarget !Int
  deriving stock (Eq, Show)

data Correction = AddChunk !Int
  deriving stock (Eq, Show)

repairBenchmarks :: Benchmark
repairBenchmarks =
  bgroup
    "repair"
    (repairSizes >>= repairBenchmarksForSize)

repairBenchmarksForSize :: Int -> [Benchmark]
repairBenchmarksForSize target =
  [ bench (caseLabel "boundedRepair" target) (nf repairResultWeight target),
    bench (caseLabel "boundedRepairTraced" target) (nf repairTraceWeight target)
  ]

repairResultWeight :: Int -> Int
repairResultWeight target =
  repairResultScore (boundedRepair (repairKernel target) (repairConfig target) 0)

repairTraceWeight :: Int -> Int
repairTraceWeight target =
  let (result, traceValue) = boundedRepairTraced (repairKernel target) (repairConfig target) 0
   in repairResultScore result + repairTraceScore traceValue

repairResultScore :: Result Int RepairObstruction -> Int
repairResultScore result =
  case result of
    ResultConverged stateValue rounds -> stateValue + naturalWeight rounds
    ResultStuck stateValue obstructionValues rounds -> stateValue + obstructionWeight obstructionValues + naturalWeight rounds
    ResultBudgetExhausted stateValue obstructionValues rounds -> stateValue + obstructionWeight obstructionValues + naturalWeight rounds

repairTraceScore :: Trace RepairObstruction Correction -> Int
repairTraceScore (Trace rounds) =
  sum (fmap repairRoundScore rounds)

repairRoundScore :: Round RepairObstruction Correction -> Int
repairRoundScore roundValue =
  obstructionWeight (obstructions roundValue)
    + naturalWeight (applied roundValue)
    + length (irreducible roundValue)

repairKernel :: Int -> Kernel Int RepairObstruction Correction
repairKernel target =
  Kernel
    { check = inspectRepairState target,
      residuate = proposeRepairCorrection target,
      applyKernelCorrection = applyRepairCorrection target
    }

inspectRepairState :: Int -> Int -> Step Int RepairObstruction
inspectRepairState target stateValue
  | stateValue >= target = StepConverged stateValue
  | otherwise = StepObstructed stateValue (BelowTarget target :| [])

proposeRepairCorrection :: Int -> RepairObstruction -> Maybe Correction
proposeRepairCorrection target obstruction =
  case obstruction of
    BelowTarget _ -> Just (AddChunk (repairChunk target))

applyRepairCorrection :: Int -> Int -> Correction -> Int
applyRepairCorrection target stateValue correction =
  case correction of
    AddChunk chunk -> min target (stateValue + chunk)

repairChunk :: Int -> Int
repairChunk target =
  max 1 (target `div` 16)

repairConfig :: Int -> Config
repairConfig target =
  Config
    { maxRounds = fromIntegral target
    }

obstructionWeight :: NonEmpty RepairObstruction -> Int
obstructionWeight =
  sum . fmap (\(BelowTarget target) -> target)