packages feed

moonlight-triangulation-1.3.0.0: bench/delaunay-compare/Moonlight/Triangulation/Bench/DelaunayCompare/Domain.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NumericUnderscores #-}

-- | The closed case matrix and typed obstruction surface for the comparison.
module Moonlight.Triangulation.Bench.DelaunayCompare.Domain
  ( SizeBand (..)
  , allSizeBands
  , sizeBandLabel
  , PointDistribution (..)
  , allPointDistributions
  , pointDistributionLabel
  , Implementation (..)
  , allImplementations
  , implementationLabel
  , NativeImplementation (..)
  , nativeImplementationLabel
  , implementationOfNative
  , PointCount
  , pointCountValue
  , pointCountsFor
  , FixtureSpec (..)
  , allFixtureSpecs
  , DelaunaySummary (..)
  , NativeStatus (..)
  , NativePreparationFailure (..)
  , NativeRunFailure (..)
  , CompareObstruction (..)
  , renderCompareObstruction
  ) where

import Control.DeepSeq (NFData)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NonEmpty
import GHC.Generics (Generic)
import Moonlight.Triangulation.Types (BuildError)

data SizeBand
  = Small
  | Big
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

allSizeBands :: [SizeBand]
allSizeBands = [Small, Big]

sizeBandLabel :: SizeBand -> String
sizeBandLabel = \case
  Small -> "small"
  Big -> "big"

data PointDistribution
  = LocalInsertion
  | Uniform
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

allPointDistributions :: [PointDistribution]
allPointDistributions = [LocalInsertion, Uniform]

pointDistributionLabel :: PointDistribution -> String
pointDistributionLabel = \case
  LocalInsertion -> "local insertion"
  Uniform -> "uniform"

data Implementation
  = Spade
  | SpadeHierarchy
  | Cdt
  | Delaunator
  | Moonlight
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

-- | Upstream order, with Moonlight appended rather than interposed.
allImplementations :: [Implementation]
allImplementations = [Spade, SpadeHierarchy, Cdt, Delaunator, Moonlight]

implementationLabel :: Implementation -> String
implementationLabel = \case
  Spade -> "spade 2"
  SpadeHierarchy -> "spade 2 hierarchy"
  Cdt -> "cdt"
  Delaunator -> "delaunator"
  Moonlight -> "moonlight-triangulation"

data NativeImplementation
  = NativeSpade
  | NativeSpadeHierarchy
  | NativeCdt
  | NativeDelaunator
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

nativeImplementationLabel :: NativeImplementation -> String
nativeImplementationLabel = implementationLabel . implementationOfNative

implementationOfNative :: NativeImplementation -> Implementation
implementationOfNative = \case
  NativeSpade -> Spade
  NativeSpadeHierarchy -> SpadeHierarchy
  NativeCdt -> Cdt
  NativeDelaunator -> Delaunator

newtype PointCount = PointCount Int
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

pointCountValue :: PointCount -> Int
pointCountValue (PointCount count) = count

data FixtureSpec = FixtureSpec
  { fixtureSizeBand :: !SizeBand
  , fixturePointDistribution :: !PointDistribution
  , fixturePointCount :: !PointCount
  }
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

allFixtureSpecs :: [FixtureSpec]
allFixtureSpecs =
  concatMap
    ( \sizeBand ->
        liftA2
          (FixtureSpec sizeBand)
          allPointDistributions
          (pointCountsFor sizeBand)
    )
    allSizeBands

pointCountsFor :: SizeBand -> [PointCount]
pointCountsFor = \case
  Small -> PointCount <$> [2_000, 4_000, 6_000, 8_000, 10_000, 12_000, 14_000]
  Big -> PointCount <$> [50_000, 100_000, 150_000, 200_000, 250_000]

data DelaunaySummary = DelaunaySummary
  { summaryVertexCount :: !Int
  , summaryTriangleCount :: !Int
  }
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

