///|
pub(all) enum Interpolation {
Linear
Previous
Next
} derive(Debug)
///|
pub(all) enum Boundary {
Clamp
Reject
Extend
} derive(Debug)
///|
fn interpolate(
a : Knot,
b : Knot,
x : Double,
interpolation : Interpolation,
) -> Result[Double, String] {
if x == a.x {
return Ok(a.y)
}
if x == b.x {
return Ok(b.y)
}
match interpolation {
Previous => Ok(a.y)
Next => Ok(b.y)
Linear => {
let width = b.x - a.x
let offset = x - a.x
if !finite(width) || !finite(offset) || width <= 0.0 {
return Err("coordinate span overflow")
}
let ratio = offset / width
let value = a.y * (1.0 - ratio) + b.y * ratio
if !finite(value) {
Err("interpolation overflow")
} else {
Ok(value)
}
}
}
}
///|
fn predict_valid(
model : Model,
x : Double,
interpolation : Interpolation,
boundary : Boundary,
) -> Result[Double, String] {
if !finite(x) {
return Err("nonfinite query")
}
let n = model.knots.length()
let first = model.knots[0]
let last = model.knots[n - 1]
if x < first.x || x > last.x {
match boundary {
Reject => return Err("query outside training domain")
Clamp => return Ok(if x < first.x { first.y } else { last.y })
Extend => {
if n == 1 {
return Ok(first.y)
}
return if x < first.x {
interpolate(first, model.knots[1], x, Linear)
} else {
interpolate(model.knots[n - 2], last, x, Linear)
}
}
}
}
if n == 1 || x == last.x {
return Ok(last.y)
}
let mut lo = 0
let mut hi = n - 1
while hi - lo > 1 {
let mid = lo + (hi - lo) / 2
if model.knots[mid].x <= x {
lo = mid
} else {
hi = mid
}
}
interpolate(model.knots[lo], model.knots[hi], x, interpolation)
}
///|
pub fn predict(
model : Model,
x : Double,
interpolation? : Interpolation = Linear,
boundary? : Boundary = Clamp,
) -> Result[Double, String] {
match validate_model(model) {
Err(e) => return Err(e)
Ok(_) => ()
}
predict_valid(model, x, interpolation, boundary)
}
///|
/// Validate once per batch, then locate each query by binary search.
pub fn predict_many(
model : Model,
queries : Array[Double],
interpolation? : Interpolation = Linear,
boundary? : Boundary = Clamp,
) -> Result[Array[Double], String] {
match validate_model(model) {
Err(e) => return Err(e)
Ok(_) => ()
}
let values = []
for x in queries {
match predict_valid(model, x, interpolation, boundary) {
Err(e) => return Err(e)
Ok(value) => values.push(value)
}
}
Ok(values)
}
///|
pub fn domain(model : Model) -> Result[(Double, Double), String] {
match validate_model(model) {
Err(e) => return Err(e)
Ok(_) => ()
}
Ok((model.knots[0].x, model.knots[model.knots.length() - 1].x))
}
///|
#deprecated
pub extend Interpolation with @debug.Debug::{to_repr}
///|
#deprecated
pub extend Boundary with @debug.Debug::{to_repr}