///|
/// Center logarithms directly, avoiding closure underflow for wide ratios.
pub fn clr(x : Array[Double]) -> Array[Double] raise CodaError {
positive(x)
let logs = x.map(v => @math.ln(v))
let mut mean = 0.0
for v in logs {
mean += v / logs.length().to_double()
}
logs.map(v => v - mean)
}
///|
/// Stable softmax. Reject an unrepresentable positive composition.
pub fn clr_inverse(z : Array[Double]) -> Array[Double] raise CodaError {
vector_size(z.length())
real_vector(z)
let mut high = z[0]
for v in z {
if v > high {
high = v
}
}
let weights = z.map(v => @math.exp(v - high))
for v in weights {
if v == 0.0 {
raise Numerical("inverse log-ratio underflow")
}
}
closure(weights)
}
///|
/// Geometric mean may overflow/underflow only when the result is unrepresentable.
pub fn geometric_mean(x : Array[Double]) -> Double raise CodaError {
positive(x)
let mut mean = 0.0
for v in x {
mean += @math.ln(v) / x.length().to_double()
}
let g = checked(@math.exp(mean))
if g <= 0.0 {
raise Numerical("geometric mean underflow")
}
g
}