moonot

    Finite-support regularized optimal transport solvers in MoonBit

    optimal-transport
    sinkhorn
    optimization
    numerics
    Download zip
    Version
    0.1.3
    License
    Apache-2.0
    Last updated
    16 days ago
    Downloads
    42

    No dependencies

    #MoonOT

    MoonOT 是纯 MoonBit 实现的密集、有限支持熵正则最优传输库。公开 API 提供 balanced_sinkhorn、unbalanced_sinkhorn、balanced_sinkhorn_divergence 和共享/异构代价固定支持重心,可复用于传输计划、去偏分布比较和分布聚合。

    库显式报告目标分量、终止状态、数值残差及零质量边界。残差不是最优间隙证书。安装、示例与范围见 中文 README,公式与数值限制见 数值契约 和 算法推导。

    BarycenterCostsError

    pub(all) enum BarycenterCostsError {
    InputError(BarycenterError)
    CostCountMismatch
    CostRowCountMismatch(Int)
    CostColumnCountMismatch(Int, Int)
    NonFiniteCost(Int, Int, Int)
    NegativeCost(Int, Int, Int)
    SolverError(Int, SinkhornError)
    NumericalFailure
    }

    Validation and numerical errors from regularized_barycenter_with_costs.

    BarycenterError

    pub(all) enum BarycenterError {
    EmptyMeasures
    WeightCountMismatch
    EmptySupport
    EmptyMeasure(Int)
    CostRowCountMismatch
    CostColumnCountMismatch(Int)
    NonFiniteWeight(Int)
    NonPositiveWeight(Int)
    NonFiniteWeightTotal
    NonFiniteMass(Int, Int)
    NegativeMass(Int, Int)
    NonFiniteMeasureTotal(Int)
    UnequalMeasureTotal(Int)
    NonFiniteCost(Int, Int)
    NegativeCost(Int, Int)
    InvalidEpsilon
    InvalidTolerance
    InvalidIterationLimit
    SolverError(SinkhornError)
    NumericalFailure
    }

    Validation and numerical errors reported by regularized_barycenter.

    BarycenterResult

    pub(all) struct BarycenterResult {
    barycenter : Array[Double]
    weighted_objective : Double
    maximum_transport_residual : Double
    log_barycenter_residual : Double
    iterations : Int
    termination : BarycenterTermination
    }

    The support weights, objective estimate, and convergence diagnostics of a regularized barycenter.

    BarycenterTermination

    pub(all) enum BarycenterTermination {
    Converged
    IterationLimit
    }

    Termination state of a regularized discrete Wasserstein barycenter solve.

    CouplingCompositionError

    pub(all) enum CouplingCompositionError {
    InvalidTolerance
    InvalidLeft(CouplingError)
    InvalidRight(CouplingError)
    BridgeDimensionMismatch(Int, Int)
    BridgeMassMismatch(Int, Double, Double)
    UndefinedBridgeConditional(Int)
    NumericalOverflow(Int, Int, Int)
    InvalidOutput(CouplingError)
    }

    Validation and arithmetic errors for Markov gluing of two dense plans. Indices are zero-based; bridge dimensions are counts.

    CouplingError

    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)
    }

    Shape and numeric errors from generic coupling-plan utilities.

    CouplingInformationError

    pub(all) enum CouplingInformationError {
    InvalidCoupling(CouplingError)
    InvalidReference(CouplingError)
    RowCountMismatch(Int, Int)
    ColumnCountMismatch(Int, Int)
    ZeroReferenceSupport(Int, Int)
    NumericalOverflow(Int, Int)
    }

    Validation and arithmetic errors for dense coupling information diagnostics. Coordinates are zero-based; dimensions are counts, not inferred from mass.

    CouplingMarginals

    pub(all) struct CouplingMarginals {
    source : Array[Double]
    target : Array[Double]
    }

    Row and column sums of a dense coupling matrix.

    CouplingOrientationError

    pub(all) enum CouplingOrientationError {
    InvalidCoupling(CouplingError)
    SourcePermutationLength
    TargetPermutationLength
    SourceIndexOutOfRange(Int)
    TargetIndexOutOfRange(Int)
    DuplicateSourceIndex(Int)
    DuplicateTargetIndex(Int)
    }

    Validation errors from coupling reorientation; permutation indices are zero-based.

    CouplingProjectionError

    pub(all) enum CouplingProjectionError {
    InvalidCoupling(CouplingError)
    TargetValueCountMismatch
    EmptyTargetFeatureVector
    RaggedTargetValuesRow(Int, Int, Int)
    NonFiniteTargetValue(Int, Int)
    ZeroMassSourceRow(Int)
    NumericalOverflow(Int, Int)
    }

    Validation failures for conditional barycentric projections from a coupling.

    CouplingRoundingError

    pub(all) enum CouplingRoundingError {
    InvalidCoupling(CouplingError)
    SourceCountMismatch
    TargetCountMismatch
    NonFiniteSourceMass(Int)
    NonFiniteTargetMass(Int)
    NegativeSourceMass(Int)
    NegativeTargetMass(Int)
    NonFiniteSourceTotal
    NonFiniteTargetTotal
    UnequalTotalMass
    InvalidTolerance
    NumericalFailure
    FeasibilityFailure(Double)
    }

    Input, arithmetic, and final-feasibility failures from coupling rounding. Indices are zero-based. FeasibilityFailure contains the measured residual.

    CouplingRoundingResult

    pub(all) struct CouplingRoundingResult {
    coupling : Array[Array[Double]]
    marginal_residual : Double
    }

    A nonnegative rounded plan and its actual maximum absolute marginal error. This is a feasibility diagnostic, not an optimality or convergence claim.

    PointCostError

    pub(all) enum PointCostError {
    EmptySourceSupport
    EmptyTargetSupport
    EmptySourcePoint(Int)
    EmptyTargetPoint(Int)
    SourcePointDimensionMismatch(Int, Int, Int)
    TargetPointDimensionMismatch(Int, Int, Int)
    PointDimensionMismatch(Int, Int)
    NonFiniteSourceCoordinate(Int, Int)
    NonFiniteTargetCoordinate(Int, Int)
    NumericalOverflow(Int, Int)
    }

    Shape and numeric errors from point-cloud cost-matrix construction.

    SinkhornContinuationError

    pub(all) enum SinkhornContinuationError {
    EmptySchedule
    InvalidEpsilon(Int)
    NonDecreasingEpsilon(Int)
    InvalidTolerance(Int)
    InvalidIterationLimit(Int)
    StageFailed(Int, SinkhornError, SinkhornContinuationProgress)
    }

    Schedule validation errors and lossless, stage-indexed solver errors. All indices are zero-based. Validation completes before any solver runs. NonDecreasingEpsilon(i) compares stage i to stage i - 1.

    SinkhornContinuationProgress

    pub(all) struct SinkhornContinuationProgress {
    stages : Array[SinkhornContinuationStage]
    total_iterations : Int64
    last_seed : SinkhornWarmStart?
    }

    Work completed before a solver error. The failing stage is not included. last_seed is the last completed stage's seed, or the caller's initial seed when no stage completed. Iterations performed inside a failing solve are not available from the underlying solver and are not counted.

    SinkhornContinuationResult

    pub(all) struct SinkhornContinuationResult {
    stages : Array[SinkhornContinuationStage]
    total_iterations : Int64
    last_seed : SinkhornWarmStart
    termination : SinkhornTermination
    }

    Completed schedule, including stages that reached their iteration limits. termination is exclusively the final stage's termination, not an aggregate. The final transport plan is stages[stages.length() - 1].result.

    SinkhornContinuationStage

    pub(all) struct SinkhornContinuationStage {
    options : SinkhornOptions
    result : SinkhornResult
    }

    One completed continuation stage, in caller-supplied schedule order. result.iterations counts full row/column updates at this epsilon only.

    SinkhornDivergenceError

    pub(all) enum SinkhornDivergenceError {
    SourceSelfCostRowCountMismatch
    SourceSelfCostColumnCountMismatch(Int)
    TargetSelfCostRowCountMismatch
    TargetSelfCostColumnCountMismatch(Int)
    AsymmetricSourceSelfCost(Int, Int)
    AsymmetricTargetSelfCost(Int, Int)
    CrossTransport(SinkhornError)
    SourceSelfTransport(SinkhornError)
    TargetSelfTransport(SinkhornError)
    NumericalFailure
    }

    Shape, symmetry, wrapped-solver, and arithmetic errors for balanced_sinkhorn_divergence.

    SinkhornDivergenceResult

    pub(all) struct SinkhornDivergenceResult {
    value : Double
    cross : SinkhornResult
    source_self : SinkhornResult
    target_self : SinkhornResult
    }

    A regularized OT solve contributing to a balanced Sinkhorn divergence.

    The result retains each underlying solve, including its coupling and convergence diagnostics. value uses the three regularized_objective values exactly as returned by balanced_sinkhorn.

    SinkhornError

    pub(all) enum SinkhornError {
    EmptySourceMarginal
    EmptyTargetMarginal
    CostRowCountMismatch
    CostColumnCountMismatch(Int)
    NegativeSourceMass(Int)
    NegativeTargetMass(Int)
    NonFiniteSourceMass(Int)
    NonFiniteTargetMass(Int)
    NonFiniteTotalMass
    UnequalTotalMass
    NegativeCost(Int, Int)
    NonFiniteCost(Int, Int)
    InvalidEpsilon
    InvalidTolerance
    InvalidIterationLimit
    InvalidWarmStart
    NumericalFailure
    }

    Input and numerical errors reported by balanced_sinkhorn.

    SinkhornOptions

    pub(all) struct SinkhornOptions {
    epsilon : Double
    tolerance : Double
    max_iterations : Int
    }

    Parameters for the balanced log-domain Sinkhorn solver. tolerance is an absolute maximum marginal residual.

    SinkhornResult

    pub(all) struct SinkhornResult {
    coupling : Array[Array[Double]]
    transport_cost : Double
    entropy_regularizer : Double
    regularized_objective : Double
    marginal_residual : Double
    iterations : Int
    termination : SinkhornTermination
    }

    A transport plan and diagnostics for the regularized problem <P, C> + epsilon * sum(P * (log(P) - 1)).

    SinkhornTermination

    pub(all) enum SinkhornTermination {
    Converged
    IterationLimit
    }

    Status of a completed balanced Sinkhorn run.

    SinkhornWarmStart

    pub struct SinkhornWarmStart {
    source_potential : Array[Double]
    target_potential : Array[Double]
    }

    Opaque dual-potential seed returned by a balanced solve.

    It can initialize another solve with compatible support dimensions. Values are expressed in cost units, so they can be reused with a different epsilon.

    UnbalancedSinkhornError

    pub(all) enum UnbalancedSinkhornError {
    EmptySourceMarginal
    EmptyTargetMarginal
    CostRowCountMismatch
    CostColumnCountMismatch(Int)
    NegativeSourceMass(Int)
    NegativeTargetMass(Int)
    NonFiniteSourceMass(Int)
    NonFiniteTargetMass(Int)
    NegativeCost(Int, Int)
    NonFiniteCost(Int, Int)
    InvalidEpsilon
    InvalidSourcePenalty
    InvalidTargetPenalty
    InvalidCostWeight
    InvalidTolerance
    InvalidIterationLimit
    NumericalFailure
    }

    Validation and numerical errors from unbalanced_sinkhorn.

    UnbalancedSinkhornOptions

    pub(all) struct UnbalancedSinkhornOptions {
    epsilon : Double
    rho_source : Double
    rho_target : Double
    cost_weight : Double
    tolerance : Double
    max_iterations : Int
    }

    Parameters for unbalanced_sinkhorn.

    cost_weight multiplies every entry of the cost matrix. tolerance bounds the maximum change in the log scaling factors between complete iterations.

    UnbalancedSinkhornResult

    pub(all) struct UnbalancedSinkhornResult {
    coupling : Array[Array[Double]]
    objective : Double
    transport_cost : Double
    reference_kl : Double
    source_marginal_kl : Double
    target_marginal_kl : Double
    source_marginal_residual : Double
    target_marginal_residual : Double
    scaling_residual : Double
    iterations : Int
    termination : UnbalancedSinkhornTermination
    }

    Plan, objective components, and diagnostics for a KL-unbalanced solve.

    The objective is <P, cost_weight * C> + epsilon * KL(P || a tensor b) + rho_source * KL(P 1 || a) + rho_target * KL(P^T 1 || b). Each KL is the generalized I-divergence x log(x/y) - x + y.

    UnbalancedSinkhornTermination

    pub(all) enum UnbalancedSinkhornTermination {
    Converged
    IterationLimit
    }

    End state of a KL-unbalanced Sinkhorn iteration.

    UnbalancedSinkhornWarmStart

    pub struct UnbalancedSinkhornWarmStart {
    log_u : Array[Double]
    log_v : Array[Double]
    epsilon : Double
    rows : Int
    columns : Int
    }

    Opaque log-scaling state from a KL-unbalanced Sinkhorn solve.

    A seed is only an initialization for a solve with the same source and target dimensions. Its arrays and source epsilon are private and copied on reuse; coordinate meaning and ordering remain the caller's responsibility.

    UnbalancedSinkhornWarmStartError

    pub(all) enum UnbalancedSinkhornWarmStartError {
    SolverError(UnbalancedSinkhornError)
    InvalidWarmStart
    }

    Compatibility and solver errors from the unbalanced warm-start API.

    WeightedPointCostError

    pub(all) enum WeightedPointCostError {
    InvalidPoints(PointCostError)
    WeightDimensionMismatch
    NonFiniteWeight(Int)
    NegativeWeight(Int)
    NumericalOverflow(Int, Int)
    }

    Errors for diagonal feature-weighted squared Euclidean costs.

    balanced_sinkhorn

    fn balanced_sinkhorn(source : Array[Double], target : Array[Double], cost : Array[Array[Double]], options : SinkhornOptions) -> Result[SinkhornResult, SinkhornError]

    Solve balanced entropy-regularized optimal transport in the log domain.

    Marginal totals must agree within 1.0e-12 relative error. Costs must be finite and nonnegative; zero marginal entries are allowed and produce zero rows or columns in the coupling. The reported residual is the maximum absolute row or column marginal error against the supplied marginals.

    balanced_sinkhorn_continuation

    fn balanced_sinkhorn_continuation(source : Array[Double], target : Array[Double], cost : Array[Array[Double]], schedule : Array[SinkhornOptions], warm_start : SinkhornWarmStart?) -> Result[SinkhornContinuationResult, SinkhornContinuationError]

    Solve balanced transport along a strictly decreasing epsilon schedule.

    Each schedule entry supplies a finite positive epsilon and absolute marginal tolerance, plus a positive iteration limit for that stage (not a shared budget). A single entry is valid. Entire-schedule validation precedes input validation by the solver. At each index, epsilon, decreasing order, tolerance, then iteration limit are checked, in that order.

    Every stage runs balanced_sinkhorn_with_warm_start; the returned opaque cost-unit seed is passed unchanged to the next stage. An intermediate IterationLimit is recorded and does not stop continuation. Only the final stage determines convergence. Solver errors stop immediately and preserve both their original cause and the completed progress. No input or seed is mutated, and no schedule is generated, sorted, or normalized.

    balanced_sinkhorn_divergence

    fn balanced_sinkhorn_divergence(source : Array[Double], target : Array[Double], cross_cost : Array[Array[Double]], source_self_cost : Array[Array[Double]], target_self_cost : Array[Array[Double]], options : SinkhornOptions) -> Result[SinkhornDivergenceResult, SinkhornDivergenceError]

    Compute the balanced entropy-regularized Sinkhorn divergence

    OT_epsilon(source, target; cross_cost) - 0.5 * OT_epsilon(source, source; source_self_cost) - 0.5 * OT_epsilon(target, target; target_self_cost).

    Each OT_epsilon value is exactly the regularized_objective from balanced_sinkhorn, namely <P, C> + epsilon * sum(P * (log(P) - 1)). The source and target self-cost matrices must be square for their respective marginals and symmetric up to relative/absolute tolerance 1.0e-12 * max(1, |x|, |y|). The cross-cost matrix is validated by the underlying solver. Solver errors are wrapped with the solve that produced them, and non-finite final arithmetic returns NumericalFailure.

    This API does not promise a nonnegative result for arbitrary cost matrices. Nonnegativity requires suitable mutually consistent ground-cost assumptions (for example, costs induced by an appropriate metric); symmetry alone is not sufficient. Each solve otherwise follows balanced_sinkhorn input rules, including finite, nonnegative cost entries and balanced finite marginals.

    balanced_sinkhorn_with_warm_start

    fn balanced_sinkhorn_with_warm_start(source : Array[Double], target : Array[Double], cost : Array[Array[Double]], options : SinkhornOptions, warm_start : SinkhornWarmStart?) -> Result[(SinkhornResult, SinkhornWarmStart), SinkhornError]

    Solve balanced transport using a prior solve's opaque dual-potential seed.

    Returns both the ordinary diagnostics and a seed for another solve. A warm start only changes the initial iterate; the requested tolerance, iteration limit, and termination checks still apply to the new problem.

    coupling_barycentric_projection

    fn coupling_barycentric_projection(coupling : Array[Array[Double]], target_values : Array[Array[Double]]) -> Result[Array[Array[Double]], CouplingProjectionError]

    Map each source row to the conditional weighted mean of target values.

    target_values[j] is a finite, non-empty feature vector associated with target column j. Every coupling row must have positive mass; a zero-mass row has no conditional barycentric projection and is reported explicitly. The operation preserves feature dimension but not source-row masses.

    coupling_compose

    fn coupling_compose(left : Array[Array[Double]], right : Array[Array[Double]], tolerance : Double) -> Result[Array[Array[Double]], CouplingCompositionError]

    Compose left[source, bridge] and right[bridge, target] by Markov gluing.

    Return sum_j left[i][j] * (right[j][k] / right_row_mass[j]). Both plans must be finite nonnegative non-empty rectangular matrices with finite marginals and matching bridge dimensions. Each bridge-mass difference must be at most the finite strictly positive absolute tolerance. Positive left bridge mass with a zero right row is always rejected; shared zero bridges are skipped. Inputs are never mutated or globally normalized.

    In exact arithmetic, source marginals are preserved; target marginals are preserved for exact bridge matches. For near matches the target L1 error is at most the sum of absolute bridge discrepancies (hence bridge_count times tolerance), before floating-point rounding. No optimality is implied.

    coupling_entropy

    fn coupling_entropy(coupling : Array[Array[Double]]) -> Result[Double, CouplingInformationError]

    Return sum(P * (log(P) - 1)), with zero entries contributing zero. This is the unscaled entropy regularizer, not Shannon entropy. The plan must be non-empty, rectangular, finite and nonnegative; it is not mutated or normalized. Non-finite terms or accumulation return a typed error.

    coupling_kl

    fn coupling_kl(coupling : Array[Array[Double]], reference : Array[Array[Double]]) -> Result[Double, CouplingInformationError]

    Return generalized KL(P || Q) = sum(p * log(p/q) - p + q). Both plans must be non-empty rectangular finite nonnegative matrices with identical dimensions; arbitrary and unequal total masses are allowed. At (0, q) the contribution is q (including (0, 0) -> 0); (p > 0, 0) returns ZeroReferenceSupport, never infinity. Near equality a relative-difference series avoids cancellation; otherwise logarithms are subtracted without constructing the potentially overflowing ratio. Inputs are neither normalized nor mutated.

    coupling_marginals

    fn coupling_marginals(coupling : Array[Array[Double]]) -> Result[CouplingMarginals, CouplingError]

    Compute source-row and target-column mass totals of a coupling plan.

    The input must be a non-empty, rectangular matrix of finite nonnegative entries. If summation exceeds the finite Double range, a typed numerical error is returned.

    coupling_permute

    fn coupling_permute(coupling : Array[Array[Double]], source_order : Array[Int], target_order : Array[Int]) -> Result[Array[Array[Double]], CouplingOrientationError]

    Reindex both supports without losing mass: output[i][j] = input[source[i]][target[j]]. Each index array must be a complete bijection of its respective support. Inputs are validated before any output is constructed, and are not modified.

    coupling_transport_cost

    fn coupling_transport_cost(coupling : Array[Array[Double]], cost : Array[Array[Double]]) -> Result[Double, CouplingError]

    Compute the unregularized transport cost <P, C> 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.

    coupling_transpose

    fn coupling_transpose(coupling : Array[Array[Double]]) -> Result[Array[Array[Double]], CouplingError]

    Return a fresh target-by-source transpose. No storage is shared with input.

    euclidean_cost_matrix

    fn euclidean_cost_matrix(source_points : Array[Array[Double]], target_points : Array[Array[Double]]) -> Result[Array[Array[Double]], PointCostError]

    Build Euclidean-distance costs between two finite point supports.

    Validation and orientation follow squared_euclidean_cost_matrix; each entry is the Euclidean norm of the coordinate-difference vector. Repeated hypot accumulation avoids squaring large finite coordinates unnecessarily.

    manhattan_cost_matrix

    fn manhattan_cost_matrix(source_points : Array[Array[Double]], target_points : Array[Array[Double]]) -> Result[Array[Array[Double]], PointCostError]

    Build Manhattan-distance costs between two finite point supports.

    Validation and orientation follow squared_euclidean_cost_matrix; each entry is the sum of absolute coordinate differences.

    regularized_barycenter

    fn regularized_barycenter(measures : Array[Array[Double]], weights : Array[Double], cost : Array[Array[Double]], options : SinkhornOptions) -> Result[BarycenterResult, BarycenterError]

    Compute the fixed-support entropy-regularized Wasserstein barycenter.

    Every input measure and the barycenter share one support with equal total mass. Weights must be finite and strictly positive; they are normalized internally. The common square cost matrix must be finite and nonnegative. The iteration uses log-domain generalized Sinkhorn updates. Its tolerance bounds the maximum change in normalized log barycenter between iterations. The objective is the weighted sum of balanced Sinkhorn objectives, computed after the barycenter iteration. The reported transport residual is the largest marginal residual among those objective-evaluation solves.

    regularized_barycenter_with_costs

    fn regularized_barycenter_with_costs(measures : Array[Array[Double]], weights : Array[Double], costs : Array[Array[Array[Double]]], options : SinkhornOptions) -> Result[BarycenterResult, BarycenterCostsError]

    Compute a fixed-support regularized barycenter with one ground-cost matrix per input measure.

    costs[k][i][j] is the cost from common barycenter-support position i to position j of measures[k]. Every matrix is square and has the same row ordering, while its columns align with its paired measure. Costs need not be symmetric or metric. The same epsilon and normalized positive weights apply to all subproblems; this function does not learn or rescale costs.

    round_balanced_coupling

    fn round_balanced_coupling(coupling : Array[Array[Double]], source : Array[Double], target : Array[Double], tolerance : Double) -> Result[CouplingRoundingResult, CouplingRoundingError]

    Round a dense nonnegative coupling toward supplied balanced marginals.

    Clip rows by min(source[i] / row_sum[i], 1), then columns by min(target[j] / column_sum[j], 1), leaving zero sums unchanged. Add a nonnegative rank-one repair from the resulting positive marginal deficits. Its denominator is the larger deficit total; for exactly balanced arithmetic both totals agree. No caller inputs are changed or normalized.

    Marginal totals must be finite and agree within 1.0e-12 relative to their maximum (both zero is valid). The finite positive tolerance bounds the final maximum absolute marginal residual against the original marginals. A larger residual is an explicit FeasibilityFailure, even for nearly equal totals. Both-zero marginals return an all-zero plan after input validation.

    squared_euclidean_cost_matrix

    fn squared_euclidean_cost_matrix(source_points : Array[Array[Double]], target_points : Array[Array[Double]]) -> Result[Array[Array[Double]], PointCostError]

    Build squared-Euclidean costs between two finite point supports.

    Each row is one point. The returned matrix has source points on rows and target points on columns. Both supports and every point must be non-empty; all points must have the same dimension and finite coordinates. A cost whose subtraction, square, or sum is not representable returns NumericalOverflow(source_index, target_index) rather than an infinite entry.

    unbalanced_sinkhorn

    fn unbalanced_sinkhorn(source : Array[Double], target : Array[Double], cost : Array[Array[Double]], options : UnbalancedSinkhornOptions) -> Result[UnbalancedSinkhornResult, UnbalancedSinkhornError]

    Solve entropy-regularized unbalanced optimal transport in the log domain.

    It minimizes sum(P[i,j] * cost_weight * C[i,j]) + epsilon * KL(P || a tensor b) + rho_source * KL(P 1 || a) + rho_target * KL(P^T 1 || b), where every KL is the generalized I-divergence sum(x * log(x / y) - x + y). The scalar cost_weight is finite and nonnegative. Costs, marginals, and penalties must be finite and nonnegative; epsilon and tolerance must be strictly positive. Marginals need not have equal totals.

    If a reference mass y is zero, finite generalized KL requires x == 0; KL(0 || 0) is zero and KL(0 || y) for positive y is y. Since a tensor b is the reference for the coupling KL, zero source or target entries force the corresponding rows or columns of P to zero. A fully zero source or target therefore has the unique zero coupling. The returned scaling residual is the maximum absolute log-scaling update; marginal residuals are maximum absolute differences from the supplied marginals and are diagnostics, not convergence criteria.

    unbalanced_sinkhorn_with_warm_start

    fn unbalanced_sinkhorn_with_warm_start(source : Array[Double], target : Array[Double], cost : Array[Array[Double]], options : UnbalancedSinkhornOptions, warm_start : UnbalancedSinkhornWarmStart?) -> Result[(UnbalancedSinkhornResult, UnbalancedSinkhornWarmStart), UnbalancedSinkhornWarmStartError]

    Solve KL-unbalanced transport from an optional prior solve's scaling state.

    The returned seed is reusable even when this call reaches its iteration limit. A seed only initializes the next call: the new inputs, objective, tolerance, and iteration budget are evaluated normally. No speedup is guaranteed. Coordinates retain the caller-defined ordering.

    weighted_squared_euclidean_cost_matrix

    fn weighted_squared_euclidean_cost_matrix(source_points : Array[Array[Double]], target_points : Array[Array[Double]], weights : Array[Double]) -> Result[Array[Array[Double]], WeightedPointCostError]

    Compute sum_d weights[d] * (source[i][d] - target[j][d])^2. Zero weights intentionally ignore a coordinate (a semimetric), but every input coordinate must still be finite. Inputs are never mutated.