mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-11-13 17:58:18 +00:00
test(detection-core): verify bounded validation cost
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
"""Deterministic work and isolated-memory scaling checks."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from fractions import Fraction
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from detection_core_performance import PerformancePoint
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _SlopeInterval:
|
||||
lower: Fraction
|
||||
upper: Fraction
|
||||
|
||||
|
||||
def _work_units(point: PerformancePoint) -> int:
|
||||
return point.relation_count + point.observation_count + point.receipt_count + point.case_count
|
||||
|
||||
|
||||
def _positive_integer(value: object, name: str) -> int:
|
||||
if type(value) is not int or value <= 0:
|
||||
raise AssertionError(f"incremental validation {name} requires exact positive integer values")
|
||||
return value
|
||||
|
||||
|
||||
def _integer_median(values: tuple[int, ...]) -> int:
|
||||
ordered = tuple(sorted(values))
|
||||
midpoint = len(ordered) // 2
|
||||
if len(ordered) % 2:
|
||||
return ordered[midpoint]
|
||||
return (ordered[midpoint - 1] + ordered[midpoint]) // 2
|
||||
|
||||
|
||||
def _series(
|
||||
points: tuple[PerformancePoint, ...],
|
||||
sample_field: str,
|
||||
summary_field: str,
|
||||
name: str,
|
||||
) -> tuple[tuple[int, ...], tuple[int, ...]]:
|
||||
samples = tuple(tuple(getattr(point, sample_field)) for point in points)
|
||||
if len({len(values) for values in samples}) != 1:
|
||||
raise AssertionError("incremental validation measurements require equal repeat counts")
|
||||
if any(len(values) < 2 for values in samples):
|
||||
raise AssertionError("incremental validation measurements require repeated positive integer samples")
|
||||
for values in samples:
|
||||
for value in values:
|
||||
_positive_integer(value, name)
|
||||
medians = tuple(_integer_median(values) for values in samples)
|
||||
summaries = tuple(_positive_integer(getattr(point, summary_field), name) for point in points)
|
||||
if medians != summaries:
|
||||
raise AssertionError(f"incremental validation {name} summary does not match repeated median")
|
||||
spreads = tuple(max(values) - min(values) for values in samples)
|
||||
return medians, spreads
|
||||
|
||||
|
||||
def _slope_intervals(
|
||||
units: tuple[int, ...],
|
||||
medians: tuple[int, ...],
|
||||
spreads: tuple[int, ...],
|
||||
) -> tuple[_SlopeInterval, ...]:
|
||||
result = []
|
||||
pairs = zip(units[:-1], units[1:], medians[:-1], medians[1:], spreads[:-1], spreads[1:], strict=True)
|
||||
for old_units, new_units, old_value, new_value, old_spread, new_spread in pairs:
|
||||
width = new_units - old_units
|
||||
increment = new_value - old_value
|
||||
uncertainty = Fraction(old_spread + new_spread, 2)
|
||||
lower = (increment - uncertainty) / width
|
||||
upper = (increment + uncertainty) / width
|
||||
result.append(_SlopeInterval(lower, upper))
|
||||
return tuple(result)
|
||||
|
||||
|
||||
def _assert_memory(points: tuple[PerformancePoint, ...], units: tuple[int, ...]) -> None:
|
||||
medians, spreads = _series(points, "peak_samples_bytes", "peak_bytes", "memory")
|
||||
for median, spread in zip(medians, spreads, strict=True):
|
||||
if spread > max(1_024, median // 10):
|
||||
raise AssertionError("incremental validation memory is obscured by point-local variability")
|
||||
increments = tuple(new - old for old, new in zip(medians[:-1], medians[1:], strict=True))
|
||||
if any(increment <= 0 for increment in increments):
|
||||
raise AssertionError("incremental validation memory requires monotonic measured peaks")
|
||||
slopes = _slope_intervals(units, medians, spreads)
|
||||
for previous, current in zip(slopes[:-1], slopes[1:], strict=True):
|
||||
if current.lower > previous.upper * Fraction(51, 50):
|
||||
raise AssertionError(
|
||||
f"incremental validation memory slope increased beyond point-local uncertainty: {current.lower}>{previous.upper}"
|
||||
)
|
||||
|
||||
|
||||
def _assert_work(points: tuple[PerformancePoint, ...]) -> None:
|
||||
pairs = (
|
||||
("canonical_relations", "relation_count"),
|
||||
("validated_relations", "relation_count"),
|
||||
("canonical_observations", "observation_count"),
|
||||
("validated_observations", "observation_count"),
|
||||
("readiness_receipts", "receipt_count"),
|
||||
("admission_cases", "case_count"),
|
||||
)
|
||||
for point in points:
|
||||
expected_total = 0
|
||||
for work_field, point_field in pairs:
|
||||
measured = _positive_integer(getattr(point.work, work_field), "work")
|
||||
expected = _positive_integer(getattr(point, point_field), "work")
|
||||
if measured != expected:
|
||||
raise AssertionError(f"incremental validation work missed {work_field}: {measured}!={expected}")
|
||||
expected_total += expected
|
||||
total = _positive_integer(point.work.total_operations, "work")
|
||||
if total != expected_total:
|
||||
raise AssertionError(f"incremental validation work total disagrees: {total}!={expected_total}")
|
||||
|
||||
|
||||
def assert_incremental_envelope(points: tuple[PerformancePoint, ...]) -> None:
|
||||
units = tuple(_work_units(point) for point in points)
|
||||
dimensions = tuple(
|
||||
tuple(getattr(point, field) for point in points) for field in ("relation_count", "observation_count", "receipt_count", "case_count")
|
||||
)
|
||||
increasing = all(all(new > old for old, new in zip(values[:-1], values[1:], strict=True)) for values in dimensions)
|
||||
if len(units) < 3 or not increasing:
|
||||
raise AssertionError("incremental validation requires increasing workload points")
|
||||
_series(points, "elapsed_samples_ns", "elapsed_ns", "elapsed")
|
||||
_assert_work(points)
|
||||
_assert_memory(points, units)
|
||||
|
||||
|
||||
__all__ = ["assert_incremental_envelope"]
|
||||
Reference in New Issue
Block a user