-
Notifications
You must be signed in to change notification settings - Fork 68
Expand file tree
/
Copy pathoptimizer_cinterface.rs.jinja
More file actions
246 lines (229 loc) · 9.65 KB
/
optimizer_cinterface.rs.jinja
File metadata and controls
246 lines (229 loc) · 9.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
{% if activate_clib_generation -%}
{% set error_message_capacity = 1024 -%}
// ---Export functionality from Rust to C/C++ --------------
/// Solver cache (structure `{{meta.optimizer_name}}Cache`)
///
#[allow(non_camel_case_types)]
pub struct {{meta.optimizer_name}}Cache {
cache: AlmCache,
}
const {{meta.optimizer_name|upper}}_NO_ERROR_CODE: c_int = 0;
const {{meta.optimizer_name|upper}}_SOLVER_ERROR_CODE: c_int = 2000;
const {{meta.optimizer_name|upper}}_ERROR_MESSAGE_CAPACITY: usize = {{ error_message_capacity }};
impl {{meta.optimizer_name}}Cache {
pub fn new(cache: AlmCache) -> Self {
{{meta.optimizer_name}}Cache { cache }
}
}
fn empty_error_message() -> [c_char; {{ error_message_capacity }}] {
[0 as c_char; {{ error_message_capacity }}]
}
fn error_message_to_c_array(
message: &str,
) -> [c_char; {{ error_message_capacity }}] {
let mut buffer = empty_error_message();
let max_len = {{meta.optimizer_name|upper}}_ERROR_MESSAGE_CAPACITY - 1;
for (idx, byte) in message.as_bytes().iter().copied().take(max_len).enumerate() {
buffer[idx] = byte as c_char;
}
buffer
}
/// {{meta.optimizer_name}} version of ExitStatus
/// Structure: `{{meta.optimizer_name}}ExitStatus`
#[allow(non_camel_case_types)]
#[repr(C)]
pub enum {{meta.optimizer_name}}ExitStatus {
/// The algorithm has converged
///
/// All termination criteria are satisfied and the algorithm
/// converged within the available time and number of iterations
{{meta.optimizer_name}}Converged,
/// Failed to converge because the maximum number of iterations was reached
{{meta.optimizer_name}}NotConvergedIterations,
/// Failed to converge because the maximum execution time was reached
{{meta.optimizer_name}}NotConvergedOutOfTime,
/// If the gradient or cost function cannot be evaluated internally
{{meta.optimizer_name}}NotConvergedCost,
/// Computation failed and NaN/Infinite value was obtained
{{meta.optimizer_name}}NotConvergedNotFiniteComputation,
}
/// {{meta.optimizer_name}} version of AlmOptimizerStatus
/// Structure: `{{meta.optimizer_name}}SolverStatus`
///
#[repr(C)]
pub struct {{meta.optimizer_name}}SolverStatus {
/// Exit status
exit_status: {{meta.optimizer_name}}ExitStatus,
/// Detailed error code (0 on success)
error_code: c_int,
/// Detailed error message (empty string on success)
error_message: [c_char; {{ error_message_capacity }}],
/// Number of outer iterations
num_outer_iterations: c_ulong,
/// Total number of inner iterations
///
/// This is the sum of the numbers of iterations of
/// inner solvers
num_inner_iterations: c_ulong,
/// Norm of the fixed-point residual of the the problem
last_problem_norm_fpr: c_double,
/// Total solve time
solve_time_ns: c_ulonglong,
/// Penalty value
penalty: c_double,
/// Norm of delta y divided by the penalty parameter
delta_y_norm_over_c: c_double,
/// Norm of F2(u)
f2_norm: c_double,
/// Value of cost function at solution
cost: c_double,
/// Lagrange multipliers
{%- if problem.dim_constraints_aug_lagrangian() > 0 %}
lagrange: [c_double; {{meta.optimizer_name|upper}}_N1]
{% else %}
lagrange: *const c_double
{% endif -%}
}
/// Allocate memory and setup the solver
#[no_mangle]
pub extern "C" fn {{meta.optimizer_name|lower}}_new() -> *mut {{meta.optimizer_name}}Cache {
Box::into_raw(Box::new({{meta.optimizer_name}}Cache::new(initialize_solver())))
}
/// Solve the parametric optimization problem for a given parameter
///
///
/// # Arguments:
/// - `instance`: re-useable instance of AlmCache, which should be created using
/// `{{meta.optimizer_name|lower}}_new` (and should be destroyed once it is not
/// needed using `{{meta.optimizer_name|lower}}_free`
/// - `u`: (on entry) initial guess of solution, (on exit) solution
/// (length: `{{meta.optimizer_name|upper}}_NUM_DECISION_VARIABLES`)
/// - `params`: static parameters of the optimizer
/// (length: `{{meta.optimizer_name|upper}}_NUM_PARAMETERS`)
/// - `y0`: Initial guess of Lagrange multipliers (if `0`, the default will
/// be used; length: `{{meta.optimizer_name|upper}}_N1`)
/// - `c0`: Initial penalty parameter (provide `0` to use the default initial
/// penalty parameter
///
///
/// # Returns:
/// Instance of `{{meta.optimizer_name}}SolverStatus`, with the solver status
/// (e.g., number of inner/outer iterations, measures of accuracy, solver time,
/// and the array of Lagrange multipliers at the solution).
///
///
///
/// # Safety
/// All arguments must have been properly initialised
#[no_mangle]
pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_solve(
instance: *mut {{meta.optimizer_name}}Cache,
u: *mut c_double,
params: *const c_double,
y0: *const c_double,
c0: *const c_double,
) -> {{meta.optimizer_name}}SolverStatus {
// Convert all pointers into the required data structures
let instance: &mut {{meta.optimizer_name}}Cache = {
assert!(!instance.is_null());
&mut *instance
};
// "*mut c_double" to "&mut [f64]"
let u : &mut [f64] = {
assert!(!u.is_null());
std::slice::from_raw_parts_mut(u, {{meta.optimizer_name|upper}}_NUM_DECISION_VARIABLES)
};
// "*const c_double" to "&[f64]"
let params : &[f64] = {
assert!(!params.is_null());
std::slice::from_raw_parts(params, {{meta.optimizer_name|upper}}_NUM_PARAMETERS)
};
let c0_option: Option<f64> = if c0.is_null() {
None::<f64>
} else {
Some(*c0)
};
let y0_option: Option<Vec<f64>> = if y0.is_null() {
None::<Vec<f64>>
} else {
Some(std::slice::from_raw_parts(y0 as *mut f64, {{meta.optimizer_name|upper}}_N1).to_vec())
};
// Invoke `solve`
let status = solve(params,&mut instance.cache, u, &y0_option, &c0_option);
// Check solution status and cast it as `{{meta.optimizer_name}}SolverStatus`
match status {
Ok(status) => {{meta.optimizer_name}}SolverStatus {
exit_status: match status.exit_status() {
core::ExitStatus::Converged => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}Converged,
core::ExitStatus::NotConvergedIterations => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedIterations,
core::ExitStatus::NotConvergedOutOfTime => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedOutOfTime,
},
error_code: {{meta.optimizer_name|upper}}_NO_ERROR_CODE,
error_message: empty_error_message(),
num_outer_iterations: status.num_outer_iterations() as c_ulong,
num_inner_iterations: status.num_inner_iterations() as c_ulong,
last_problem_norm_fpr: status.last_problem_norm_fpr(),
solve_time_ns: status.solve_time().as_nanos() as c_ulonglong,
penalty: status.penalty() as c_double,
delta_y_norm_over_c: status.delta_y_norm_over_c() as c_double,
f2_norm: status.f2_norm() as c_double,
cost: status.cost() as c_double,
lagrange: match status.lagrange_multipliers() {
Some({% if problem.dim_constraints_aug_lagrangian() == 0 %}_{% endif %}y) => {
{%- if problem.dim_constraints_aug_lagrangian() > 0 %}
let mut y_array : [f64; {{meta.optimizer_name|upper}}_N1] = [0.0; {{meta.optimizer_name|upper}}_N1];
y_array.copy_from_slice(y);
y_array
{% else %}
std::ptr::null::<c_double>()
{% endif %}
},
None => {
{%- if problem.dim_constraints_aug_lagrangian() > 0 %}
[0.0; {{meta.optimizer_name|upper}}_N1]
{% else %}
std::ptr::null::<c_double>()
{% endif -%}
}
}
},
Err(e) => {
let error_message = format!("problem solution failed: {}", e);
{{meta.optimizer_name}}SolverStatus {
exit_status: match e {
SolverError::Cost(_)
| SolverError::ProjectionFailed(_)
| SolverError::LinearAlgebraFailure(_)
| SolverError::InvalidProblemState(_) => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedCost,
SolverError::NotFiniteComputation(_) => {{meta.optimizer_name}}ExitStatus::{{meta.optimizer_name}}NotConvergedNotFiniteComputation,
},
error_code: {{meta.optimizer_name|upper}}_SOLVER_ERROR_CODE,
error_message: error_message_to_c_array(&error_message),
num_outer_iterations: u64::MAX as c_ulong,
num_inner_iterations: u64::MAX as c_ulong,
last_problem_norm_fpr: f64::INFINITY,
solve_time_ns: u64::MAX as c_ulonglong,
penalty: f64::INFINITY as c_double,
delta_y_norm_over_c: f64::INFINITY as c_double,
f2_norm: f64::INFINITY as c_double,
cost: f64::INFINITY as c_double,
lagrange: {%- if problem.dim_constraints_aug_lagrangian() > 0 -%}
[0.0; {{meta.optimizer_name|upper}}_N1]
{%- else -%}std::ptr::null::<c_double>(){%- endif %}
}
},
}
}
/// Deallocate the solver's memory, which has been previously allocated
/// using `{{meta.optimizer_name|lower}}_new`
///
///
/// # Safety
/// All arguments must have been properly initialised
#[no_mangle]
pub unsafe extern "C" fn {{meta.optimizer_name|lower}}_free(instance: *mut {{meta.optimizer_name}}Cache) {
// Add impl
assert!(!instance.is_null());
drop(Box::from_raw(instance));
}
{% endif %}