Skip to main content

pounce_linsol/
scaling.rs

1//! Symmetric-matrix scaling for triplet inputs.
2//!
3//! Port of `Algorithm/LinearSolvers/IpTSymScalingMethod.hpp`. A scaling
4//! method takes the matrix in triplet form `(airn, ajcn, a)` and writes
5//! a per-row scaling factor `s[i]` to `scaling_factors`. The
6//! `TSymLinearSolver` wrapper then applies the symmetric scaling
7//! `A' = diag(s) · A · diag(s)` (and the inverse to the RHS / solution)
8//! before / after delegating to the backend.
9//!
10//! Variants registered upstream:
11//!
12//! * `none` — no scaling, default in many problem classes
13//!   ([`IdentityScalingMethod`]).
14//! * `mc19` — HSL MC19 row/column scaling. Bit-equivalence-default;
15//!   implemented as `pounce_hsl::Mc19TSymScalingMethod` (FFI to
16//!   `libcoinhsl.dylib`'s `mc19ad_`).
17//! * `slack-based` — slack-aware scaling driven by the current
18//!   barrier slacks. Implemented as
19//!   `pounce_algorithm::kkt::SlackBasedTSymScalingMethod`; lives in
20//!   the algorithm crate because it reads `IpoptData::curr` /
21//!   `IpoptCq::curr_slack_*`, which would otherwise create a
22//!   circular dependency.
23
24use pounce_common::types::{Index, Number};
25
26/// Backend-agnostic scaling method.
27///
28/// Returns `true` on success. On `false` the caller must skip scaling
29/// (mirrors upstream's `ComputeSymTScalingFactors` contract).
30pub trait TSymScalingMethod {
31    fn compute_sym_t_scaling_factors(
32        &mut self,
33        n: Index,
34        nnz: Index,
35        airn: &[Index],
36        ajcn: &[Index],
37        a: &[Number],
38        scaling_factors: &mut [Number],
39    ) -> bool;
40}
41
42/// `linear_system_scaling=none` — write identity scaling factors. The
43/// `TSymLinearSolver` wrapper detects this case and skips the symmetric
44/// scaling pass entirely; this implementation exists so that callers
45/// who hand a scaling method unconditionally get a sensible default.
46#[derive(Debug, Default, Clone, Copy)]
47pub struct IdentityScalingMethod;
48
49impl TSymScalingMethod for IdentityScalingMethod {
50    fn compute_sym_t_scaling_factors(
51        &mut self,
52        n: Index,
53        _nnz: Index,
54        _airn: &[Index],
55        _ajcn: &[Index],
56        _a: &[Number],
57        scaling_factors: &mut [Number],
58    ) -> bool {
59        debug_assert_eq!(scaling_factors.len(), n as usize);
60        for s in scaling_factors.iter_mut() {
61            *s = 1.0;
62        }
63        true
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn identity_writes_unit_factors() {
73        let mut method = IdentityScalingMethod;
74        let irn = [1, 2, 2];
75        let jcn = [1, 1, 2];
76        let vals = [2.0, 1.0, 3.0];
77        let mut s = vec![0.0; 2];
78        assert!(method.compute_sym_t_scaling_factors(2, 3, &irn, &jcn, &vals, &mut s));
79        assert_eq!(s, &[1.0, 1.0]);
80    }
81}