///|
/// Shape and numeric errors from generic coupling-plan utilities.
pub(all) enum CouplingError {
EmptyCoupling
EmptyCouplingRow(Int)
RaggedCouplingRow(Int, Int, Int)
NonFiniteCouplingEntry(Int, Int)
NegativeCouplingEntry(Int, Int)
CostRowCountMismatch
CostColumnCountMismatch(Int)
NonFiniteCost(Int, Int)
NegativeCost(Int, Int)
NumericalOverflow(Int, Int)
}
///|
/// Row and column sums of a dense coupling matrix.
pub(all) struct CouplingMarginals {
source : Array[Double]
target : Array[Double]
}
///|
/// Validate a non-empty dense coupling and return its column count.
fn validate_coupling(
coupling : Array[Array[Double]],
) -> Result[Int, CouplingError] {
if coupling.length() == 0 {
return Err(CouplingError::EmptyCoupling)
}
let columns = coupling[0].length()
if columns == 0 {
return Err(CouplingError::EmptyCouplingRow(0))
}
for i in 0.. Result[CouplingMarginals, CouplingError] {
let columns = match validate_coupling(coupling) {
Err(error) => return Err(error)
Ok(value) => value
}
let source = Array::make(coupling.length(), 0.0)
let target = Array::make(columns, 0.0)
for i in 0..` of a dense coupling.
///
/// The plan and cost matrix must be non-empty rectangular matrices with the
/// same shape. Coupling entries and costs must be finite and nonnegative. This
/// diagnostic is distinct from the entropy-regularized objective returned by
/// the solver.
pub fn coupling_transport_cost(
coupling : Array[Array[Double]],
cost : Array[Array[Double]],
) -> Result[Double, CouplingError] {
let columns = match validate_coupling(coupling) {
Err(error) => return Err(error)
Ok(value) => value
}
if cost.length() != coupling.length() {
return Err(CouplingError::CostRowCountMismatch)
}
let mut total = 0.0
for i in 0..