///|
pub(all) enum Normalization {
MinMax
Vector
} derive(@debug.Debug, Eq)
///|
pub fn normalize_weights(
weights : Array[Double],
) -> Array[Double] raise ChoiceError {
if weights.length() == 0 || weights.length() > 32 {
raise Budget("weight count must be 1..32")
}
for weight in weights {
input_number(weight)
if weight < 0.0 {
raise Invalid("weights must be nonnegative")
}
}
let scale = maximum(weights)
if scale == 0.0 {
raise Invalid("at least one weight must be positive")
}
let scaled = weights.map(w => w / scale)
let total = sum(scaled)
scaled.map(w => w / total)
}
///|
fn matrix_weights(
m : DecisionMatrix,
weights : Array[Double],
) -> Array[Double] raise ChoiceError {
if weights.length() != m.criteria.length() {
raise Invalid("weight count differs from criterion count")
}
normalize_weights(weights)
}
///|
/// Benefit-oriented values. Vector-normalized cost criteria are negated.
pub fn DecisionMatrix::normalize(
self : DecisionMatrix,
mode : Normalization,
) -> Array[Array[Double]] raise ChoiceError {
let (n, p) = self.shape()
let out = zeros(n, p)
for j in 0.. {
let low = minimum(col)
let high = maximum(col)
let span = high - low
for i in 0.. {
let scale = maximum(col.map(x => x.abs()))
if scale > 0.0 {
let square_sum = sum(
col.map(x => {
let y = x / scale
y * y
}),
)
let norm = square_sum.sqrt()
let sign = if self.directions[j] == Benefit { 1.0 } else { -1.0 }
for i in 0..