///|
pub(all) enum Loss {
Squared
Absolute
Quantile(Double)
} derive(Debug)
///|
pub(all) struct Knot {
x : Double
y : Double
weight : Double
} derive(Debug)
///|
pub(all) struct Model {
knots : Array[Knot]
direction : Direction
loss : Loss
} derive(Debug)
///|
pub fn validate_model(model : Model) -> Result[Unit, String] {
if model.knots.length() == 0 {
return Err("empty model")
}
match model.loss {
Quantile(tau) =>
if !finite(tau) || tau <= 0.0 || tau >= 1.0 {
return Err("invalid model quantile")
}
_ => ()
}
for i = 0; i < model.knots.length(); i = i + 1 {
let k = model.knots[i]
if !finite(k.x) || !finite(k.y) || !finite(k.weight) || k.weight <= 0.0 {
return Err("invalid knot")
}
if i > 0 {
let previous = model.knots[i - 1]
if previous.x >= k.x {
return Err("coordinates must be strictly increasing")
}
if !before(previous.y, k.y, model.direction) {
return Err("nonmonotone model")
}
}
}
Ok(())
}
///|
/// L2 empirical calibration. Repeated x values are constrained to one fitted y.
pub fn train_l2(
samples : Array[Sample],
direction? : Direction = Increasing,
) -> Result[Model, String] {
let p = match prepare(samples) {
Err(e) => return Err(e)
Ok(v) => v
}
let fit = match fit_l2(p.means, p.weights, direction~) {
Err(e) => return Err(e)
Ok(v) => v
}
let knots : Array[Knot] = []
for i = 0; i < p.coordinates.length(); i = i + 1 {
knots.push({ x: p.coordinates[i], y: fit.values[i], weight: p.weights[i], })
}
Ok({ knots, direction, loss: Squared, })
}
///|
/// Safe model copying for consumers retaining independently mutable arrays.
pub fn copy_model(model : Model) -> Result[Model, String] {
match validate_model(model) {
Err(e) => Err(e)
Ok(_) => Ok({ ..model, knots: model.knots.copy(), })
}
}
///|
pub fn knot_arrays(
model : Model,
) -> Result[(Array[Double], Array[Double]), String] {
match validate_model(model) {
Err(e) => return Err(e)
Ok(_) => ()
}
Ok((model.knots.map(fn(k) { k.x }), model.knots.map(fn(k) { k.y })))
}
///|
#deprecated
pub extend Loss with @debug.Debug::{to_repr}
///|
#deprecated
pub extend Knot with @debug.Debug::{to_repr}
///|
#deprecated
pub extend Model with @debug.Debug::{to_repr}