///|
pub(all) struct OrderAudit {
violations : Array[Int]
largest_drop : Double
total_drop : Double
plateaus : Int
} derive(Debug)
///|
pub fn audit_order(
values : Array[Double],
direction? : Direction = Increasing,
tolerance? : Double = 0.0,
) -> Result[OrderAudit, String] {
if !finite(tolerance) || tolerance < 0.0 {
return Err("invalid tolerance")
}
let violations = []
let mut largest_drop = 0.0
let mut total_drop = 0.0
let mut plateaus = 0
for i = 0; i < values.length(); i = i + 1 {
if !finite(values[i]) {
return Err("nonfinite value")
}
if i > 0 {
let drop = match direction {
Increasing => values[i - 1] - values[i]
Decreasing => values[i] - values[i - 1]
}
if !finite(drop) {
return Err("difference overflow")
}
if drop > tolerance {
violations.push(i)
total_drop = total_drop + drop
if drop > largest_drop {
largest_drop = drop
}
}
if drop.abs() <= tolerance {
plateaus = plateaus + 1
}
}
}
if !finite(total_drop) {
return Err("drop sum overflow")
}
Ok({ violations, largest_drop, total_drop, plateaus, })
}
///|
pub(all) struct ProjectionAudit {
residual_sum : Double
min_prefix : Double
max_prefix : Double
max_block_balance : Double
order : OrderAudit
} derive(Debug)
///|
/// L2 chain optimality diagnostics. Increasing fits have nonpositive residual prefixes.
pub fn audit_projection(
y : Array[Double],
weights : Array[Double],
fit : Fit,
) -> Result[ProjectionAudit, String] {
match validate_series(y, weights) {
Err(e) => return Err(e)
Ok(_) => ()
}
if fit.values.length() != y.length() {
return Err("fit length mismatch")
}
match expand_blocks(fit.blocks, y.length()) {
Err(e) => return Err(e)
Ok(v) => if v != fit.values { return Err("fit partition mismatch") }
}
let order = match audit_order(fit.values, direction=fit.direction) {
Err(e) => return Err(e)
Ok(v) => v
}
let mut residual_sum = 0.0
let mut min_prefix = 0.0
let mut max_prefix = 0.0
let mut max_block_balance = 0.0
for block in fit.blocks {
let mut balance = 0.0
for i = block.start; i < block.end; i = i + 1 {
let residual = weights[i] * (fit.values[i] - y[i])
balance = balance + residual
residual_sum = residual_sum + residual
if !finite(residual_sum) || !finite(balance) {
return Err("residual overflow")
}
if residual_sum < min_prefix {
min_prefix = residual_sum
}
if residual_sum > max_prefix {
max_prefix = residual_sum
}
}
if balance.abs() > max_block_balance {
max_block_balance = balance.abs()
}
}
Ok({ residual_sum, min_prefix, max_prefix, max_block_balance, order, })
}
///|
#deprecated
pub extend OrderAudit with @debug.Debug::{to_repr}
///|
#deprecated
pub extend ProjectionAudit with @debug.Debug::{to_repr}