///|
fn selection(indices : Array[Int32], count : Int32) -> Unit raise ChoiceError {
if indices.length() == 0 {
raise Invalid("selection must not be empty")
}
for i, index in indices {
if index < 0 || index >= count {
raise Invalid("selection index out of bounds")
}
for j in 0.. DecisionMatrix raise ChoiceError {
selection(indices, self.names.length())
decision_matrix(
indices.map(i => self.names[i]),
self.criteria,
self.directions,
indices.map(i => self.cells[i]),
)
}
///|
pub fn DecisionMatrix::select_criteria(
self : DecisionMatrix,
indices : Array[Int32],
) -> DecisionMatrix raise ChoiceError {
selection(indices, self.criteria.length())
decision_matrix(
self.names,
indices.map(i => self.criteria[i]),
indices.map(i => self.directions[i]),
self.cells.map(row => indices.map(j => row[j])),
)
}
///|
/// Affine unit conversion must be strictly increasing; directions stay unchanged.
pub fn DecisionMatrix::convert_units(
self : DecisionMatrix,
scales : Array[Double],
offsets : Array[Double],
) -> DecisionMatrix raise ChoiceError {
let (n, p) = self.shape()
if scales.length() != p || offsets.length() != p {
raise Invalid("unit transform shape mismatch")
}
for j in 0.. RemovalAnalysis raise ChoiceError {
let n = m.names.length()
if n < 2 || removed < 0 || removed >= n {
raise Invalid(
"removal requires at least two alternatives and a valid index",
)
}
let baseline = evaluate(m, weights, algorithm)
let indices = [ for i in 0.. i ]
let subset = m.select_alternatives(indices)
let remaining = evaluate(subset, weights, algorithm)
let projected = rank_scores(
subset.names,
indices.map(i => baseline.scores[i]),
higher_is_better=baseline.higher_is_better,
tolerance=baseline.tie_tolerance,
)
let agreement = rank_agreement(projected, remaining)
let winner_changed = projected.winners() != remaining.winners()
{ removed: m.names[removed], baseline, remaining, agreement, winner_changed, }
}
///|
pub extend RemovalAnalysis with @debug.Debug::{to_repr}