1use osqp_rust_sys as ffi;
2use std::fmt;
3use std::slice;
4use std::time::Duration;
5
6use crate::{float, Problem};
7
8#[derive(Clone, Debug)]
10pub enum Status<'a> {
11 Solved(Solution<'a>),
12 SolvedInaccurate(Solution<'a>),
13 MaxIterationsReached(Solution<'a>),
14 TimeLimitReached(Solution<'a>),
15 PrimalInfeasible(PrimalInfeasibilityCertificate<'a>),
16 PrimalInfeasibleInaccurate(PrimalInfeasibilityCertificate<'a>),
17 DualInfeasible(DualInfeasibilityCertificate<'a>),
18 DualInfeasibleInaccurate(DualInfeasibilityCertificate<'a>),
19 NonConvex(Failure<'a>),
20 #[doc(hidden)]
22 __Nonexhaustive,
23}
24
25#[derive(Clone)]
27pub struct Solution<'a> {
28 prob: &'a Problem,
29}
30
31#[derive(Clone)]
33pub struct PrimalInfeasibilityCertificate<'a> {
34 prob: &'a Problem,
35}
36
37#[derive(Clone)]
39pub struct DualInfeasibilityCertificate<'a> {
40 prob: &'a Problem,
41}
42
43#[derive(Clone)]
45pub struct Failure<'a> {
46 prob: &'a Problem,
47}
48
49#[derive(Copy, Clone, Debug, Hash, PartialEq)]
51pub enum PolishStatus {
52 Successful,
53 Unsuccessful,
54 Unperformed,
55 #[doc(hidden)]
57 __Nonexhaustive,
58}
59
60impl<'a> Status<'a> {
61 pub(crate) fn from_problem(prob: &'a Problem) -> Status<'a> {
62 use std::os::raw::c_int;
63 unsafe {
64 match (*(*prob.workspace).info).status_val as c_int {
65 ffi::src::src::auxil::OSQP_SOLVED => Status::Solved(Solution { prob }),
66 ffi::src::src::auxil::OSQP_SOLVED_INACCURATE => Status::SolvedInaccurate(Solution { prob }),
67 ffi::src::src::auxil::OSQP_MAX_ITER_REACHED => Status::MaxIterationsReached(Solution { prob }),
68 ffi::src::src::auxil::OSQP_TIME_LIMIT_REACHED => Status::TimeLimitReached(Solution { prob }),
69 ffi::src::src::auxil::OSQP_PRIMAL_INFEASIBLE => {
70 Status::PrimalInfeasible(PrimalInfeasibilityCertificate { prob })
71 }
72 ffi::src::src::auxil::OSQP_PRIMAL_INFEASIBLE_INACCURATE => {
73 Status::PrimalInfeasibleInaccurate(PrimalInfeasibilityCertificate { prob })
74 }
75 ffi::src::src::auxil::OSQP_DUAL_INFEASIBLE => {
76 Status::DualInfeasible(DualInfeasibilityCertificate { prob })
77 }
78 ffi::src::src::auxil::OSQP_DUAL_INFEASIBLE_INACCURATE => {
79 Status::DualInfeasibleInaccurate(DualInfeasibilityCertificate { prob })
80 }
81 ffi::src::src::auxil::OSQP_NON_CVX => Status::NonConvex(Failure { prob }),
82 _ => unreachable!(),
83 }
84 }
85 }
86
87 pub fn x(&self) -> Option<&'a [float]> {
89 self.solution().map(|s| s.x())
90 }
91
92 pub fn solution(&self) -> Option<Solution<'a>> {
94 match *self {
95 Status::Solved(ref solution) => Some(solution.clone()),
96 _ => None,
97 }
98 }
99
100 pub fn iter(&self) -> u32 {
102 unsafe {
103 (*(*self.prob().workspace).info).iter as u32
105 }
106 }
107
108 pub fn setup_time(&self) -> Duration {
110 unsafe { secs_to_duration((*(*self.prob().workspace).info).setup_time) }
111 }
112
113 pub fn solve_time(&self) -> Duration {
115 unsafe { secs_to_duration((*(*self.prob().workspace).info).solve_time) }
116 }
117
118 pub fn polish_time(&self) -> Duration {
120 unsafe { secs_to_duration((*(*self.prob().workspace).info).polish_time) }
121 }
122
123 pub fn run_time(&self) -> Duration {
127 unsafe { secs_to_duration((*(*self.prob().workspace).info).run_time) }
128 }
129
130 pub fn rho_updates(&self) -> u32 {
132 unsafe {
133 (*(*self.prob().workspace).info).rho_updates as u32
135 }
136 }
137
138 pub fn rho_estimate(&self) -> float {
140 unsafe { (*(*self.prob().workspace).info).rho_estimate }
141 }
142
143 fn prob(&self) -> &'a Problem {
144 match *self {
145 Status::Solved(ref solution)
146 | Status::SolvedInaccurate(ref solution)
147 | Status::MaxIterationsReached(ref solution)
148 | Status::TimeLimitReached(ref solution) => solution.prob,
149 Status::PrimalInfeasible(ref cert) | Status::PrimalInfeasibleInaccurate(ref cert) => {
150 cert.prob
151 }
152 Status::DualInfeasible(ref cert) | Status::DualInfeasibleInaccurate(ref cert) => {
153 cert.prob
154 }
155 Status::NonConvex(ref failure) => failure.prob,
156 Status::__Nonexhaustive => unreachable!(),
157 }
158 }
159}
160
161impl<'a> Solution<'a> {
162 pub fn x(&self) -> &'a [float] {
164 unsafe { slice::from_raw_parts((*(*self.prob.workspace).solution).x, self.prob.n) }
165 }
166
167 pub fn y(&self) -> &'a [float] {
171 unsafe { slice::from_raw_parts((*(*self.prob.workspace).solution).y, self.prob.m) }
172 }
173
174 pub fn polish_status(&self) -> PolishStatus {
176 unsafe {
177 match (*(*self.prob.workspace).info).status_polish {
178 1 => PolishStatus::Successful,
179 -1 => PolishStatus::Unsuccessful,
180 0 => PolishStatus::Unperformed,
181 _ => unreachable!(),
182 }
183 }
184 }
185
186 pub fn obj_val(&self) -> float {
188 unsafe { (*(*self.prob.workspace).info).obj_val }
189 }
190
191 pub fn pri_res(&self) -> float {
193 unsafe { (*(*self.prob.workspace).info).pri_res }
194 }
195
196 pub fn dua_res(&self) -> float {
198 unsafe { (*(*self.prob.workspace).info).dua_res }
199 }
200}
201
202impl<'a> fmt::Debug for Solution<'a> {
203 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
204 fmt.debug_struct("Solution")
205 .field("x", &self.x())
206 .field("y", &self.y())
207 .field("polish_status", &self.polish_status())
208 .field("obj_val", &self.obj_val())
209 .field("pri_res", &self.pri_res())
210 .field("dua_res", &self.dua_res())
211 .finish()
212 }
213}
214
215impl<'a> PrimalInfeasibilityCertificate<'a> {
216 pub fn delta_y(&self) -> &'a [float] {
222 unsafe { slice::from_raw_parts((*self.prob.workspace).delta_y, self.prob.m) }
223 }
224}
225
226impl<'a> fmt::Debug for PrimalInfeasibilityCertificate<'a> {
227 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
228 fmt.debug_struct("PrimalInfeasibilityCertificate")
229 .field("delta_y", &self.delta_y())
230 .finish()
231 }
232}
233
234impl<'a> DualInfeasibilityCertificate<'a> {
235 pub fn delta_x(&self) -> &'a [float] {
241 unsafe { slice::from_raw_parts((*self.prob.workspace).delta_x, self.prob.n) }
242 }
243}
244
245impl<'a> fmt::Debug for DualInfeasibilityCertificate<'a> {
246 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
247 fmt.debug_struct("DualInfeasibilityCertificate")
248 .field("delta_x", &self.delta_x())
249 .finish()
250 }
251}
252
253impl<'a> fmt::Debug for Failure<'a> {
254 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
255 fmt.debug_struct("Failure").finish()
256 }
257}
258
259fn secs_to_duration(secs: float) -> Duration {
260 let whole_secs = secs.floor() as u64;
261 let nanos = (secs.fract() * 1e9) as u32;
262 Duration::new(whole_secs, nanos)
263}