data NativeStatus
  = NativeNullPointer
  | NativeUnknownTag
  | NativeCoordinateCountMismatch
  | NativeDistributionConstructionFailed
  | NativeTriangulationFailed
  | NativePanicked
  | NativeUnknownStatus !Int
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

data NativePreparationFailure
  = NativePreparationStatus !NativeStatus
  | NativePreparationReturnedNull
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

data NativeRunFailure
  = NativeRunStatus !NativeStatus
  | NativeCountExceedsHaskellInt !Integer
  deriving stock (Eq, Ord, Show, Generic)
  deriving anyclass (NFData)

data CompareObstruction
  = RustManifestNotFound !(NonEmpty FilePath)
  | CargoInvocationFailed !FilePath !String
  | CargoBuildFailed !FilePath !Int !String
  | UnsupportedDynamicLibraryHost !String
  | DynamicLibraryOpenFailed !FilePath !String
  | DynamicSymbolLoadFailed !FilePath !String !String
  | FixtureGenerationFailed !FixtureSpec !NativeStatus
  | FixturePreparationFailed !FixtureSpec !NativeImplementation !NativeStatus
  | NativePreparedNull !FixtureSpec !NativeImplementation
  | MoonlightConstructionFailed !FixtureSpec !BuildError
  | NativeConstructionFailed !FixtureSpec !NativeImplementation !NativeRunFailure
  | SummaryDisagreement !FixtureSpec !Implementation !DelaunaySummary !DelaunaySummary
  | ComparisonObstructions !(NonEmpty CompareObstruction)
  deriving stock (Eq, Show, Generic)
  deriving anyclass (NFData)

renderCompareObstruction :: CompareObstruction -> String
renderCompareObstruction = \case
  RustManifestNotFound candidates ->
    "Rust referent manifest not found; checked " <> show (NonEmpty.toList candidates)
  CargoInvocationFailed manifest details ->
    "could not invoke Cargo for " <> manifest <> ": " <> details
  CargoBuildFailed manifest exitCode details ->
    "Cargo failed for "
      <> manifest
      <> " with exit code "
      <> show exitCode
      <> if null details then "" else ":\n" <> details
  UnsupportedDynamicLibraryHost host ->
    "delaunay-compare does not know the dynamic-library suffix for host " <> show host
  DynamicLibraryOpenFailed path details ->
    "could not load Rust referent library " <> path <> ": " <> details
  DynamicSymbolLoadFailed path symbol details ->
    "could not load symbol " <> symbol <> " from " <> path <> ": " <> details
  FixtureGenerationFailed fixture status ->
    "upstream fixture generation failed for " <> renderFixture fixture <> ": " <> show status
  FixturePreparationFailed fixture implementation status ->
    "native preparation failed for "
      <> nativeImplementationLabel implementation
      <> " on "
      <> renderFixture fixture
      <> ": "
      <> show status
  NativePreparedNull fixture implementation ->
    "native preparation returned a null handle for "
      <> nativeImplementationLabel implementation
      <> " on "
      <> renderFixture fixture
  MoonlightConstructionFailed fixture buildError ->
    "Moonlight construction failed for " <> renderFixture fixture <> ": " <> show buildError
  NativeConstructionFailed fixture implementation failure ->
    "native construction failed for "
      <> nativeImplementationLabel implementation
      <> " on "
      <> renderFixture fixture
      <> ": "
      <> show failure
  SummaryDisagreement fixture implementation expected observed ->
    "construction summary disagreement for "
      <> implementationLabel implementation
      <> " on "
      <> renderFixture fixture
      <> "; Moonlight="
      <> show expected
      <> ", observed="
      <> show observed
  ComparisonObstructions obstructions ->
    unlines ("delaunay comparison obstructed:" : fmap (("  - " <>) . renderCompareObstruction) (NonEmpty.toList obstructions))

renderFixture :: FixtureSpec -> String
renderFixture fixture =
  sizeBandLabel (fixtureSizeBand fixture)
    <> "/"
    <> pointDistributionLabel (fixturePointDistribution fixture)
    <> "/"
    <> show (pointCountValue (fixturePointCount fixture))