From 9f919596f4963681a8f6526293abe4e949f63d5d Mon Sep 17 00:00:00 2001 From: Parth Pore Date: Fri, 24 Oct 2025 16:01:28 -0500 Subject: [PATCH 1/3] Enhance getSolution(): convert lists to NumPy arrays --- highs/highspy/highs.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/highs/highspy/highs.py b/highs/highspy/highs.py index 5d294186dc..27c27d09f3 100644 --- a/highs/highspy/highs.py +++ b/highs/highspy/highs.py @@ -36,6 +36,11 @@ class Highs(_Highs): __solver_started: Lock = Lock() __solver_status: Optional[HighsStatus] = None + @staticmethod + def float_array(vals): + if isinstance(vals,np.ndarray): return vals + return np.asarray(vals,dtype=np.float64) + def __init__(self): super().__init__() self.callbacks = [HighsCallback(cb.HighsCallbackType(_), self) for _ in range(int(cb.HighsCallbackType.kCallbackMax) + 1)] @@ -179,6 +184,15 @@ def optimize(self): """ return self.solve() + def getSolution(self): + #retriving the current solution to numpy vector fields + solution = super().getSolution() + solution.col_value = Highs.float_array(solution.col_value) + solution.col_dual = Highs.float_array(solution.col_dual) + solution.row_value = Highs.float_array(solution.row_value) + solution.row_dual = Highs.float_array(solution.row_dual) + return solution + # reset the objective def setObjective(self, obj: Optional[Union[highs_var, highs_linear_expression]] = None, sense: Optional[ObjSense] = None): """ @@ -294,7 +308,7 @@ def val( Returns: The value of the variable in the solution. """ - return Highs.internal_get_value(super().getSolution().col_value, var) + return Highs.internal_get_value(self.getSolution().col_value, var) @overload def vals(self, idxs: Union[Integral, highs_var, highs_cons]) -> float: ... @@ -321,7 +335,7 @@ def vals( Returns: If idxs is a Mapping, returns a dict where keys are the same keys from the input idxs and values are the solution values of the corresponding variables. If idxs is an iterable, returns a list of solution values for the variables. """ - return Highs.internal_get_value(super().getSolution().col_value, idxs) + return Highs.internal_get_value(self.getSolution().col_value, idxs) def variableName(self, var: Union[Integral, highs_var]): """ @@ -414,7 +428,7 @@ def allVariableValues(self): Returns: A list of values for all variables in the solution. """ - return super().getSolution().col_value + return self.getSolution().col_value def variableDual( self, @@ -429,7 +443,7 @@ def variableDual( Returns: The dual value of the specified variable in the solution. """ - return Highs.internal_get_value(super().getSolution().col_dual, var) + return Highs.internal_get_value(self.getSolution().col_dual, var) def variableDuals( self, @@ -444,7 +458,7 @@ def variableDuals( Returns: If idxs is a Mapping, returns a dict where keys are the same keys from the input idxs and values are the dual values of the corresponding variables. If idxs is an iterable, returns a list of dual values for the variables. """ - return Highs.internal_get_value(super().getSolution().col_dual, idxs) + return Highs.internal_get_value(self.getSolution().col_dual, idxs) def allVariableDuals(self): """ @@ -453,7 +467,7 @@ def allVariableDuals(self): Returns: A list of dual values for all variables in the solution. """ - return super().getSolution().col_dual + return self.getSolution().col_dual def constrValue( self, @@ -468,7 +482,7 @@ def constrValue( Returns: The value of the specified constraint in the solution. """ - return Highs.internal_get_value(super().getSolution().row_value, con) + return Highs.internal_get_value(self.getSolution().row_value, con) def constrValues( self, @@ -483,7 +497,7 @@ def constrValues( Returns: If cons is a Mapping, returns a dict where keys are the same keys from the input cons and values are the solution values of the corresponding constraints. If cons is an iterable, returns a list of solution values for the constraints. """ - return Highs.internal_get_value(super().getSolution().row_value, cons) + return Highs.internal_get_value(self.getSolution().row_value, cons) def allConstrValues(self): """ @@ -492,7 +506,7 @@ def allConstrValues(self): Returns: A list of values for all constraints in the solution. """ - return super().getSolution().row_value + return self.getSolution().row_value def constrDual( self, @@ -507,7 +521,7 @@ def constrDual( Returns: The dual value of the specified constraint in the solution. """ - return Highs.internal_get_value(super().getSolution().row_dual, con) + return Highs.internal_get_value(self.getSolution().row_dual, con) def constrDuals( self, @@ -522,7 +536,7 @@ def constrDuals( Returns: If cons is a Mapping, returns a dict where keys are the same keys from the input cons and values are the dual values of the corresponding constraints. If cons is an iterable, returns a list of dual values for the constraints. """ - return Highs.internal_get_value(super().getSolution().row_dual, cons) + return Highs.internal_get_value(self.getSolution().row_dual, cons) def allConstrDuals(self): """ @@ -531,7 +545,7 @@ def allConstrDuals(self): Returns: A list of dual values for all constraints in the solution. """ - return super().getSolution().row_dual + return self.getSolution().row_dual def addVariable( self, From abf026e57bc9627fc3add99e809c240833343e02 Mon Sep 17 00:00:00 2001 From: Parth Pore Date: Tue, 28 Oct 2025 20:44:46 -0500 Subject: [PATCH 2/3] Addressed review comments and updated the code as per the feedback --- highs/highs_bindings.cpp | 13 +++++++++++- highs/highspy/_core/__init__.pyi | 10 ++++----- highs/highspy/highs.py | 36 ++++++++++---------------------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/highs/highs_bindings.cpp b/highs/highs_bindings.cpp index 84990c130d..681d6cc662 100644 --- a/highs/highs_bindings.cpp +++ b/highs/highs_bindings.cpp @@ -28,6 +28,16 @@ std::function(const Base&)> make_readonly_ptr( }; } +template +std::function)> make_vector_setter( + std::vector Base::* member){ + return [member](Base& self,dense_array_tvalues){ + auto buf = values.request(); + auto ptr = static_cast(buf.ptr); + (self.*member).assign(ptr, ptr + buf.size); + }; +} + HighsStatus highs_passModel(Highs* h, HighsModel& model) { return h->passModel(model); } @@ -1059,7 +1069,8 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) { .def(py::init<>()) .def_readwrite("num_col_", &HighsLp::num_col_) .def_readwrite("num_row_", &HighsLp::num_row_) - .def_readwrite("col_cost_", &HighsLp::col_cost_) + .def_property("col_cost_", make_readonly_ptr(&HighsLp::col_cost_), + make_vector_setter(&HighsLp::col_cost_)) .def_readwrite("col_lower_", &HighsLp::col_lower_) .def_readwrite("col_upper_", &HighsLp::col_upper_) .def_readwrite("row_lower_", &HighsLp::row_lower_) diff --git a/highs/highspy/_core/__init__.pyi b/highs/highspy/_core/__init__.pyi index 27b7fc18a7..61acf2c85d 100644 --- a/highs/highspy/_core/__init__.pyi +++ b/highs/highspy/_core/__init__.pyi @@ -262,7 +262,7 @@ class HighsLogType: class HighsLp: a_matrix_: HighsSparseMatrix - col_cost_: list[float] + col_cost_: numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] col_lower_: list[float] col_names_: list[str] col_upper_: list[float] @@ -525,11 +525,11 @@ class HighsScale: pass class HighsSolution: - col_dual: list[float] - col_value: list[float] + col_dual: numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] + col_value: numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] dual_valid: bool - row_dual: list[float] - row_value: list[float] + row_dual: numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] + row_value: numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] value_valid: bool def __init__(self) -> None: ... diff --git a/highs/highspy/highs.py b/highs/highspy/highs.py index 27c27d09f3..cf585a48c1 100644 --- a/highs/highspy/highs.py +++ b/highs/highspy/highs.py @@ -36,11 +36,6 @@ class Highs(_Highs): __solver_started: Lock = Lock() __solver_status: Optional[HighsStatus] = None - @staticmethod - def float_array(vals): - if isinstance(vals,np.ndarray): return vals - return np.asarray(vals,dtype=np.float64) - def __init__(self): super().__init__() self.callbacks = [HighsCallback(cb.HighsCallbackType(_), self) for _ in range(int(cb.HighsCallbackType.kCallbackMax) + 1)] @@ -184,15 +179,6 @@ def optimize(self): """ return self.solve() - def getSolution(self): - #retriving the current solution to numpy vector fields - solution = super().getSolution() - solution.col_value = Highs.float_array(solution.col_value) - solution.col_dual = Highs.float_array(solution.col_dual) - solution.row_value = Highs.float_array(solution.row_value) - solution.row_dual = Highs.float_array(solution.row_dual) - return solution - # reset the objective def setObjective(self, obj: Optional[Union[highs_var, highs_linear_expression]] = None, sense: Optional[ObjSense] = None): """ @@ -308,7 +294,7 @@ def val( Returns: The value of the variable in the solution. """ - return Highs.internal_get_value(self.getSolution().col_value, var) + return Highs.internal_get_value(super().getSolution().col_value, var) @overload def vals(self, idxs: Union[Integral, highs_var, highs_cons]) -> float: ... @@ -335,7 +321,7 @@ def vals( Returns: If idxs is a Mapping, returns a dict where keys are the same keys from the input idxs and values are the solution values of the corresponding variables. If idxs is an iterable, returns a list of solution values for the variables. """ - return Highs.internal_get_value(self.getSolution().col_value, idxs) + return Highs.internal_get_value(super().getSolution().col_value, idxs) def variableName(self, var: Union[Integral, highs_var]): """ @@ -428,7 +414,7 @@ def allVariableValues(self): Returns: A list of values for all variables in the solution. """ - return self.getSolution().col_value + return super().getSolution().col_value def variableDual( self, @@ -443,7 +429,7 @@ def variableDual( Returns: The dual value of the specified variable in the solution. """ - return Highs.internal_get_value(self.getSolution().col_dual, var) + return Highs.internal_get_value(super().getSolution().col_dual, var) def variableDuals( self, @@ -458,7 +444,7 @@ def variableDuals( Returns: If idxs is a Mapping, returns a dict where keys are the same keys from the input idxs and values are the dual values of the corresponding variables. If idxs is an iterable, returns a list of dual values for the variables. """ - return Highs.internal_get_value(self.getSolution().col_dual, idxs) + return Highs.internal_get_value(super().getSolution().col_dual, idxs) def allVariableDuals(self): """ @@ -467,7 +453,7 @@ def allVariableDuals(self): Returns: A list of dual values for all variables in the solution. """ - return self.getSolution().col_dual + return super().getSolution().col_dual def constrValue( self, @@ -482,7 +468,7 @@ def constrValue( Returns: The value of the specified constraint in the solution. """ - return Highs.internal_get_value(self.getSolution().row_value, con) + return Highs.internal_get_value(super().getSolution().row_value, con) def constrValues( self, @@ -497,7 +483,7 @@ def constrValues( Returns: If cons is a Mapping, returns a dict where keys are the same keys from the input cons and values are the solution values of the corresponding constraints. If cons is an iterable, returns a list of solution values for the constraints. """ - return Highs.internal_get_value(self.getSolution().row_value, cons) + return Highs.internal_get_value(super().getSolution().row_value, cons) def allConstrValues(self): """ @@ -521,7 +507,7 @@ def constrDual( Returns: The dual value of the specified constraint in the solution. """ - return Highs.internal_get_value(self.getSolution().row_dual, con) + return Highs.internal_get_value(super().getSolution().row_dual, con) def constrDuals( self, @@ -536,7 +522,7 @@ def constrDuals( Returns: If cons is a Mapping, returns a dict where keys are the same keys from the input cons and values are the dual values of the corresponding constraints. If cons is an iterable, returns a list of dual values for the constraints. """ - return Highs.internal_get_value(self.getSolution().row_dual, cons) + return Highs.internal_get_value(super().getSolution().row_dual, cons) def allConstrDuals(self): """ @@ -545,7 +531,7 @@ def allConstrDuals(self): Returns: A list of dual values for all constraints in the solution. """ - return self.getSolution().row_dual + return super().getSolution().row_dual def addVariable( self, From 6b86755a8d54c8efea4faceaf852999262b7e0c3 Mon Sep 17 00:00:00 2001 From: Parth Pore Date: Tue, 28 Oct 2025 21:01:16 -0500 Subject: [PATCH 3/3] Fix Bazel GitHub Actions workflow (triggers + matrix build) --- .github/workflows/build-bazel.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-bazel.yml b/.github/workflows/build-bazel.yml index 6fcce6ae37..dd5bbba4b9 100644 --- a/.github/workflows/build-bazel.yml +++ b/.github/workflows/build-bazel.yml @@ -1,6 +1,11 @@ name: build-bazel -on: [push, pull_request] +on: + push : + branches: [ master, main, Parthpore10_1590issue_fix ] + pull_request: + branches: [master,main] + workflow_dispatch: jobs: bazel: