1use osqp_rust_sys as ffi;
2use std::mem;
3use std::ptr;
4use std::time::Duration;
5
6use crate::{float, Problem};
7
8#[derive(Clone, Debug, PartialEq)]
10pub enum LinsysSolver {
11 Qdldl,
12 MklPardiso,
13 #[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 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 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 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}