use std::panic::{AssertUnwindSafe, catch_unwind};
use clarabel::solver::{DefaultSolution, LinearSolverInfo};
use crate::copp::copp3::Topp3Profile;
use crate::copp::copp3::opt3::copp3_socp::{
copp3_socp as rust_copp3_socp, copp3_socp_expert_with_info as rust_copp3_socp_expert,
objective_value_copp3_opt,
};
use crate::ffi::c::core::status::{clear_last_error, panic_to_status};
use crate::ffi::c::{
Copp3Problem, CoppClarabelLinearSolverInfo, CoppClarabelOptions, CoppClarabelSolverStatus,
CoppProfile3rd, CoppStatus, CoppVecF64,
};
#[repr(C)]
#[derive(Debug)]
pub struct Copp3SocpResult {
pub has_profile: bool,
pub profile: CoppProfile3rd,
pub x: CoppVecF64,
pub z: CoppVecF64,
pub s: CoppVecF64,
pub solver_status: CoppClarabelSolverStatus,
pub obj_val: f64,
pub obj_val_dual: f64,
pub solve_time: f64,
pub iterations: u32,
pub r_prim: f64,
pub r_dual: f64,
pub linsolver: CoppClarabelLinearSolverInfo,
pub objective_value: f64,
pub objective_terms: CoppVecF64,
}
impl Copp3SocpResult {
pub(crate) fn empty() -> Self {
Self {
has_profile: false,
profile: CoppProfile3rd::empty(),
x: CoppVecF64::empty(),
z: CoppVecF64::empty(),
s: CoppVecF64::empty(),
solver_status: CoppClarabelSolverStatus::Unsolved,
obj_val: f64::NAN,
obj_val_dual: f64::NAN,
solve_time: 0.0,
iterations: 0,
r_prim: f64::NAN,
r_dual: f64::NAN,
linsolver: CoppClarabelLinearSolverInfo::empty(),
objective_value: f64::NAN,
objective_terms: CoppVecF64::empty(),
}
}
pub(crate) fn from_solution(
profile: Option<Topp3Profile>,
solution: DefaultSolution<f64>,
linsolver: LinearSolverInfo,
objective_breakdown: Option<(f64, Vec<f64>)>,
) -> Self {
let DefaultSolution {
x,
z,
s,
status,
obj_val,
obj_val_dual,
solve_time,
iterations,
r_prim,
r_dual,
} = solution;
let (objective_value, objective_terms) =
objective_breakdown.unwrap_or((f64::NAN, Vec::new()));
Self {
has_profile: profile.is_some(),
profile: profile.map_or_else(CoppProfile3rd::empty, |profile| {
CoppProfile3rd::from_parts(profile.a, profile.b, profile.num_stationary)
}),
x: CoppVecF64::from_vec(x),
z: CoppVecF64::from_vec(z),
s: CoppVecF64::from_vec(s),
solver_status: status.into(),
obj_val,
obj_val_dual,
solve_time,
iterations,
r_prim,
r_dual,
linsolver: linsolver.into(),
objective_value,
objective_terms: CoppVecF64::from_vec(objective_terms),
}
}
fn free(self) {
self.profile.free();
self.x.free();
self.z.free();
self.s.free();
self.objective_terms.free();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copp3_socp(
problem: Copp3Problem,
options: CoppClarabelOptions,
out_profile: *mut CoppProfile3rd,
) -> CoppStatus {
crate::ffi::c::core::status::clear_last_error();
if out_profile.is_null() {
return CoppStatus::NullPointer.into_ffi_status();
}
unsafe {
out_profile.write(CoppProfile3rd::empty());
}
match catch_unwind(AssertUnwindSafe(|| {
let options = options.build();
let profile = unsafe {
problem.with_rust_problem(true, |problem| {
rust_copp3_socp(&problem, &options).map_err(|error| CoppStatus::from(&error))
})?
};
unsafe {
out_profile.write(CoppProfile3rd::from_parts(
profile.a,
profile.b,
profile.num_stationary,
));
}
Ok(CoppStatus::Ok)
})) {
Ok(Ok(status)) | Ok(Err(status)) => status.into_ffi_status(),
Err(payload) => panic_to_status(payload).into_ffi_status(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copp3_socp_expert(
problem: Copp3Problem,
options: CoppClarabelOptions,
out_result: *mut Copp3SocpResult,
) -> CoppStatus {
crate::ffi::c::core::status::clear_last_error();
if out_result.is_null() {
return CoppStatus::NullPointer.into_ffi_status();
}
unsafe {
out_result.write(Copp3SocpResult::empty());
}
match catch_unwind(AssertUnwindSafe(|| {
let options = options.build();
let result = unsafe {
problem.with_rust_problem(true, |problem| {
let expert = rust_copp3_socp_expert(&problem, &options)
.map_err(|error| CoppStatus::from(&error))?;
let objective_breakdown = expert
.result
.as_ref()
.map(|profile| objective_value_copp3_opt(&problem, profile.as_parts()));
Ok(Copp3SocpResult::from_solution(
expert.result,
expert.solution,
expert.linsolver,
objective_breakdown,
))
})?
};
unsafe {
out_result.write(result);
}
Ok(CoppStatus::Ok)
})) {
Ok(Ok(status)) | Ok(Err(status)) => status.into_ffi_status(),
Err(payload) => panic_to_status(payload).into_ffi_status(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copp3_socp_result_free(result: Copp3SocpResult) {
result.free();
clear_last_error();
}