@@ -11,8 +11,11 @@ use {{ meta.optimizer_name }}::*;
1111#[ pymodule]
1212fn { { meta. optimizer_name } } ( _py: Python , m: & PyModule ) -> PyResult <( ) > {
1313 m. add_function ( wrap_pyfunction ! ( solver, m) ?) ?;
14- m. add_class :: < OptimizerSolution > ( ) ?;
14+ m. add_class :: < SolverStatus > ( ) ?;
15+ m. add_class :: < SolverError > ( ) ?;
16+ m. add_class :: < SolverResponse > ( ) ?;
1517 m. add_class :: < Solver > ( ) ?;
18+ m. add ( "OptimizerSolution" , m. getattr ( "SolverStatus" ) ?) ?;
1619 Ok ( ( ) )
1720}
1821
@@ -22,9 +25,53 @@ fn solver() -> PyResult<Solver> {
2225 Ok ( Solver { cache } )
2326}
2427
28+ #[ derive ( Clone ) ]
29+ struct SolverStatusData {
30+ exit_status : String ,
31+ num_outer_iterations : usize,
32+ num_inner_iterations : usize,
33+ last_problem_norm_fpr : f64,
34+ f1_infeasibility : f64,
35+ f2_norm : f64,
36+ solve_time_ms : f64,
37+ penalty : f64,
38+ solution : Vec <f64>,
39+ lagrange_multipliers : Vec <f64>,
40+ cost : f64,
41+ }
42+
43+ impl SolverStatusData {
44+ fn from_status( status: AlmOptimizerStatus , solution: & [ f64] ) -> Self {
45+ SolverStatusData {
46+ exit_status : format ! ( "{:?}" , status. exit_status( ) ) ,
47+ num_outer_iterations : status. num_outer_iterations ( ) ,
48+ num_inner_iterations : status. num_inner_iterations ( ) ,
49+ last_problem_norm_fpr : status. last_problem_norm_fpr ( ) ,
50+ f1_infeasibility : status. delta_y_norm_over_c ( ) ,
51+ f2_norm : status. f2_norm ( ) ,
52+ penalty : status. penalty ( ) ,
53+ lagrange_multipliers : status. lagrange_multipliers ( ) . clone ( ) . unwrap_or_default ( ) ,
54+ solve_time_ms : ( status. solve_time ( ) . as_nanos ( ) as f64 ) / 1e6 ,
55+ solution : solution. to_vec ( ) ,
56+ cost : status. cost ( ) ,
57+ }
58+ }
59+ }
60+
61+ #[ derive ( Clone ) ]
62+ struct SolverErrorData {
63+ code : i32 ,
64+ message : String ,
65+ }
66+
67+ enum SolverResponsePayload {
68+ Ok ( SolverStatusData ) ,
69+ Err ( SolverErrorData ) ,
70+ }
71+
2572/// Solution and solution status of optimizer
2673#[ pyclass]
27- struct OptimizerSolution {
74+ struct SolverStatus {
2875 #[ pyo3( get) ]
2976 exit_status : String ,
3077 #[ pyo3( get) ]
@@ -49,6 +96,64 @@ struct OptimizerSolution {
4996 cost : f64 ,
5097}
5198
99+ impl From < SolverStatusData > for SolverStatus {
100+ fn from ( status : SolverStatusData ) -> Self {
101+ SolverStatus {
102+ exit_status : status. exit_status ,
103+ num_outer_iterations : status. num_outer_iterations ,
104+ num_inner_iterations : status. num_inner_iterations ,
105+ last_problem_norm_fpr : status. last_problem_norm_fpr ,
106+ f1_infeasibility : status. f1_infeasibility ,
107+ f2_norm : status. f2_norm ,
108+ solve_time_ms : status. solve_time_ms ,
109+ penalty : status. penalty ,
110+ solution : status. solution ,
111+ lagrange_multipliers : status. lagrange_multipliers ,
112+ cost : status. cost ,
113+ }
114+ }
115+ }
116+
117+ #[ pyclass]
118+ struct SolverError {
119+ #[ pyo3( get) ]
120+ code : i32 ,
121+ #[ pyo3( get) ]
122+ message : String ,
123+ }
124+
125+ impl From < SolverErrorData > for SolverError {
126+ fn from ( error : SolverErrorData ) -> Self {
127+ SolverError {
128+ code : error. code ,
129+ message : error. message ,
130+ }
131+ }
132+ }
133+
134+ #[ pyclass]
135+ struct SolverResponse {
136+ payload : SolverResponsePayload ,
137+ }
138+
139+ #[ pymethods]
140+ impl SolverResponse {
141+ fn is_ok ( & self ) -> bool {
142+ matches ! ( self . payload, SolverResponsePayload :: Ok ( _) )
143+ }
144+
145+ fn get ( & self , py : Python < ' _ > ) -> PyResult < PyObject > {
146+ match & self . payload {
147+ SolverResponsePayload :: Ok ( status) => {
148+ Ok ( Py :: new ( py, SolverStatus :: from ( status. clone ( ) ) ) ?. into_py ( py) )
149+ }
150+ SolverResponsePayload :: Err ( error) => {
151+ Ok ( Py :: new ( py, SolverError :: from ( error. clone ( ) ) ) ?. into_py ( py) )
152+ }
153+ }
154+ }
155+ }
156+
52157#[ pyclass]
53158struct Solver {
54159 cache : AlmCache ,
@@ -65,20 +170,24 @@ impl Solver {
65170 initial_guess : Option < Vec < f64 > > ,
66171 initial_lagrange_multipliers : Option < Vec < f64 > > ,
67172 initial_penalty : Option < f64 > ,
68- ) -> PyResult <Option < OptimizerSolution > > {
173+ ) -> PyResult < SolverResponse > {
69174 let mut u = [ 0.0 ; { { meta. optimizer_name |upper} } _NUM_DECISION_VARIABLES] ;
70175
71176 // ----------------------------------------------------
72177 // Set initial value
73178 // ----------------------------------------------------
74179 if let Some ( u0) = initial_guess {
75180 if u0. len ( ) != { { meta. optimizer_name |upper} } _NUM_DECISION_VARIABLES {
76- println ! (
77- "1600 -> Initial guess has incompatible dimensions: {} != {}" ,
78- u0. len( ) ,
79- { { meta. optimizer_name|upper} } _NUM_DECISION_VARIABLES
80- ) ;
81- return Ok ( None ) ;
181+ return Ok ( SolverResponse {
182+ payload : SolverResponsePayload :: Err ( SolverErrorData {
183+ code : 1600 ,
184+ message : format ! (
185+ "initial guess has incompatible dimensions: provided {}, expected {}" ,
186+ u0. len( ) ,
187+ { { meta. optimizer_name|upper} } _NUM_DECISION_VARIABLES
188+ ) ,
189+ } ) ,
190+ } ) ;
82191 }
83192 u. copy_from_slice ( & u0) ;
84193 }
@@ -88,25 +197,33 @@ impl Solver {
88197 // ----------------------------------------------------
89198 if let Some ( y0) = & initial_lagrange_multipliers {
90199 if y0. len ( ) != { { meta. optimizer_name |upper} } _N1 {
91- println ! (
92- "1700 -> wrong dimension of Langrange multipliers: {} != {}" ,
93- y0. len( ) ,
94- { { meta. optimizer_name|upper} } _N1
95- ) ;
96- return Ok ( None ) ;
200+ return Ok ( SolverResponse {
201+ payload : SolverResponsePayload :: Err ( SolverErrorData {
202+ code : 1700 ,
203+ message : format ! (
204+ "wrong dimension of Langrange multipliers: provided {}, expected {}" ,
205+ y0. len( ) ,
206+ { { meta. optimizer_name|upper} } _N1
207+ ) ,
208+ } ) ,
209+ } ) ;
97210 }
98211 }
99212
100213 // ----------------------------------------------------
101214 // Check parameter
102215 // ----------------------------------------------------
103216 if p. len ( ) != { { meta. optimizer_name |upper} } _NUM_PARAMETERS {
104- println ! (
105- "3003 -> wrong number of parameters: {} != {}" ,
106- p. len( ) ,
107- { { meta. optimizer_name|upper} } _NUM_PARAMETERS
108- ) ;
109- return Ok ( None ) ;
217+ return Ok ( SolverResponse {
218+ payload : SolverResponsePayload :: Err ( SolverErrorData {
219+ code : 3003 ,
220+ message : format ! (
221+ "wrong number of parameters: provided {}, expected {}" ,
222+ p. len( ) ,
223+ { { meta. optimizer_name|upper} } _NUM_PARAMETERS
224+ ) ,
225+ } ) ,
226+ } ) ;
110227 }
111228
112229 // ----------------------------------------------------
@@ -121,23 +238,15 @@ impl Solver {
121238 ) ;
122239
123240 match solver_status {
124- Ok ( status) => Ok ( Some ( OptimizerSolution {
125- exit_status : format ! ( "{:?}" , status. exit_status( ) ) ,
126- num_outer_iterations : status. num_outer_iterations ( ) ,
127- num_inner_iterations : status. num_inner_iterations ( ) ,
128- last_problem_norm_fpr : status. last_problem_norm_fpr ( ) ,
129- f1_infeasibility : status. delta_y_norm_over_c ( ) ,
130- f2_norm : status. f2_norm ( ) ,
131- penalty : status. penalty ( ) ,
132- lagrange_multipliers : status. lagrange_multipliers ( ) . clone ( ) . unwrap_or_default ( ) ,
133- solve_time_ms : ( status. solve_time ( ) . as_nanos ( ) as f64 ) / 1e6 ,
134- solution : u. to_vec ( ) ,
135- cost : status. cost ( ) ,
136- } ) ) ,
137- Err ( _) => {
138- println ! ( "2000 -> Problem solution failed (solver error)" ) ;
139- Ok ( None )
140- }
241+ Ok ( status) => Ok ( SolverResponse {
242+ payload : SolverResponsePayload :: Ok ( SolverStatusData :: from_status ( status, & u) ) ,
243+ } ) ,
244+ Err ( err) => Ok ( SolverResponse {
245+ payload : SolverResponsePayload :: Err ( SolverErrorData {
246+ code : 2000 ,
247+ message : format ! ( "problem solution failed: {}" , err) ,
248+ } ) ,
249+ } ) ,
141250 }
142251 }
143252}
0 commit comments