Skip to main content

resopt/solve/
options.rs

1/// External problem scaling applied before the backend-specific solve.
2#[derive(Debug, Clone, PartialEq)]
3pub struct ScalingOptions {
4    pub enable: bool,
5    pub min_scale: f64,
6    pub max_scale: f64,
7}
8
9impl Default for ScalingOptions {
10    fn default() -> Self {
11        Self {
12            enable: true,
13            min_scale: 1e-4,
14            max_scale: 1e4,
15        }
16    }
17}
18
19/// Clarabel-specific tuning knobs that materially affect numerical robustness.
20#[derive(Debug, Clone, PartialEq)]
21pub struct ClarabelOptions {
22    pub equilibrate_enable: bool,
23    pub equilibrate_max_iter: u32,
24    pub presolve_enable: bool,
25    pub direct_solve_method: Option<String>,
26    pub tol_ktratio: f64,
27    pub reduced_tol_gap_abs: f64,
28    pub reduced_tol_gap_rel: f64,
29    pub reduced_tol_feas: f64,
30    pub reduced_tol_infeas_abs: f64,
31    pub reduced_tol_infeas_rel: f64,
32    pub reduced_tol_ktratio: f64,
33    pub static_regularization_enable: bool,
34    pub static_regularization_constant: f64,
35    pub dynamic_regularization_enable: bool,
36    pub dynamic_regularization_eps: f64,
37    pub dynamic_regularization_delta: f64,
38    pub iterative_refinement_enable: bool,
39    pub iterative_refinement_reltol: f64,
40    pub iterative_refinement_abstol: f64,
41    pub iterative_refinement_max_iter: u32,
42}
43
44impl Default for ClarabelOptions {
45    fn default() -> Self {
46        Self {
47            equilibrate_enable: true,
48            equilibrate_max_iter: 10,
49            presolve_enable: true,
50            direct_solve_method: None,
51            tol_ktratio: 1e-6,
52            reduced_tol_gap_abs: 5e-5,
53            reduced_tol_gap_rel: 5e-5,
54            reduced_tol_feas: 1e-4,
55            reduced_tol_infeas_abs: 5e-12,
56            reduced_tol_infeas_rel: 5e-5,
57            reduced_tol_ktratio: 1e-4,
58            static_regularization_enable: true,
59            static_regularization_constant: 1e-8,
60            dynamic_regularization_enable: true,
61            dynamic_regularization_eps: 1e-13,
62            dynamic_regularization_delta: 2e-7,
63            iterative_refinement_enable: true,
64            iterative_refinement_reltol: 1e-13,
65            iterative_refinement_abstol: 1e-12,
66            iterative_refinement_max_iter: 10,
67        }
68    }
69}
70
71/// Generic solver options shared by the public API.
72#[derive(Debug, Clone, PartialEq)]
73pub struct SolveOptions {
74    pub tolerance: f64,
75    pub max_iterations: usize,
76    pub verbose: bool,
77    pub scaling: ScalingOptions,
78    pub clarabel: ClarabelOptions,
79    pub retry_on_numerical_failure: bool,
80}
81
82impl Default for SolveOptions {
83    fn default() -> Self {
84        Self {
85            tolerance: 1e-8,
86            max_iterations: 10_000,
87            verbose: false,
88            scaling: ScalingOptions::default(),
89            clarabel: ClarabelOptions::default(),
90            retry_on_numerical_failure: true,
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn defaults() {
101        let opts = SolveOptions::default();
102        assert!((opts.tolerance - 1e-8).abs() < f64::EPSILON);
103        assert_eq!(opts.max_iterations, 10_000);
104        assert!(!opts.verbose);
105        assert!(opts.scaling.enable);
106        assert!(opts.clarabel.equilibrate_enable);
107        assert!(opts.retry_on_numerical_failure);
108    }
109
110    #[test]
111    fn custom() {
112        let opts = SolveOptions {
113            tolerance: 1e-4,
114            max_iterations: 500,
115            verbose: true,
116            scaling: ScalingOptions {
117                enable: false,
118                min_scale: 1e-3,
119                max_scale: 1e3,
120            },
121            clarabel: ClarabelOptions {
122                presolve_enable: false,
123                direct_solve_method: Some("qdldl".to_string()),
124                ..ClarabelOptions::default()
125            },
126            retry_on_numerical_failure: false,
127        };
128
129        assert!((opts.tolerance - 1e-4).abs() < f64::EPSILON);
130        assert_eq!(opts.max_iterations, 500);
131        assert!(opts.verbose);
132        assert!(!opts.scaling.enable);
133        assert_eq!(opts.clarabel.direct_solve_method.as_deref(), Some("qdldl"));
134        assert!(!opts.retry_on_numerical_failure);
135    }
136
137    #[test]
138    fn clone_and_eq() {
139        let a = SolveOptions::default();
140        assert_eq!(a, a.clone());
141    }
142}