#![allow(non_snake_case)]
use super::*;
use crate::{
algebra::CscMatrix,
io::*,
solver::{
core::{
callbacks::ClarabelCallbackFn,
kktsolvers::LinearSolverInfo,
traits::{InfoPrint, Settings},
IPSolver, SettingsError, SolverStatus,
},
implementations::default::*,
SolverJSONReadWrite,
},
};
use derive_more::with_trait::Debug;
use pyo3::{exceptions::PyException, prelude::*, types::PyDict};
use std::fmt::Write;
#[derive(Clone)]
#[pyclass(name = "LinearSolverInfo")]
pub struct PyLinearSolverInfo {
#[pyo3(get)]
pub name: String,
#[pyo3(get)]
pub threads: usize,
#[pyo3(get)]
pub direct: bool,
#[pyo3(get)]
pub nnzA: usize,
#[pyo3(get)]
pub nnzL: usize,
}
impl From<&LinearSolverInfo> for PyLinearSolverInfo {
fn from(info: &LinearSolverInfo) -> Self {
Self {
name: info.name.clone(),
threads: info.threads,
direct: info.direct,
nnzA: info.nnzA,
nnzL: info.nnzL,
}
}
}
impl Debug for PyLinearSolverInfo {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("LinearSolverInfo")
.field("name", &self.name)
.field("threads", &self.threads)
.field("direct", &self.direct)
.field("nnzA", &self.nnzA)
.field("nnzL", &self.nnzL)
.finish()
}
}
#[pymethods]
impl PyLinearSolverInfo {
pub fn __repr__(&self) -> String {
let mut s = String::new();
write!(s, "{:#?}", self).unwrap();
s
}
}
#[derive(Debug, Clone)]
#[pyclass(name = "DefaultInfo")]
pub struct PyDefaultInfo {
#[pyo3(get)]
pub mu: f64,
#[pyo3(get)]
pub sigma: f64,
#[pyo3(get)]
pub step_length: f64,
#[pyo3(get)]
pub iterations: u32,
#[pyo3(get)]
pub cost_primal: f64,
#[pyo3(get)]
pub cost_dual: f64,
#[pyo3(get)]
pub res_primal: f64,
#[pyo3(get)]
pub res_dual: f64,
#[pyo3(get)]
pub res_primal_inf: f64,
#[pyo3(get)]
pub res_dual_inf: f64,
#[pyo3(get)]
pub gap_abs: f64,
#[pyo3(get)]
pub gap_rel: f64,
#[pyo3(get)]
pub ktratio: f64,
#[pyo3(get)]
pub solve_time: f64,
#[pyo3(get)]
pub status: PySolverStatus,
#[pyo3(get)]
pub linsolver: PyLinearSolverInfo,
}
impl From<&DefaultInfo<f64>> for PyDefaultInfo {
fn from(info: &DefaultInfo<f64>) -> Self {
let status = (&info.status).into();
let linsolver = (&info.linsolver).into();
Self {
mu: info.mu,
sigma: info.sigma,
step_length: info.step_length,
iterations: info.iterations,
cost_primal: info.cost_primal,
cost_dual: info.cost_dual,
res_primal: info.res_primal,
res_dual: info.res_dual,
res_primal_inf: info.res_primal_inf,
res_dual_inf: info.res_dual_inf,
gap_abs: info.gap_abs,
gap_rel: info.gap_rel,
ktratio: info.ktratio,
solve_time: info.solve_time,
status,
linsolver,
}
}
}
#[pymethods]
impl PyDefaultInfo {
pub fn __repr__(&self) -> String {
let mut s = String::new();
write!(s, "{:#?}", self).unwrap();
s.replacen("PyDefaultInfo", "DefaultInfo", 1)
}
}
#[pyclass(name = "DefaultSolution")]
pub struct PyDefaultSolution {
#[pyo3(get)]
pub x: Vec<f64>,
#[pyo3(get)]
pub s: Vec<f64>,
#[pyo3(get)]
pub z: Vec<f64>,
#[pyo3(get)]
pub status: PySolverStatus,
#[pyo3(get)]
pub obj_val: f64,
#[pyo3(get)]
pub obj_val_dual: f64,
#[pyo3(get)]
pub solve_time: f64,
#[pyo3(get)]
pub iterations: u32,
#[pyo3(get)]
pub r_prim: f64,
#[pyo3(get)]
pub r_dual: f64,
}
impl From<&DefaultSolution<f64>> for PyDefaultSolution {
fn from(result: &DefaultSolution<f64>) -> Self {
let x = result.x.clone();
let s = result.s.clone();
let z = result.z.clone();
let status = (&result.status).into();
Self {
x,
s,
z,
obj_val: result.obj_val,
obj_val_dual: result.obj_val_dual,
status,
solve_time: result.solve_time,
iterations: result.iterations,
r_prim: result.r_prim,
r_dual: result.r_dual,
}
}
}
struct TruncatedSlice<'a, T>(&'a [T]);
impl<T> Debug for TruncatedSlice<'_, T>
where
T: std::fmt::Debug,
{
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let MAX = 5;
if self.0.len() > MAX {
let truncated: Vec<String> = self.0[..MAX - 2]
.iter()
.map(|v| format!("{:?}", v))
.collect();
write!(
fmt,
"[{} ... ({} more)]",
truncated.join(", "),
self.0.len() - truncated.len()
)
} else {
write!(fmt, "{:?}", self.0)
}
}
}
impl Debug for PyDefaultSolution {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("PyDefaultSolution")
.field("x", &TruncatedSlice(&self.x))
.field("s", &TruncatedSlice(&self.s))
.field("z", &TruncatedSlice(&self.z))
.field("status", &self.status)
.field("obj_val", &self.obj_val)
.field("obj_val_dual", &self.obj_val_dual)
.field("solve_time", &self.solve_time)
.field("iterations", &self.iterations)
.field("r_prim", &self.r_prim)
.field("r_dual", &self.r_dual)
.finish()
}
}
#[pymethods]
impl PyDefaultSolution {
pub fn __repr__(&self) -> String {
let mut s = String::new();
write!(s, "{:#?}", self).unwrap();
s.replacen("PyDefaultSolution", "DefaultSolution", 1)
}
}
impl From<SolverError> for PyErr {
fn from(err: SolverError) -> Self {
PyException::new_err(err.to_string())
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
#[pyclass(eq, eq_int, name = "SolverStatus")]
pub enum PySolverStatus {
Unsolved = 0,
Solved,
PrimalInfeasible,
DualInfeasible,
AlmostSolved,
AlmostPrimalInfeasible,
AlmostDualInfeasible,
MaxIterations,
MaxTime,
NumericalError,
InsufficientProgress,
CallbackTerminated,
}
impl From<&SolverStatus> for PySolverStatus {
fn from(status: &SolverStatus) -> Self {
match status {
SolverStatus::Unsolved => PySolverStatus::Unsolved,
SolverStatus::Solved => PySolverStatus::Solved,
SolverStatus::PrimalInfeasible => PySolverStatus::PrimalInfeasible,
SolverStatus::DualInfeasible => PySolverStatus::DualInfeasible,
SolverStatus::AlmostSolved => PySolverStatus::AlmostSolved,
SolverStatus::AlmostPrimalInfeasible => PySolverStatus::AlmostPrimalInfeasible,
SolverStatus::AlmostDualInfeasible => PySolverStatus::AlmostDualInfeasible,
SolverStatus::MaxIterations => PySolverStatus::MaxIterations,
SolverStatus::MaxTime => PySolverStatus::MaxTime,
SolverStatus::NumericalError => PySolverStatus::NumericalError,
SolverStatus::InsufficientProgress => PySolverStatus::InsufficientProgress,
SolverStatus::CallbackTerminated => PySolverStatus::CallbackTerminated,
}
}
}
#[pymethods]
impl PySolverStatus {
pub fn __repr__(&self) -> String {
match self {
PySolverStatus::Unsolved => "Unsolved",
PySolverStatus::Solved => "Solved",
PySolverStatus::PrimalInfeasible => "PrimalInfeasible",
PySolverStatus::DualInfeasible => "DualInfeasible",
PySolverStatus::AlmostSolved => "AlmostSolved",
PySolverStatus::AlmostPrimalInfeasible => "AlmostPrimalInfeasible",
PySolverStatus::AlmostDualInfeasible => "AlmostDualInfeasible",
PySolverStatus::MaxIterations => "MaxIterations",
PySolverStatus::MaxTime => "MaxTime",
PySolverStatus::NumericalError => "NumericalError",
PySolverStatus::InsufficientProgress => "InsufficientProgress",
PySolverStatus::CallbackTerminated => "CallbackTerminated",
}
.to_string()
}
pub fn __hash__(&self) -> u32 {
*self as u32
}
}
impl From<SettingsError> for PyErr {
fn from(err: SettingsError) -> Self {
PyException::new_err(err.to_string())
}
}
#[derive(Debug, Clone)]
#[pyclass(name = "DefaultSettings")]
pub struct PyDefaultSettings {
#[pyo3(get, set)]
pub max_iter: u32,
#[pyo3(get, set)]
pub time_limit: f64,
#[pyo3(get, set)]
pub verbose: bool,
#[pyo3(get, set)]
pub max_step_fraction: f64,
#[pyo3(get, set)]
pub tol_gap_abs: f64,
#[pyo3(get, set)]
pub tol_gap_rel: f64,
#[pyo3(get, set)]
pub tol_feas: f64,
#[pyo3(get, set)]
pub tol_infeas_abs: f64,
#[pyo3(get, set)]
pub tol_infeas_rel: f64,
#[pyo3(get, set)]
pub tol_ktratio: f64,
#[pyo3(get, set)]
pub reduced_tol_gap_abs: f64,
#[pyo3(get, set)]
pub reduced_tol_gap_rel: f64,
#[pyo3(get, set)]
pub reduced_tol_feas: f64,
#[pyo3(get, set)]
pub reduced_tol_infeas_abs: f64,
#[pyo3(get, set)]
pub reduced_tol_infeas_rel: f64,
#[pyo3(get, set)]
pub reduced_tol_ktratio: f64,
#[pyo3(get, set)]
pub equilibrate_enable: bool,
#[pyo3(get, set)]
pub equilibrate_max_iter: u32,
#[pyo3(get, set)]
pub equilibrate_min_scaling: f64,
#[pyo3(get, set)]
pub equilibrate_max_scaling: f64,
#[pyo3(get, set)]
pub linesearch_backtrack_step: f64,
#[pyo3(get, set)]
pub min_switch_step_length: f64,
#[pyo3(get, set)]
pub min_terminate_step_length: f64,
#[pyo3(get, set)]
pub max_threads: u32,
#[pyo3(get, set)]
pub direct_kkt_solver: bool,
#[pyo3(get, set)]
pub direct_solve_method: String,
#[pyo3(get, set)]
pub static_regularization_enable: bool,
#[pyo3(get, set)]
pub static_regularization_constant: f64,
#[pyo3(get, set)]
pub static_regularization_proportional: f64,
#[pyo3(get, set)]
pub dynamic_regularization_enable: bool,
#[pyo3(get, set)]
pub dynamic_regularization_eps: f64,
#[pyo3(get, set)]
pub dynamic_regularization_delta: f64,
#[pyo3(get, set)]
pub iterative_refinement_enable: bool,
#[pyo3(get, set)]
pub iterative_refinement_reltol: f64,
#[pyo3(get, set)]
pub iterative_refinement_abstol: f64,
#[pyo3(get, set)]
pub iterative_refinement_max_iter: u32,
#[pyo3(get, set)]
pub iterative_refinement_stop_ratio: f64,
#[pyo3(get, set)]
pub presolve_enable: bool,
#[pyo3(get, set)]
pub input_sparse_dropzeros: bool,
#[pyo3(get, set)]
pub chordal_decomposition_enable: bool,
#[pyo3(get, set)]
pub chordal_decomposition_merge_method: String,
#[pyo3(get, set)]
pub chordal_decomposition_compact: bool,
#[pyo3(get, set)]
pub chordal_decomposition_complete_dual: bool,
#[cfg(any(feature = "pardiso-mkl", feature = "pardiso-panua"))]
#[debug("iparm array [i32; 64]")]
#[pyo3(get, set)]
pub pardiso_iparm: [i32; 64],
#[cfg(any(feature = "pardiso-mkl", feature = "pardiso-panua"))]
#[pyo3(get, set)]
pub pardiso_verbose: bool,
}
#[pymethods]
impl PyDefaultSettings {
#[new]
pub fn new() -> Self {
(&DefaultSettings::<f64>::default()).into()
}
#[staticmethod]
#[pyo3(name = "default")]
pub fn py_default() -> Self {
PyDefaultSettings::default()
}
pub fn __repr__(&self) -> String {
let mut s = String::new();
write!(s, "{:#?}", self).unwrap();
s.replacen("PyDefaultSettings", "DefaultSettings", 1)
}
}
impl Default for PyDefaultSettings {
fn default() -> Self {
PyDefaultSettings::new()
}
}
impl From<&DefaultSettings<f64>> for PyDefaultSettings {
fn from(set: &DefaultSettings<f64>) -> Self {
PyDefaultSettings {
max_iter: set.max_iter,
time_limit: set.time_limit,
verbose: set.verbose,
tol_gap_abs: set.tol_gap_abs,
tol_gap_rel: set.tol_gap_rel,
tol_feas: set.tol_feas,
tol_infeas_abs: set.tol_infeas_abs,
tol_infeas_rel: set.tol_infeas_rel,
tol_ktratio: set.tol_ktratio,
reduced_tol_gap_abs: set.reduced_tol_gap_abs,
reduced_tol_gap_rel: set.reduced_tol_gap_rel,
reduced_tol_feas: set.reduced_tol_feas,
reduced_tol_infeas_abs: set.reduced_tol_infeas_abs,
reduced_tol_infeas_rel: set.reduced_tol_infeas_rel,
reduced_tol_ktratio: set.reduced_tol_ktratio,
max_step_fraction: set.max_step_fraction,
equilibrate_enable: set.equilibrate_enable,
equilibrate_max_iter: set.equilibrate_max_iter,
equilibrate_min_scaling: set.equilibrate_min_scaling,
equilibrate_max_scaling: set.equilibrate_max_scaling,
linesearch_backtrack_step: set.linesearch_backtrack_step,
min_switch_step_length: set.min_switch_step_length,
min_terminate_step_length: set.min_terminate_step_length,
max_threads: set.max_threads,
direct_kkt_solver: set.direct_kkt_solver,
direct_solve_method: set.direct_solve_method.clone(),
static_regularization_enable: set.static_regularization_enable,
static_regularization_constant: set.static_regularization_constant,
static_regularization_proportional: set.static_regularization_proportional,
dynamic_regularization_enable: set.dynamic_regularization_enable,
dynamic_regularization_eps: set.dynamic_regularization_eps,
dynamic_regularization_delta: set.dynamic_regularization_delta,
iterative_refinement_enable: set.iterative_refinement_enable,
iterative_refinement_reltol: set.iterative_refinement_reltol,
iterative_refinement_abstol: set.iterative_refinement_abstol,
iterative_refinement_max_iter: set.iterative_refinement_max_iter,
iterative_refinement_stop_ratio: set.iterative_refinement_stop_ratio,
presolve_enable: set.presolve_enable,
input_sparse_dropzeros: set.input_sparse_dropzeros,
#[cfg(feature = "sdp")]
chordal_decomposition_enable: set.chordal_decomposition_enable,
#[cfg(feature = "sdp")]
chordal_decomposition_merge_method: set.chordal_decomposition_merge_method.clone(),
#[cfg(feature = "sdp")]
chordal_decomposition_compact: set.chordal_decomposition_compact,
#[cfg(feature = "sdp")]
chordal_decomposition_complete_dual: set.chordal_decomposition_complete_dual,
#[cfg(any(feature = "pardiso-mkl", feature = "pardiso-panua"))]
pardiso_iparm: set.pardiso_iparm,
#[cfg(any(feature = "pardiso-mkl", feature = "pardiso-panua"))]
pardiso_verbose: set.pardiso_verbose,
}
}
}
impl PyDefaultSettings {
pub(crate) fn to_internal(&self) -> Result<DefaultSettings<f64>, PyErr> {
let settings = DefaultSettings::<f64> {
max_iter: self.max_iter,
time_limit: self.time_limit,
verbose: self.verbose,
tol_gap_abs: self.tol_gap_abs,
tol_gap_rel: self.tol_gap_rel,
tol_feas: self.tol_feas,
tol_infeas_abs: self.tol_infeas_abs,
tol_infeas_rel: self.tol_infeas_rel,
tol_ktratio: self.tol_ktratio,
reduced_tol_gap_abs: self.reduced_tol_gap_abs,
reduced_tol_gap_rel: self.reduced_tol_gap_rel,
reduced_tol_feas: self.reduced_tol_feas,
reduced_tol_infeas_abs: self.reduced_tol_infeas_abs,
reduced_tol_infeas_rel: self.reduced_tol_infeas_rel,
reduced_tol_ktratio: self.reduced_tol_ktratio,
max_step_fraction: self.max_step_fraction,
equilibrate_enable: self.equilibrate_enable,
equilibrate_max_iter: self.equilibrate_max_iter,
equilibrate_min_scaling: self.equilibrate_min_scaling,
equilibrate_max_scaling: self.equilibrate_max_scaling,
linesearch_backtrack_step: self.linesearch_backtrack_step,
min_switch_step_length: self.min_switch_step_length,
min_terminate_step_length: self.min_terminate_step_length,
max_threads: self.max_threads,
direct_kkt_solver: self.direct_kkt_solver,
direct_solve_method: self.direct_solve_method.clone(),
static_regularization_enable: self.static_regularization_enable,
static_regularization_constant: self.static_regularization_constant,
static_regularization_proportional: self.static_regularization_proportional,
dynamic_regularization_enable: self.dynamic_regularization_enable,
dynamic_regularization_eps: self.dynamic_regularization_eps,
dynamic_regularization_delta: self.dynamic_regularization_delta,
iterative_refinement_enable: self.iterative_refinement_enable,
iterative_refinement_reltol: self.iterative_refinement_reltol,
iterative_refinement_abstol: self.iterative_refinement_abstol,
iterative_refinement_max_iter: self.iterative_refinement_max_iter,
iterative_refinement_stop_ratio: self.iterative_refinement_stop_ratio,
presolve_enable: self.presolve_enable,
input_sparse_dropzeros: self.input_sparse_dropzeros,
#[cfg(feature = "sdp")]
chordal_decomposition_enable: self.chordal_decomposition_enable,
#[cfg(feature = "sdp")]
chordal_decomposition_merge_method: self.chordal_decomposition_merge_method.clone(),
#[cfg(feature = "sdp")]
chordal_decomposition_compact: self.chordal_decomposition_compact,
#[cfg(feature = "sdp")]
chordal_decomposition_complete_dual: self.chordal_decomposition_complete_dual,
#[cfg(any(feature = "pardiso-mkl", feature = "pardiso-panua"))]
pardiso_iparm: self.pardiso_iparm,
#[cfg(any(feature = "pardiso-mkl", feature = "pardiso-panua"))]
pardiso_verbose: self.pardiso_verbose,
};
Ok(settings)
}
}
impl From<DataUpdateError> for PyErr {
fn from(err: DataUpdateError) -> Self {
PyException::new_err(err.to_string())
}
}
#[pyclass(name = "DefaultSolver")]
pub struct PyDefaultSolver {
inner: DefaultSolver<f64>,
}
#[pymethods]
impl PyDefaultSolver {
#[new]
fn new(
P: PyCscMatrix,
q: Vec<f64>,
A: PyCscMatrix,
b: Vec<f64>,
cones: Vec<PySupportedCone>,
settings: PyDefaultSettings,
) -> PyResult<Self> {
let cones = _py_to_native_cones(cones);
let settings = settings.to_internal()?;
let solver = DefaultSolver::new(&P, &q, &A, &b, &cones, settings)?;
Ok(Self { inner: solver })
}
fn solve(&mut self, py: Python<'_>) -> PyDefaultSolution {
py.allow_threads(|| self.inner.solve());
self.get_solution()
}
pub fn __repr__(&self) -> String {
"Clarabel model with Float precision: f64".to_string()
}
fn print_configuration(&mut self) {
let verbose = self.inner.settings.core().verbose;
self.inner.settings.core_mut().verbose = true;
self.inner
.info
.print_configuration(&self.inner.settings, &self.inner.data, &self.inner.cones)
.unwrap();
self.inner.settings.core_mut().verbose = verbose;
}
fn print_timers(&self) {
match &self.inner.timers {
Some(timers) => timers.print(),
None => println!("no timers enabled"),
};
}
fn print_to_stdout(&mut self) {
self.inner.print_to_stdout();
}
fn print_to_file(&mut self, filename: &str) -> PyResult<()> {
let file = std::fs::File::create(filename)?;
self.inner.print_to_file(file);
Ok(())
}
fn print_to_buffer(&mut self) {
self.inner.print_to_buffer();
}
fn get_print_buffer(&mut self) -> PyResult<String> {
self.inner.get_print_buffer().map_err(|e| e.into())
}
#[pyo3(signature = (**kwds))]
fn update(&mut self, kwds: Option<&Bound<'_, PyDict>>) -> PyResult<()> {
for (key, value) in kwds.unwrap().iter() {
let key = key.extract::<String>()?;
match key.as_str() {
"P" => match _py_to_matrix_update(value) {
Some(PyMatrixUpdateData::Matrix(M)) => {
self.inner.update_P(&M)?;
}
Some(PyMatrixUpdateData::Vector(v)) => {
self.inner.update_P(&v)?;
}
Some(PyMatrixUpdateData::Tuple((indices, values))) => {
self.inner.update_P(&(indices, values))?;
}
None => {
return Err(PyException::new_err("Invalid P update data"));
}
},
"A" => match _py_to_matrix_update(value) {
Some(PyMatrixUpdateData::Matrix(M)) => {
self.inner.update_A(&M)?;
}
Some(PyMatrixUpdateData::Vector(v)) => {
self.inner.update_A(&v)?;
}
Some(PyMatrixUpdateData::Tuple((indices, values))) => {
self.inner.update_A(&(indices, values))?;
}
None => {
return Err(PyException::new_err("Invalid A update data"));
}
},
"q" => match _py_to_vector_update(value) {
Some(PyVectorUpdateData::Vector(v)) => {
self.inner.update_q(&v)?;
}
Some(PyVectorUpdateData::Tuple((indices, values))) => {
self.inner.update_q(&(indices, values))?;
}
None => {
return Err(PyException::new_err("Invalid q update data"));
}
},
"b" => match _py_to_vector_update(value) {
Some(PyVectorUpdateData::Vector(v)) => {
self.inner.update_b(&v)?;
}
Some(PyVectorUpdateData::Tuple((indices, values))) => {
self.inner.update_b(&(indices, values))?;
}
None => {
return Err(PyException::new_err("Invalid b update data"));
}
},
"settings" => {
let settings: PyDefaultSettings = value.extract()?;
let settings = settings.to_internal()?;
self.inner.update_settings(settings)?;
}
_ => {
println!("unrecognized key: {}", key);
}
}
}
Ok(())
}
fn get_settings(&self) -> PyDefaultSettings {
(&self.inner.settings).into()
}
fn get_info(&self) -> PyDefaultInfo {
(&self.inner.info).into()
}
fn set_termination_callback(&mut self, callback: PyObject) {
let callback = make_termination_callback(callback);
self.inner.set_termination_callback(callback);
}
fn unset_termination_callback(&mut self) {
self.inner.unset_termination_callback()
}
fn get_solution(&self) -> PyDefaultSolution {
(&self.inner.solution).into()
}
fn is_data_update_allowed(&self) -> bool {
self.inner.is_data_update_allowed()
}
}
pub fn make_termination_callback(
py_callback: PyObject,
) -> impl ClarabelCallbackFn<DefaultInfo<f64>> {
move |info: &DefaultInfo<f64>| {
let py_info = PyDefaultInfo::from(info);
Python::with_gil(|py| match py_callback.call1(py, (py_info,)) {
Ok(result) => result.extract::<bool>(py).unwrap_or(false),
Err(e) => {
e.print(py);
false
}
})
}
}
enum PyMatrixUpdateData {
Matrix(CscMatrix<f64>),
Vector(Vec<f64>),
Tuple((Vec<usize>, Vec<f64>)),
}
enum PyVectorUpdateData {
Vector(Vec<f64>),
Tuple((Vec<usize>, Vec<f64>)),
}
impl From<PyVectorUpdateData> for PyMatrixUpdateData {
fn from(val: PyVectorUpdateData) -> Self {
match val {
PyVectorUpdateData::Vector(v) => PyMatrixUpdateData::Vector(v),
PyVectorUpdateData::Tuple((indices, values)) => {
PyMatrixUpdateData::Tuple((indices, values))
}
}
}
}
fn _py_to_matrix_update(arg: Bound<'_, PyAny>) -> Option<PyMatrixUpdateData> {
let csc: Option<CscMatrix<f64>> = arg.extract::<PyCscMatrix>().ok().map(|x| x.into());
if let Some(csc) = csc {
return Some(PyMatrixUpdateData::Matrix(csc));
}
_py_to_vector_update(arg).map(|x| x.into())
}
fn _py_to_vector_update(arg: Bound<'_, PyAny>) -> Option<PyVectorUpdateData> {
let values: Option<Vec<f64>> = arg.extract().ok();
if let Some(values) = values {
return Some(PyVectorUpdateData::Vector(values));
}
let tuple = arg.extract::<(Vec<usize>, Vec<f64>)>().ok();
if let Some(tuple) = tuple {
return Some(PyVectorUpdateData::Tuple(tuple));
}
None
}
#[pyfunction(name = "load_from_file")]
#[pyo3(signature = (filename, settings=None))]
pub fn load_from_file_py(
filename: &str,
settings: Option<PyDefaultSettings>,
) -> PyResult<PyDefaultSolver> {
let mut file = std::fs::File::open(filename)?;
match settings {
Some(settings) => {
let settings = settings.to_internal()?;
let solver = DefaultSolver::<f64>::load_from_file(&mut file, Some(settings))?;
Ok(PyDefaultSolver { inner: solver })
}
None => {
let solver = DefaultSolver::<f64>::load_from_file(&mut file, None)?;
Ok(PyDefaultSolver { inner: solver })
}
}
}