From e27b984766faba444614cadb21d06b6b5e7b46fa Mon Sep 17 00:00:00 2001 From: Jonas Breuling Date: Wed, 4 Mar 2026 09:44:03 +0100 Subject: [PATCH 1/2] Add bindings for update_vector_data and update_matrix_data. --- src/bindings.cpp.in | 80 +++++++++++++++++++++++++++++++++++- src/qoco/interface.py | 95 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/src/bindings.cpp.in b/src/bindings.cpp.in index 7e88d7b..e798559 100644 --- a/src/bindings.cpp.in +++ b/src/bindings.cpp.in @@ -140,8 +140,10 @@ public: PyQOCOSolution &get_solution(); QOCOInt update_settings(const QOCOSettings &); - // QOCOInt update_vector_data(py::object, py::object, py::object); - // QOCOInt update_matrix_data(py::object, py::object, py::object); + //QOCOInt update_vector_data(py::object, py::object, py::object); + //QOCOInt update_matrix_data(py::object, py::object, py::object); + QOCOInt update_vector_data(py::object cnew, py::object bnew, py::object hnew); + QOCOInt update_matrix_data(py::object Pxnew, py::object Axnew, py::object Gxnew); QOCOInt solve(); @@ -241,6 +243,78 @@ QOCOInt PyQOCOSolver::update_settings(const QOCOSettings &new_settings) return qoco_update_settings(this->_solver, &new_settings); } +QOCOInt PyQOCOSolver::update_vector_data(py::object cnew, py::object bnew, py::object hnew) +{ + QOCOFloat *cnew_ptr = nullptr; + QOCOFloat *bnew_ptr = nullptr; + QOCOFloat *hnew_ptr = nullptr; + + if (cnew != py::none()) + { + auto cnew_arr = cnew.cast>(); + auto buf = cnew_arr.request(); + if (buf.shape[0] != this->n) + throw std::runtime_error("cnew size must be n = " + std::to_string(this->n)); + cnew_ptr = (QOCOFloat *)buf.ptr; + } + + if (bnew != py::none()) + { + auto bnew_arr = bnew.cast>(); + auto buf = bnew_arr.request(); + if (buf.shape[0] != this->p) + throw std::runtime_error("bnew size must be p = " + std::to_string(this->p)); + bnew_ptr = (QOCOFloat *)buf.ptr; + } + + if (hnew != py::none()) + { + auto hnew_arr = hnew.cast>(); + auto buf = hnew_arr.request(); + if (buf.shape[0] != this->m) + throw std::runtime_error("hnew size must be m = " + std::to_string(this->m)); + hnew_ptr = (QOCOFloat *)buf.ptr; + } + + return qoco_update_vector_data(this->_solver, cnew_ptr, bnew_ptr, hnew_ptr); +} + +QOCOInt PyQOCOSolver::update_matrix_data(py::object Pxnew, py::object Axnew, py::object Gxnew) +{ + QOCOFloat *Pxnew_ptr = nullptr; + QOCOFloat *Axnew_ptr = nullptr; + QOCOFloat *Gxnew_ptr = nullptr; + + if (Pxnew != py::none()) + { + auto Pxnew_arr = Pxnew.cast>(); + auto buf = Pxnew_arr.request(); + if (buf.ndim != 1) + throw std::runtime_error("Pxnew must be 1-D array"); + Pxnew_ptr = (QOCOFloat *)buf.ptr; + } + + if (Axnew != py::none()) + { + auto Axnew_arr = Axnew.cast>(); + auto buf = Axnew_arr.request(); + if (buf.ndim != 1) + throw std::runtime_error("Axnew must be 1-D array"); + Axnew_ptr = (QOCOFloat *)buf.ptr; + } + + if (Gxnew != py::none()) + { + auto Gxnew_arr = Gxnew.cast>(); + auto buf = Gxnew_arr.request(); + if (buf.ndim != 1) + throw std::runtime_error("Gxnew must be 1-D array"); + Gxnew_ptr = (QOCOFloat *)buf.ptr; + } + + return qoco_update_matrix_data(this->_solver, Pxnew_ptr, Axnew_ptr, Gxnew_ptr); +} + PYBIND11_MODULE(@QOCO_EXT_MODULE_NAME@, m) { // Enums. @@ -308,6 +382,8 @@ PYBIND11_MODULE(@QOCO_EXT_MODULE_NAME@, m) .def(py::init, const CSC &, const py::array_t, const CSC &, const py::array_t, QOCOInt, QOCOInt, const py::array_t, QOCOSettings *>(), "n"_a, "m"_a, "p"_a, "P"_a, "c"_a.noconvert(), "A"_a, "b"_a.noconvert(), "G"_a, "h"_a.noconvert(), "l"_a, "nsoc"_a, "q"_a.noconvert(), "settings"_a) .def_property_readonly("solution", &PyQOCOSolver::get_solution, py::return_value_policy::reference) .def("update_settings", &PyQOCOSolver::update_settings) + .def("update_vector_data", &PyQOCOSolver::update_vector_data, "cnew"_a=py::none(), "bnew"_a=py::none(), "hnew"_a=py::none()) + .def("update_matrix_data", &PyQOCOSolver::update_matrix_data, "Pxnew"_a=py::none(), "Axnew"_a=py::none(), "Gxnew"_a=py::none()) .def("solve", &PyQOCOSolver::solve) .def("get_settings", &PyQOCOSolver::get_settings, py::return_value_policy::reference); } diff --git a/src/qoco/interface.py b/src/qoco/interface.py index e55ced5..a09e373 100644 --- a/src/qoco/interface.py +++ b/src/qoco/interface.py @@ -86,6 +86,101 @@ def update_settings(self, **kwargs): if settings_changed and self._solver is not None: self._solver.update_settings(self.settings) + def update_vector_data(self, c=None, b=None, h=None): + """ + Update data vectors. + + Parameters + ---------- + c : np.ndarray, optional + New c vector of size n. If None, c is not updated. Default is None. + b : np.ndarray, optional + New b vector of size p. If None, b is not updated. Default is None. + h : np.ndarray, optional + New h vector of size m. If None, h is not updated. Default is None. + + Returns + ------- + int + Status code from the solver + """ + cnew_ptr = None + bnew_ptr = None + hnew_ptr = None + + if c is not None: + if not isinstance(c, np.ndarray): + c = np.array(c) + c = c.astype(np.float64) + if c.shape[0] != self.n: + raise ValueError(f"c size must be n = {self.n}") + cnew_ptr = c + + if b is not None: + if not isinstance(b, np.ndarray): + b = np.array(b) + b = b.astype(np.float64) + if b.shape[0] != self.p: + raise ValueError(f"b size must be p = {self.p}") + bnew_ptr = b + + if h is not None: + if not isinstance(h, np.ndarray): + h = np.array(h) + h = h.astype(np.float64) + if h.shape[0] != self.m: + raise ValueError(f"h size must be m = {self.m}") + hnew_ptr = h + + return self._solver.update_vector_data(cnew_ptr, bnew_ptr, hnew_ptr) + + def update_matrix_data(self, P=None, A=None, G=None): + """ + Update sparse matrix data. + + The new matrices must have the same sparsity structure as the original ones. + + Parameters + ---------- + P : np.ndarray, optional + New data for P matrix (only the nonzero values). If None, P is not updated. + Default is None. + A : np.ndarray, optional + New data for A matrix (only the nonzero values). If None, A is not updated. + Default is None. + G : np.ndarray, optional + New data for G matrix (only the nonzero values). If None, G is not updated. + Default is None. + + Returns + ------- + int + Status code from the solver + """ + Pxnew_ptr = None + Axnew_ptr = None + Gxnew_ptr = None + + if P is not None: + if not isinstance(P, np.ndarray): + P = np.array(P) + P = P.astype(np.float64) + Pxnew_ptr = P + + if A is not None: + if not isinstance(A, np.ndarray): + A = np.array(A) + A = A.astype(np.float64) + Axnew_ptr = A + + if G is not None: + if not isinstance(G, np.ndarray): + G = np.array(G) + G = G.astype(np.float64) + Gxnew_ptr = G + + return self._solver.update_matrix_data(Pxnew_ptr, Axnew_ptr, Gxnew_ptr) + def setup(self, n, m, p, P, c, A, b, G, h, l, nsoc, q, **settings): self.m = m self.n = n From 872dc9df1cd1fce85f715440737af51d6ccf23f7 Mon Sep 17 00:00:00 2001 From: Jonas Breuling Date: Fri, 6 Mar 2026 06:21:34 +0100 Subject: [PATCH 2/2] Removed unused exit flag. --- src/bindings.cpp.in | 12 +++++------- src/qoco/interface.py | 10 ---------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/bindings.cpp.in b/src/bindings.cpp.in index e798559..abea24a 100644 --- a/src/bindings.cpp.in +++ b/src/bindings.cpp.in @@ -139,11 +139,9 @@ public: QOCOSettings *get_settings(); PyQOCOSolution &get_solution(); - QOCOInt update_settings(const QOCOSettings &); - //QOCOInt update_vector_data(py::object, py::object, py::object); - //QOCOInt update_matrix_data(py::object, py::object, py::object); - QOCOInt update_vector_data(py::object cnew, py::object bnew, py::object hnew); - QOCOInt update_matrix_data(py::object Pxnew, py::object Axnew, py::object Gxnew); + QOCOInt update_settings(const QOCOSettings &new_settings); + void update_vector_data(py::object cnew, py::object bnew, py::object hnew); + void update_matrix_data(py::object Pxnew, py::object Axnew, py::object Gxnew); QOCOInt solve(); @@ -243,7 +241,7 @@ QOCOInt PyQOCOSolver::update_settings(const QOCOSettings &new_settings) return qoco_update_settings(this->_solver, &new_settings); } -QOCOInt PyQOCOSolver::update_vector_data(py::object cnew, py::object bnew, py::object hnew) +void PyQOCOSolver::update_vector_data(py::object cnew, py::object bnew, py::object hnew) { QOCOFloat *cnew_ptr = nullptr; QOCOFloat *bnew_ptr = nullptr; @@ -279,7 +277,7 @@ QOCOInt PyQOCOSolver::update_vector_data(py::object cnew, py::object bnew, py::o return qoco_update_vector_data(this->_solver, cnew_ptr, bnew_ptr, hnew_ptr); } -QOCOInt PyQOCOSolver::update_matrix_data(py::object Pxnew, py::object Axnew, py::object Gxnew) +void PyQOCOSolver::update_matrix_data(py::object Pxnew, py::object Axnew, py::object Gxnew) { QOCOFloat *Pxnew_ptr = nullptr; QOCOFloat *Axnew_ptr = nullptr; diff --git a/src/qoco/interface.py b/src/qoco/interface.py index a09e373..e6a7153 100644 --- a/src/qoco/interface.py +++ b/src/qoco/interface.py @@ -98,11 +98,6 @@ def update_vector_data(self, c=None, b=None, h=None): New b vector of size p. If None, b is not updated. Default is None. h : np.ndarray, optional New h vector of size m. If None, h is not updated. Default is None. - - Returns - ------- - int - Status code from the solver """ cnew_ptr = None bnew_ptr = None @@ -151,11 +146,6 @@ def update_matrix_data(self, P=None, A=None, G=None): G : np.ndarray, optional New data for G matrix (only the nonzero values). If None, G is not updated. Default is None. - - Returns - ------- - int - Status code from the solver """ Pxnew_ptr = None Axnew_ptr = None