///|
/// Results of a deterministic detector benchmark.
pub struct BenchmarkResult {
name : String
samples : Int
passes : Int
detections : Int
first_detection : Int
checksum : Double
truth_count : Int
metrics : ChangePointMetrics
}
///|
pub fn BenchmarkResult::empty(name : String) -> BenchmarkResult {
{
name,
samples: 0,
passes: 0,
detections: 0,
first_detection: -1,
checksum: 0.0,
truth_count: 0,
metrics: ChangePointMetrics::empty(),
}
}
///|
pub fn BenchmarkResult::summary(self : BenchmarkResult) -> String {
"\{self.name}: samples=\{self.samples},passes=\{self.passes},detections=\{self.detections},first=\{self.first_detection},precision=\{self.metrics.precision},recall=\{self.metrics.recall},f1=\{self.metrics.f1},checksum=\{self.checksum}"
}
///|
fn record_detection(
result : DetectionResult,
detections : Array[Int],
first : Int,
) -> Int {
if result.changed {
detections.push(result.index)
if first < 0 {
result.index
} else {
first
}
} else {
first
}
}
///|
pub fn benchmark_cusum(
scenario : SignalScenario,
rounds? : Int = 1,
) -> BenchmarkResult {
let values = generate_signal(scenario)
let detections : Array[Int] = []
let mut first = -1
let mut checksum = 0.0
let mut passes = 0
let count = if rounds < 1 { 1 } else { rounds }
for round in 0.. BenchmarkResult {
let values = generate_signal(scenario)
let detections : Array[Int] = []
let mut first = -1
let mut checksum = 0.0
let mut passes = 0
let count = if rounds < 1 { 1 } else { rounds }
for round in 0.. BenchmarkResult {
let values = generate_signal(scenario)
let detections : Array[Int] = []
let mut first = -1
let mut checksum = 0.0
let mut passes = 0
let count = if rounds < 1 { 1 } else { rounds }
for round in 0.. Array[BenchmarkResult] {
let scenario = SignalScenario::mean_and_variance(
length=512,
change_at=256,
baseline=10.0,
shift=2.0,
noise=0.15,
post_noise=0.7,
seed=20260818L,
)
[
benchmark_cusum(scenario, rounds~),
benchmark_robust_z(scenario, rounds~),
benchmark_ensemble(scenario, rounds~),
]
}
///|
pub fn benchmark_markdown(results : Array[BenchmarkResult]) -> String {
let mut output = "| detector | samples | passes | detections | first detection | precision | recall | F1 | checksum |\n|---|---:|---:|---:|---:|---:|---:|---:|---:|\n"
for result in results {
output = output +
"| \{result.name} | \{result.samples} | \{result.passes} | \{result.detections} | \{result.first_detection} | \{result.metrics.precision} | \{result.metrics.recall} | \{result.metrics.f1} | \{result.checksum} |\n"
}
output
}