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}