osqp_rust/
settings.rs

1use osqp_rust_sys as ffi;
2use std::mem;
3use std::ptr;
4use std::time::Duration;
5
6use crate::{float, Problem};
7
8/// The linear system solver for OSQP to use.
9#[derive(Clone, Debug, PartialEq)]
10pub enum LinsysSolver {
11    Qdldl,
12    MklPardiso,
13    // Prevent exhaustive enum matching
14    #[doc(hidden)]
15    __Nonexhaustive,
16}
17
18macro_rules! u32_to_osqp_int {
19    ($name:ident, $value:expr) => {{
20        let value = $value;
21        assert!(
22            value as u64 <= ffi::src::src::osqp::c_int::max_value() as u64,
23            "{} must be smaller than the largest isize value",
24            stringify!($name)
25        );
26        value as ffi::src::src::osqp::c_int
27    }};
28}
29
30macro_rules! rust_type {
31    (float) => (float);
32    (u32) => (u32);
33    (option_u32) => (Option<u32>);
34    (bool) => (bool);
35    (linsys_solver) => (LinsysSolver);
36    (option_duration) => (Option<Duration>);
37}
38
39macro_rules! convert_rust_type {
40    ($name:ident, float, $value:expr) => ($value);
41    ($name:ident, u32, $value:expr) => (u32_to_osqp_int!($name, $value));
42    ($name:ident, option_u32, $value:expr) => (u32_to_osqp_int!($name, $value.unwrap_or(0)));
43    ($name:ident, bool, $value:expr) => ($value as ffi::src::src::osqp::c_int);
44    ($name:ident, linsys_solver, $value:expr) => (
45        match $value {
46            LinsysSolver::Qdldl => ffi::src::src::osqp::QDLDL_SOLVER,
47            LinsysSolver::MklPardiso => ffi::src::src::osqp::MKL_PARDISO_SOLVER,
48            LinsysSolver::__Nonexhaustive => unreachable!(),
49        }
50    );
51    ($name:ident, option_duration, $value:expr) => (
52        $value.map(|v| {
53            let mut secs = duration_to_secs(v);
54            // Setting time_limit to 0.0 disables the time limit to we treat a duration of zero as
55            // a very small time limit instead.
56            if secs == 0.0 {
57                secs = 1e-12;
58            }
59            secs
60        }).unwrap_or(0.0)
61    );
62}
63
64macro_rules! settings {
65    ($problem_ty:ty, $(
66        #[$doc:meta] $name:ident: $typ:ident $([$update_name:ident, $update_ffi:ident])*,
67    )*) => (
68        /// The settings used when initialising a solver.
69        pub struct Settings {
70            pub(crate) inner: ffi::src::src::osqp::OSQPSettings,
71        }
72
73        impl Settings {
74            $(
75                #[$doc]
76                pub fn $name(mut self, value: rust_type!($typ)) -> Settings {
77                    self.inner.$name = convert_rust_type!($name, $typ, value);
78                    Settings {
79                        inner: self.inner
80                    }
81                }
82            )*
83        }
84
85        impl Clone for Settings {
86            fn clone(&self) -> Settings {
87                unsafe {
88                    Settings {
89                        inner: ptr::read(&self.inner)
90                    }
91                }
92            }
93        }
94
95        impl Default for Settings {
96            fn default() -> Settings {
97                unsafe {
98                    let mut settings: ffi::src::src::osqp::OSQPSettings = mem::zeroed();
99                    ffi::src::src::osqp::osqp_set_default_settings(&mut settings);
100                    Settings {
101                        inner: settings
102                    }
103                }
104            }
105        }
106
107        unsafe impl Send for Settings {}
108        unsafe impl Sync for Settings {}
109
110        impl $problem_ty {
111            $($(
112                #[$doc]
113                pub fn $update_name(&mut self, value: rust_type!($typ)) {
114                    unsafe {
115                        let ret = ffi::src::src::osqp::$update_ffi(
116                            self.workspace,
117                            convert_rust_type!($name, $typ, value)
118                        );
119                        if ret != 0 {
120                            panic!("updating {} failed", stringify!($name));
121                        }
122                    }
123                }
124            )*)*
125        }
126    );
127}
128
129settings! {
130    Problem,
131
132    #[doc = "Sets the ADMM step rho."]
133    rho: float [update_rho, osqp_update_rho],
134
135    #[doc = "Sets the ADMM step sigma."]
136    sigma: float,
137
138    #[doc = "
139    Sets the number of heuristic data scaling iterations.
140
141    If `None` scaling is disabled.
142
143    Panics on 32-bit platforms if the value is above `i32::max_value()`.
144    "]
145    scaling: option_u32,
146
147    #[doc = "Enables choosing rho adaptively."]
148    adaptive_rho: bool,
149
150    #[doc = "
151    Sets the number of iterations between rho adaptations.
152
153    If `None` it is automatic.
154
155    Panics on 32-bit platforms if the value is above `i32::max_value()`.
156    "]
157    adaptive_rho_interval: option_u32,
158
159    #[doc = "
160    Sets the tolerance for adapting rho.
161
162    The new rho has to be `value` times larger or `1/value` times smaller than the current rho to
163    trigger a new factorization.
164    "]
165    adaptive_rho_tolerance: float,
166
167    #[doc = "Set the interval for adapting rho as a fraction of the setup time."]
168    adaptive_rho_fraction: float,
169
170    #[doc = "
171    Sets the maximum number of ADMM iterations.
172
173    Panics on 32-bit platforms if the value is above `i32::max_value()`.
174    "]
175    max_iter: u32 [update_max_iter, osqp_update_max_iter],
176
177    #[doc = "Sets the absolute convergence tolerance."]
178    eps_abs: float [update_eps_abs, osqp_update_eps_abs],
179
180    #[doc = "Sets the relative convergence tolerance."]
181    eps_rel: float [update_eps_rel, osqp_update_eps_rel],
182
183    #[doc = "Sets the primal infeasibility tolerance."]
184    eps_prim_inf: float [update_eps_prim_inf, osqp_update_eps_prim_inf],
185
186    #[doc = "Sets the dual infeasibility tolerance."]
187    eps_dual_inf: float [update_eps_dual_inf, osqp_update_eps_dual_inf],
188
189    #[doc = "Sets the linear solver relaxation parameter."]
190    alpha: float [update_alpha, osqp_update_alpha],
191
192    #[doc = "Sets the linear system solver to use."]
193    linsys_solver: linsys_solver,
194
195    #[doc = "Sets the polishing regularization parameter."]
196    delta: float [update_delta, osqp_update_delta],
197
198    #[doc = "Enables polishing the ADMM solution."]
199    polish: bool [update_polish, osqp_update_polish],
200
201    #[doc = "
202    Sets the number of iterative refinement steps to use when polishing.
203
204    Panics on 32-bit platforms if the value is above `i32::max_value()`.
205    "]
206    polish_refine_iter: u32 [update_polish_refine_iter, osqp_update_polish_refine_iter],
207
208    #[doc = "Enables writing progress to stdout."]
209    verbose: bool [update_verbose, osqp_update_verbose],
210
211    #[doc = "Enables scaled termination criteria."]
212    scaled_termination: bool [update_scaled_termination, osqp_update_scaled_termination],
213
214    #[doc = "
215    Sets the number of ADMM iterations between termination checks.
216
217    If `None` termination checking is disabled.
218
219    Panics on 32-bit platforms if the value is above `i32::max_value()`.
220    "]
221    check_termination: option_u32 [update_check_termination, osqp_update_check_termination],
222
223    #[doc = "Enables warm starting the primal and dual variables from the previous solution."]
224    warm_start: bool [update_warm_start, osqp_update_warm_start],
225
226    #[doc = "Sets the solve time limit."]
227    time_limit: option_duration [update_time_limit, osqp_update_time_limit],
228}
229
230fn duration_to_secs(dur: Duration) -> float {
231    dur.as_secs() as float + dur.subsec_nanos() as float * 1e-9
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    #[cfg(target_pointer_width = "32")]
239    #[test]
240    #[should_panic]
241    fn large_u32_settings_value_panics_on_32_bit() {
242        // Larger than i32::max_value()
243        Settings::default().polish_refine_iter(3_000_000_000);
244    }
245
246    #[test]
247    fn duration_to_secs_examples() {
248        assert_eq!(duration_to_secs(Duration::new(2, 0)), 2.0);
249        assert_eq!(duration_to_secs(Duration::new(8, 100_000_000)), 8.1);
250        assert_eq!(duration_to_secs(Duration::new(0, 10_000_000)), 0.01);
251    }
252}