From 0c59a8d7b26428fa150dfb7ab3d41be4adfefaa8 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 8 Oct 2023 06:32:59 +0000 Subject: [PATCH 01/19] BUG: Add back presolve to highspy --- highspy/highs_bindings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index 9c2a75d91d..34d2ab24f9 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -819,6 +819,7 @@ PYBIND11_MODULE(highspy, m) { .def("postsolve", &highs_postsolve) .def("postsolve", &highs_mipPostsolve) .def("run", &Highs::run) + .def("presolve", &Highs::presolve) .def("writeSolution", &highs_writeSolution) .def("readSolution", &Highs::readSolution) .def("setOptionValue", From 6b8b3588a693ee6349d80523400dbd7327cddf15 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 8 Oct 2023 07:05:43 +0000 Subject: [PATCH 02/19] MAINT: Format and export enum values --- highspy/highs_bindings.cpp | 50 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index 34d2ab24f9..b2ebe38ca5 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -118,13 +118,12 @@ HighsStatus highs_passHessianPointers(Highs* h, const HighsInt dim, q_value_ptr); } -HighsStatus highs_postsolve(Highs* h, const HighsSolution& solution, const HighsBasis& basis) -{ +HighsStatus highs_postsolve(Highs* h, const HighsSolution& solution, + const HighsBasis& basis) { return h->postsolve(solution, basis); } - -HighsStatus highs_mipPostsolve(Highs* h, const HighsSolution& solution) -{ + +HighsStatus highs_mipPostsolve(Highs* h, const HighsSolution& solution) { return h->postsolve(solution); } @@ -557,23 +556,27 @@ PYBIND11_MODULE(highspy, m) { // enum classes py::enum_(m, "ObjSense") .value("kMinimize", ObjSense::kMinimize) - .value("kMaximize", ObjSense::kMaximize); + .value("kMaximize", ObjSense::kMaximize) + .export_values(); py::enum_(m, "MatrixFormat") .value("kColwise", MatrixFormat::kColwise) .value("kRowwise", MatrixFormat::kRowwise) - .value("kRowwisePartitioned", MatrixFormat::kRowwisePartitioned); + .value("kRowwisePartitioned", MatrixFormat::kRowwisePartitioned) + .export_values(); py::enum_(m, "HessianFormat") .value("kTriangular", HessianFormat::kTriangular) - .value("kSquare", HessianFormat::kSquare); + .value("kSquare", HessianFormat::kSquare) + .export_values(); py::enum_(m, "SolutionStatus") .value("kSolutionStatusNone", SolutionStatus::kSolutionStatusNone) .value("kSolutionStatusInfeasible", SolutionStatus::kSolutionStatusInfeasible) - .value("kSolutionStatusFeasible", - SolutionStatus::kSolutionStatusFeasible); + .value("kSolutionStatusFeasible", SolutionStatus::kSolutionStatusFeasible) + .export_values(); py::enum_(m, "BasisValidity") .value("kBasisValidityInvalid", BasisValidity::kBasisValidityInvalid) - .value("kBasisValidityValid", BasisValidity::kBasisValidityValid); + .value("kBasisValidityValid", BasisValidity::kBasisValidityValid) + .export_values(); py::enum_(m, "HighsModelStatus") .value("kNotset", HighsModelStatus::kNotset) .value("kLoadError", HighsModelStatus::kLoadError) @@ -592,7 +595,8 @@ PYBIND11_MODULE(highspy, m) { .value("kIterationLimit", HighsModelStatus::kIterationLimit) .value("kUnknown", HighsModelStatus::kUnknown) .value("kSolutionLimit", HighsModelStatus::kSolutionLimit) - .value("kInterrupt", HighsModelStatus::kInterrupt); + .value("kInterrupt", HighsModelStatus::kInterrupt) + .export_values(); py::enum_(m, "HighsPresolveStatus") .value("kNotPresolved", HighsPresolveStatus::kNotPresolved) .value("kNotReduced", HighsPresolveStatus::kNotReduced) @@ -603,37 +607,45 @@ PYBIND11_MODULE(highspy, m) { .value("kReducedToEmpty", HighsPresolveStatus::kReducedToEmpty) .value("kTimeout", HighsPresolveStatus::kTimeout) .value("kNullError", HighsPresolveStatus::kNullError) - .value("kOptionsError", HighsPresolveStatus::kOptionsError); + .value("kOptionsError", HighsPresolveStatus::kOptionsError) + .export_values(); py::enum_(m, "HighsBasisStatus") .value("kLower", HighsBasisStatus::kLower) .value("kBasic", HighsBasisStatus::kBasic) .value("kUpper", HighsBasisStatus::kUpper) .value("kZero", HighsBasisStatus::kZero) - .value("kNonbasic", HighsBasisStatus::kNonbasic); + .value("kNonbasic", HighsBasisStatus::kNonbasic) + .export_values(); py::enum_(m, "HighsVarType") .value("kContinuous", HighsVarType::kContinuous) .value("kInteger", HighsVarType::kInteger) .value("kSemiContinuous", HighsVarType::kSemiContinuous) - .value("kSemiInteger", HighsVarType::kSemiInteger); + .value("kSemiInteger", HighsVarType::kSemiInteger) + .export_values(); py::enum_(m, "HighsOptionType") .value("kBool", HighsOptionType::kBool) .value("kInt", HighsOptionType::kInt) .value("kDouble", HighsOptionType::kDouble) - .value("kString", HighsOptionType::kString); + .value("kString", HighsOptionType::kString) + .export_values(); py::enum_(m, "HighsInfoType") .value("kInt64", HighsInfoType::kInt64) .value("kInt", HighsInfoType::kInt) - .value("kDouble", HighsInfoType::kDouble); + .value("kDouble", HighsInfoType::kDouble) + .export_values(); py::enum_(m, "HighsStatus") .value("kError", HighsStatus::kError) .value("kOk", HighsStatus::kOk) - .value("kWarning", HighsStatus::kWarning); + .value("kWarning", HighsStatus::kWarning) + .export_values(); py::enum_(m, "HighsLogType") .value("kInfo", HighsLogType::kInfo) .value("kDetailed", HighsLogType::kDetailed) .value("kVerbose", HighsLogType::kVerbose) .value("kWarning", HighsLogType::kWarning) - .value("kError", HighsLogType::kError); + .value("kError", HighsLogType::kError) + .export_values(); + // Classes py::class_(m, "HighsSparseMatrix") .def(py::init<>()) .def_readwrite("format_", &HighsSparseMatrix::format_) From 88c038fc2679811880ce1384d84db4e6fd759ad2 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 8 Oct 2023 12:16:26 +0000 Subject: [PATCH 03/19] CI: Test python examples on the CI --- .github/workflows/build-meson.yml | 6 ++++++ examples/call_highs_from_python.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-meson.yml b/.github/workflows/build-meson.yml index 7f3b45a1e1..8ab5c49b1b 100644 --- a/.github/workflows/build-meson.yml +++ b/.github/workflows/build-meson.yml @@ -32,3 +32,9 @@ jobs: run: | meson setup bbdir_test -Duse_zlib=enabled -Dwith_tests=True --prefix $CONDA_PREFIX meson test -C bbdir_test + - name: Test compiled highspy + shell: bash -l {0} + run: | + meson configure bbdir_test -Dwith_pybind11=True + meson compile -C bbdir_test + PYTHONPATH=$(pwd)/bbdir_test/highspy python examples/call_highs_from_python.py diff --git a/examples/call_highs_from_python.py b/examples/call_highs_from_python.py index 32223e0445..544b589f92 100644 --- a/examples/call_highs_from_python.py +++ b/examples/call_highs_from_python.py @@ -41,7 +41,7 @@ print(icol, solution.col_value[icol], h.basisStatusToString(basis.col_status[icol])) # Read in and solve avgas -h.readModel("../check/instances/avgas.mps") +h.readModel("check/instances/avgas.mps") #h.writeModel("ml.mps") h.run() lp = h.getLp() @@ -149,7 +149,7 @@ h.clear() print('25fv47 as HighsModel') -h.readModel("../check/instances/25fv47.mps") +h.readModel("check/instances/25fv47.mps") h.presolve() presolved_lp = h.getPresolvedLp() # Create a HiGHS instance to solve the presolved LP From b294b7bb2b4aab5343bc1aafc4e009665324b20d Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 8 Oct 2023 12:28:25 +0000 Subject: [PATCH 04/19] CI: Add a dep for running the examples --- .github/workflows/build-meson.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-meson.yml b/.github/workflows/build-meson.yml index 8ab5c49b1b..8183005325 100644 --- a/.github/workflows/build-meson.yml +++ b/.github/workflows/build-meson.yml @@ -23,6 +23,7 @@ jobs: ninja zlib catch2 + numpy cache-environment: true init-shell: >- bash From a32318ed82f9cb64dcf4afbaa41c110548c36523 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 17 Sep 2023 08:09:45 +0000 Subject: [PATCH 05/19] TST: Isolate buggy constraint removal [highspy] --- highspy/tests/test_highspy.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/highspy/tests/test_highspy.py b/highspy/tests/test_highspy.py index fc4b909e9d..c693f6da8b 100644 --- a/highspy/tests/test_highspy.py +++ b/highspy/tests/test_highspy.py @@ -432,7 +432,16 @@ def test_ranging(self): self.assertEqual(ranging.row_bound_up.value_[1], inf); self.assertEqual(ranging.row_bound_up.objective_[1], inf); - + def test_constraint_removal(self): + h = highspy.Highs() + x = h.addVar(lb=-h.inf) + y = h.addVar(lb=-h.inf) + c1 = h.addConstr(-x + y >= 2) + c2 = h.addConstr(x + y >= 0) + self.assertEqual(h.numConstrs, 2) + h.removeConstr(c1) + self.assertEqual(h.numConstrs, 1) + def test_infeasible_model(self): h = highspy.Highs() h.setOptionValue('output_flag', False) From 18edf05742811cd48ea5df6e86d04d163fa4abd5 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 17 Sep 2023 09:40:30 +0000 Subject: [PATCH 06/19] BUG: Fix constraint and var removal [highspy] --- highspy/highs_bindings.cpp | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index b2ebe38ca5..755a104581 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -268,27 +268,14 @@ HighsStatus highs_changeColsIntegrality(Highs* h, HighsInt num_set_entries, integrality_ptr); } +// Same as deleteVars HighsStatus highs_deleteCols(Highs* h, HighsInt num_set_entries, - py::array_t indices) { - py::buffer_info indices_info = indices.request(); - - HighsInt* indices_ptr = static_cast(indices_info.ptr); - - return h->deleteCols(num_set_entries, indices_ptr); -} - -HighsStatus highs_deleteVars(Highs* h, HighsInt num_set_entries, - py::array_t indices) { - return highs_deleteCols(h, num_set_entries, indices); + std::vector& indices) { + return h->deleteCols(num_set_entries, indices.data()); } -HighsStatus highs_deleteRows(Highs* h, HighsInt num_set_entries, - py::array_t indices) { - py::buffer_info indices_info = indices.request(); - - HighsInt* indices_ptr = static_cast(indices_info.ptr); - - return h->deleteRows(num_set_entries, indices_ptr); +HighsStatus highs_deleteRows(Highs* h, HighsInt num_set_entries, std::vector& indices) { + return h->deleteRows(num_set_entries, indices.data()); } std::tuple highs_getOptionValue( @@ -920,7 +907,7 @@ PYBIND11_MODULE(highspy, m) { .def("changeColsBounds", &highs_changeColsBounds) .def("changeColsIntegrality", &highs_changeColsIntegrality) .def("deleteCols", &highs_deleteCols) - .def("deleteVars", &highs_deleteVars) + .def("deleteVars", &highs_deleteCols) // alias .def("deleteRows", &highs_deleteRows) .def("setSolution", &Highs::setSolution) .def("modelStatusToString", &Highs::modelStatusToString) From 9f3d7a34de1d01b5730d961b3ee928eb8d95d511 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 30 Sep 2023 23:34:13 +0000 Subject: [PATCH 07/19] MAINT: Add back the simplex_constants --- highspy/highs_bindings.cpp | 135 +++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index 755a104581..e2c3a65556 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -955,4 +955,139 @@ PYBIND11_MODULE(highspy, m) { // constants m.attr("kHighsInf") = kHighsInf; m.attr("kHighsIInf") = kHighsIInf; + // Submodules + py::module_ simplex_constants = + m.def_submodule("simplex_constants", "Submodule for simplex constants"); + + py::enum_(simplex_constants, "SimplexStrategy") + .value("kSimplexStrategyMin", SimplexStrategy::kSimplexStrategyMin) + .value("kSimplexStrategyChoose", SimplexStrategy::kSimplexStrategyChoose) + .value("kSimplexStrategyDual", SimplexStrategy::kSimplexStrategyDual) + .value("kSimplexStrategyDualPlain", + SimplexStrategy::kSimplexStrategyDualPlain) + .value("kSimplexStrategyDualTasks", + SimplexStrategy::kSimplexStrategyDualTasks) + .value("kSimplexStrategyDualMulti", + SimplexStrategy::kSimplexStrategyDualMulti) + .value("kSimplexStrategyPrimal", SimplexStrategy::kSimplexStrategyPrimal) + .value("kSimplexStrategyMax", SimplexStrategy::kSimplexStrategyMax) + .value("kSimplexStrategyNum", SimplexStrategy::kSimplexStrategyNum) + .export_values(); // needed since it isn't an enum class + py::enum_(simplex_constants, + "SimplexUnscaledSolutionStrategy") + .value( + "kSimplexUnscaledSolutionStrategyMin", + SimplexUnscaledSolutionStrategy::kSimplexUnscaledSolutionStrategyMin) + .value( + "kSimplexUnscaledSolutionStrategyNone", + SimplexUnscaledSolutionStrategy::kSimplexUnscaledSolutionStrategyNone) + .value("kSimplexUnscaledSolutionStrategyRefine", + SimplexUnscaledSolutionStrategy:: + kSimplexUnscaledSolutionStrategyRefine) + .value("kSimplexUnscaledSolutionStrategyDirect", + SimplexUnscaledSolutionStrategy:: + kSimplexUnscaledSolutionStrategyDirect) + .value( + "kSimplexUnscaledSolutionStrategyMax", + SimplexUnscaledSolutionStrategy::kSimplexUnscaledSolutionStrategyMax) + .value( + "kSimplexUnscaledSolutionStrategyNum", + SimplexUnscaledSolutionStrategy::kSimplexUnscaledSolutionStrategyNum) + .export_values(); + py::enum_(simplex_constants, "SimplexSolvePhase") + .value("kSolvePhaseMin", SimplexSolvePhase::kSolvePhaseMin) + .value("kSolvePhaseError", SimplexSolvePhase::kSolvePhaseError) + .value("kSolvePhaseExit", SimplexSolvePhase::kSolvePhaseExit) + .value("kSolvePhaseUnknown", SimplexSolvePhase::kSolvePhaseUnknown) + .value("kSolvePhaseOptimal", SimplexSolvePhase::kSolvePhaseOptimal) + .value("kSolvePhase1", SimplexSolvePhase::kSolvePhase1) + .value("kSolvePhase2", SimplexSolvePhase::kSolvePhase2) + .value("kSolvePhasePrimalInfeasibleCleanup", + SimplexSolvePhase::kSolvePhasePrimalInfeasibleCleanup) + .value("kSolvePhaseOptimalCleanup", + SimplexSolvePhase::kSolvePhaseOptimalCleanup) + .value("kSolvePhaseTabooBasis", SimplexSolvePhase::kSolvePhaseTabooBasis) + .value("kSolvePhaseMax", SimplexSolvePhase::kSolvePhaseMax) + .export_values(); + py::enum_(simplex_constants, + "SimplexEdgeWeightStrategy") + .value("kSimplexEdgeWeightStrategyMin", + SimplexEdgeWeightStrategy::kSimplexEdgeWeightStrategyMin) + .value("kSimplexEdgeWeightStrategyChoose", + SimplexEdgeWeightStrategy::kSimplexEdgeWeightStrategyChoose) + .value("kSimplexEdgeWeightStrategyDantzig", + SimplexEdgeWeightStrategy::kSimplexEdgeWeightStrategyDantzig) + .value("kSimplexEdgeWeightStrategyDevex", + SimplexEdgeWeightStrategy::kSimplexEdgeWeightStrategyDevex) + .value("kSimplexEdgeWeightStrategySteepestEdge", + SimplexEdgeWeightStrategy::kSimplexEdgeWeightStrategySteepestEdge) + .value("kSimplexEdgeWeightStrategyMax", + SimplexEdgeWeightStrategy::kSimplexEdgeWeightStrategyMax) + .export_values(); + py::enum_(simplex_constants, "SimplexPriceStrategy") + .value("kSimplexPriceStrategyMin", + SimplexPriceStrategy::kSimplexPriceStrategyMin) + .value("kSimplexPriceStrategyCol", + SimplexPriceStrategy::kSimplexPriceStrategyCol) + .value("kSimplexPriceStrategyRow", + SimplexPriceStrategy::kSimplexPriceStrategyRow) + .value("kSimplexPriceStrategyRowSwitch", + SimplexPriceStrategy::kSimplexPriceStrategyRowSwitch) + .value("kSimplexPriceStrategyRowSwitchColSwitch", + SimplexPriceStrategy::kSimplexPriceStrategyRowSwitchColSwitch) + .value("kSimplexPriceStrategyMax", + SimplexPriceStrategy::kSimplexPriceStrategyMax) + .export_values(); + py::enum_( + simplex_constants, "SimplexPivotalRowRefinementStrategy") + .value("kSimplexInfeasibilityProofRefinementMin", + SimplexPivotalRowRefinementStrategy:: + kSimplexInfeasibilityProofRefinementMin) + .value("kSimplexInfeasibilityProofRefinementNo", + SimplexPivotalRowRefinementStrategy:: + kSimplexInfeasibilityProofRefinementNo) + .value("kSimplexInfeasibilityProofRefinementUnscaledLp", + SimplexPivotalRowRefinementStrategy:: + kSimplexInfeasibilityProofRefinementUnscaledLp) + .value("kSimplexInfeasibilityProofRefinementAlsoScaledLp", + SimplexPivotalRowRefinementStrategy:: + kSimplexInfeasibilityProofRefinementAlsoScaledLp) + .value("kSimplexInfeasibilityProofRefinementMax", + SimplexPivotalRowRefinementStrategy:: + kSimplexInfeasibilityProofRefinementMax) + .export_values(); + py::enum_(simplex_constants, + "SimplexPrimalCorrectionStrategy") + .value( + "kSimplexPrimalCorrectionStrategyNone", + SimplexPrimalCorrectionStrategy::kSimplexPrimalCorrectionStrategyNone) + .value("kSimplexPrimalCorrectionStrategyInRebuild", + SimplexPrimalCorrectionStrategy:: + kSimplexPrimalCorrectionStrategyInRebuild) + .value("kSimplexPrimalCorrectionStrategyAlways", + SimplexPrimalCorrectionStrategy:: + kSimplexPrimalCorrectionStrategyAlways) + .export_values(); + py::enum_(simplex_constants, "SimplexNlaOperation") + .value("kSimplexNlaNull", SimplexNlaOperation::kSimplexNlaNull) + .value("kSimplexNlaBtranFull", SimplexNlaOperation::kSimplexNlaBtranFull) + .value("kSimplexNlaPriceFull", SimplexNlaOperation::kSimplexNlaPriceFull) + .value("kSimplexNlaBtranBasicFeasibilityChange", + SimplexNlaOperation::kSimplexNlaBtranBasicFeasibilityChange) + .value("kSimplexNlaPriceBasicFeasibilityChange", + SimplexNlaOperation::kSimplexNlaPriceBasicFeasibilityChange) + .value("kSimplexNlaBtranEp", SimplexNlaOperation::kSimplexNlaBtranEp) + .value("kSimplexNlaPriceAp", SimplexNlaOperation::kSimplexNlaPriceAp) + .value("kSimplexNlaFtran", SimplexNlaOperation::kSimplexNlaFtran) + .value("kSimplexNlaFtranBfrt", SimplexNlaOperation::kSimplexNlaFtranBfrt) + .value("kSimplexNlaFtranDse", SimplexNlaOperation::kSimplexNlaFtranDse) + .value("kSimplexNlaBtranPse", SimplexNlaOperation::kSimplexNlaBtranPse) + .value("kNumSimplexNlaOperation", + SimplexNlaOperation::kNumSimplexNlaOperation) + .export_values(); + py::enum_(simplex_constants, "EdgeWeightMode") + .value("kDantzig", EdgeWeightMode::kDantzig) + .value("kDevex", EdgeWeightMode::kDevex) + .value("kSteepestEdge", EdgeWeightMode::kSteepestEdge) + .value("kCount", EdgeWeightMode::kCount); } From 3612bac2472a73cd1570ae3ffa98c7a084543c98 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 1 Oct 2023 15:14:38 +0000 Subject: [PATCH 08/19] ENH: Add highs_options --- highspy/highs_bindings.cpp | 2 +- highspy/highs_options.cpp | 92 ++++++++++++++++++++++++++++++++++++++ highspy/meson.build | 20 ++++++--- meson.build | 7 ++- 4 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 highspy/highs_options.cpp diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index e2c3a65556..04e3c07755 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -539,7 +539,7 @@ std::tuple highs_getRowByName(Highs* h, return std::make_tuple(status, row); } -PYBIND11_MODULE(highspy, m) { +PYBIND11_MODULE(_highs, m) { // enum classes py::enum_(m, "ObjSense") .value("kMinimize", ObjSense::kMinimize) diff --git a/highspy/highs_options.cpp b/highspy/highs_options.cpp new file mode 100644 index 0000000000..05eb0f3d11 --- /dev/null +++ b/highspy/highs_options.cpp @@ -0,0 +1,92 @@ +#include +#include + +#include +#include + +#include "lp_data/HighsOptions.h" + +namespace py = pybind11; + +bool log_to_console = false; +bool output_flag = true; +HighsLogOptions highs_log_options = {nullptr, &output_flag, &log_to_console, + nullptr}; + +class HighsOptionsManager { + public: + HighsOptionsManager() { + for (const auto& record : highs_options_.records) { + record_type_lookup_.emplace(record->name, record->type); + } + } + + const HighsOptions& get_highs_options() const { return highs_options_; } + + const std::map& get_record_type_lookup() const { + return record_type_lookup_; + } + + template + bool check_option(const std::string& name, const T value) { + std::lock_guard guard(highs_options_mutex); + HighsInt idx = 0; + const OptionStatus idx_status = getOptionIndex( + highs_log_options, name.c_str(), highs_options_.records, idx); + + if (OptionStatus::kOk != idx_status) { + return false; + } + + OptionRecordType& record = + static_cast(*highs_options_.records.at(idx)); + const OptionStatus check_status = + checkOptionValue(highs_log_options, record, value); + if (OptionStatus::kIllegalValue == check_status) { + return false; + } + + return true; + } + + private: + HighsOptions highs_options_; + std::mutex highs_options_mutex; + std::map record_type_lookup_; +}; + +PYBIND11_MODULE(_highs_options, m) { + py::class_(m, "HighsOptionsManager") + .def(py::init<>()) + .def("get_option_type", + [](const HighsOptionsManager& manager, const std::string& name) { + const auto& lookup = manager.get_record_type_lookup().find(name); + if (manager.get_record_type_lookup().end() == lookup) { + return -1; + } + return static_cast(lookup->second); + }) + .def("get_all_option_types", &HighsOptionsManager::get_record_type_lookup) + .def("get_highs_options_records", + [](const HighsOptionsManager& manager) { + std::vector records_names; + for (const auto& record : manager.get_highs_options().records) { + records_names.push_back(record->name); + } + return records_names; + }) + .def("check_int_option", + [](HighsOptionsManager& self, const std::string& name, int value) { + return self.check_option(name, value); + }) + .def( + "check_double_option", + [](HighsOptionsManager& self, const std::string& name, double value) { + return self.check_option(name, value); + }) + .def("check_string_option", [](HighsOptionsManager& self, + const std::string& name, + const std::string& value) { + return self.check_option(name, value); + }); +} diff --git a/highspy/meson.build b/highspy/meson.build index aad6048a10..3a65f001de 100644 --- a/highspy/meson.build +++ b/highspy/meson.build @@ -5,12 +5,22 @@ pyb11_dep = [ dependency('pybind11') ] -highspyext = py.extension_module( - 'highspy', - sources : [ - 'highs_bindings.cpp', - ], +py.extension_module( + '_highs', + sources : highspy_cpp, dependencies: [pyb11_dep, highs_dep], cpp_args: _args, install: true, + subdir: 'highspy', + include_directories: _incdirs, +) + +py.extension_module( + '_highs_options', + sources : highsoptions_cpp, + dependencies: [pyb11_dep, highs_dep], + cpp_args: _args, + install: true, + subdir: 'highspy', + include_directories: _incdirs, ) diff --git a/meson.build b/meson.build index bf5a951958..64a3be3c19 100644 --- a/meson.build +++ b/meson.build @@ -134,7 +134,12 @@ if get_option('with_tests') endif # --------------------- Bindings - +highspy_cpp = files([ + 'highspy/highs_bindings.cpp' +]) +highsoptions_cpp = files([ + 'highspy/highs_options.cpp' +]) if get_option('with_pybind11') subdir('highspy') endif From c211c2f86bdba38485f701544a8906abfb948774 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 15 Oct 2023 00:49:19 +0000 Subject: [PATCH 09/19] MAINT: Rework call_highs_from_python for _highs --- examples/call_highs_from_python.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/call_highs_from_python.py b/examples/call_highs_from_python.py index 544b589f92..d7df77a49e 100644 --- a/examples/call_highs_from_python.py +++ b/examples/call_highs_from_python.py @@ -5,13 +5,13 @@ # The paths to MPS file instances assumes that this is run in the # directory of this file (ie highs/examples) or any other subdirectory # of HiGHS -import highspy +import highspy._highs import numpy as np -inf = highspy.kHighsInf +inf = highspy._highs.kHighsInf -h = highspy.Highs() +h = highspy._highs.Highs() alt_inf = h.getInfinity() -print('highspy.kHighsInf = ', inf, '; h.getInfinity() = ', alt_inf) +print('highspy._highs.kHighsInf = ', inf, '; h.getInfinity() = ', alt_inf) h.addVar(-inf, inf) h.addVar(-inf, inf) @@ -53,10 +53,10 @@ # Now define the blending model as a HighsLp instance # -lp = highspy.HighsLp() +lp = highspy._highs.HighsLp() lp.num_col_ = 2 lp.num_row_ = 2 -lp.sense_ = highspy.ObjSense.kMaximize +lp.sense_ = highspy._highs.ObjSense.kMaximize lp.col_cost_ = np.array([8, 10], dtype=np.double) lp.col_lower_ = np.array([0, 0], dtype=np.double) lp.col_upper_ = np.array([inf, inf], dtype=np.double) @@ -90,7 +90,7 @@ h.clear() # Now define the test-semi-definite0 model (from TestQpSolver.cpp) as a HighsModel instance # -model = highspy.HighsModel() +model = highspy._highs.HighsModel() model.lp_.model_name_ = "semi-definite" model.lp_.num_col_ = 3 model.lp_.num_row_ = 1 @@ -99,7 +99,7 @@ model.lp_.col_upper_ = np.array([inf, inf, inf], dtype=np.double) model.lp_.row_lower_ = np.array([2], dtype=np.double) model.lp_.row_upper_ = np.array([inf], dtype=np.double) -model.lp_.a_matrix_.format_ = highspy.MatrixFormat.kColwise +model.lp_.a_matrix_.format_ = highspy._highs.MatrixFormat.kColwise model.lp_.a_matrix_.start_ = np.array([0, 1, 2, 3]) model.lp_.a_matrix_.index_ = np.array([0, 0, 0]) model.lp_.a_matrix_.value_ = np.array([1.0, 1.0, 1.0], dtype=np.double) @@ -116,19 +116,19 @@ h.clear() num_col = 3 num_row = 1 -sense = highspy.ObjSense.kMinimize +sense = highspy._highs.ObjSense.kMinimize offset = 0 col_cost = np.array([1.0, 1.0, 2.0], dtype=np.double) col_lower = np.array([0, 0, 0], dtype=np.double) col_upper = np.array([inf, inf, inf], dtype=np.double) row_lower = np.array([2], dtype=np.double) row_upper = np.array([inf], dtype=np.double) -a_matrix_format = highspy.MatrixFormat.kColwise +a_matrix_format = highspy._highs.MatrixFormat.kColwise a_matrix_start = np.array([0, 1, 2, 3]) a_matrix_index = np.array([0, 0, 0]) a_matrix_value = np.array([1.0, 1.0, 1.0], dtype=np.double) a_matrix_num_nz = a_matrix_start[num_col] -hessian_format = highspy.HessianFormat.kTriangular +hessian_format = highspy._highs.HessianFormat.kTriangular hessian_start = np.array([0, 2, 2, 3]) hessian_index = np.array([0, 2, 2]) hessian_value = np.array([2.0, -1.0, 1.0], dtype=np.double) @@ -154,7 +154,7 @@ presolved_lp = h.getPresolvedLp() # Create a HiGHS instance to solve the presolved LP print('\nCreate Highs instance to solve presolved LP') -h1 = highspy.Highs() +h1 = highspy._highs.Highs() h1.passModel(presolved_lp) options = h1.getOptions() options.presolve = 'off' From 1814b09a71c79a8d0fe71656778a1393dc1208af Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 1 Oct 2023 16:57:52 +0000 Subject: [PATCH 10/19] MAINT: Increase character size (21 is the max) --- src/mip/HighsMipSolverData.cpp | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index fe032837ac..1e170a34c9 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -1045,9 +1045,9 @@ bool HighsMipSolverData::addIncumbent(const std::vector& sol, return true; } -static std::array convertToPrintString(int64_t val) { +static std::array convertToPrintString(int64_t val) { double l = std::log10(std::max(1.0, double(val))); - std::array printString; + std::array printString; switch (int(l)) { case 0: case 1: @@ -1055,23 +1055,23 @@ static std::array convertToPrintString(int64_t val) { case 3: case 4: case 5: - std::snprintf(printString.data(), 16, "%" PRId64, val); + std::snprintf(printString.data(), 22, "%" PRId64, val); break; case 6: case 7: case 8: - std::snprintf(printString.data(), 16, "%" PRId64 "k", val / 1000); + std::snprintf(printString.data(), 22, "%" PRId64 "k", val / 1000); break; default: - std::snprintf(printString.data(), 16, "%" PRId64 "m", val / 1000000); + std::snprintf(printString.data(), 22, "%" PRId64 "m", val / 1000000); } return printString; } -static std::array convertToPrintString(double val, +static std::array convertToPrintString(double val, const char* trailingStr = "") { - std::array printString; + std::array printString; double l = std::abs(val) == kHighsInf ? 0.0 : std::log10(std::max(1e-6, std::abs(val))); @@ -1080,23 +1080,23 @@ static std::array convertToPrintString(double val, case 1: case 2: case 3: - std::snprintf(printString.data(), 16, "%.10g%s", val, trailingStr); + std::snprintf(printString.data(), 22, "%.10g%s", val, trailingStr); break; case 4: - std::snprintf(printString.data(), 16, "%.11g%s", val, trailingStr); + std::snprintf(printString.data(), 22, "%.11g%s", val, trailingStr); break; case 5: - std::snprintf(printString.data(), 16, "%.12g%s", val, trailingStr); + std::snprintf(printString.data(), 22, "%.12g%s", val, trailingStr); break; case 6: case 7: case 8: case 9: case 10: - std::snprintf(printString.data(), 16, "%.13g%s", val, trailingStr); + std::snprintf(printString.data(), 22, "%.13g%s", val, trailingStr); break; default: - std::snprintf(printString.data(), 16, "%.9g%s", val, trailingStr); + std::snprintf(printString.data(), 22, "%.9g%s", val, trailingStr); } return printString; @@ -1139,10 +1139,10 @@ void HighsMipSolverData::printDisplayLine(char first) { ++num_disp_lines; - std::array print_nodes = convertToPrintString(num_nodes); - std::array queue_nodes = + std::array print_nodes = convertToPrintString(num_nodes); + std::array queue_nodes = convertToPrintString(nodequeue.numActiveNodes()); - std::array print_leaves = + std::array print_leaves = convertToPrintString(num_leaves - num_leaves_before_run); double explored = 100 * double(pruned_treeweight); @@ -1153,7 +1153,7 @@ void HighsMipSolverData::printDisplayLine(char first) { double ub = kHighsInf; double gap = kHighsInf; - std::array print_lp_iters = + std::array print_lp_iters = convertToPrintString(total_lp_iterations); if (upper_bound != kHighsInf) { ub = upper_bound + offset; @@ -1165,13 +1165,13 @@ void HighsMipSolverData::printDisplayLine(char first) { else gap = 100. * (ub - lb) / fabs(ub); - std::array gap_string; + std::array gap_string; if (gap >= 9999.) std::strcpy(gap_string.data(), "Large"); else std::snprintf(gap_string.data(), gap_string.size(), "%.2f%%", gap); - std::array ub_string; + std::array ub_string; if (mipsolver.options_mip_->objective_bound < ub) { ub = mipsolver.options_mip_->objective_bound; ub_string = @@ -1179,7 +1179,7 @@ void HighsMipSolverData::printDisplayLine(char first) { } else ub_string = convertToPrintString((int)mipsolver.orig_model_->sense_ * ub); - std::array lb_string = + std::array lb_string = convertToPrintString((int)mipsolver.orig_model_->sense_ * lb); highsLogUser( @@ -1192,7 +1192,7 @@ void HighsMipSolverData::printDisplayLine(char first) { cutpool.getNumCuts(), lp.numRows() - lp.getNumModelRows(), conflictPool.getNumConflicts(), print_lp_iters.data(), time); } else { - std::array ub_string; + std::array ub_string; if (mipsolver.options_mip_->objective_bound < ub) { ub = mipsolver.options_mip_->objective_bound; ub_string = @@ -1200,7 +1200,7 @@ void HighsMipSolverData::printDisplayLine(char first) { } else ub_string = convertToPrintString((int)mipsolver.orig_model_->sense_ * ub); - std::array lb_string = + std::array lb_string = convertToPrintString((int)mipsolver.orig_model_->sense_ * lb); highsLogUser( From 6c4e9f7edfb08428f80948e320d12071e703f02d Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 1 Oct 2023 17:58:59 +0000 Subject: [PATCH 11/19] MAINT: Add the cpp args --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 64a3be3c19..e0bec32063 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,7 @@ if host_system == 'linux' _args += '-Wno-return-type' _args += '-Wno-switch' _args += '-Wno-unused-variable' + _args += '-Wno-unused-but-set-variable' _args += '-Wno-unused-const-variable' endif From c5e3fc594e8e8c71430798152b66956b4be921be Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 1 Oct 2023 18:08:59 +0000 Subject: [PATCH 12/19] MAINT: Use c_args too --- src/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/src/meson.build b/src/meson.build index 1953c5af66..414ac03088 100644 --- a/src/meson.build +++ b/src/meson.build @@ -278,6 +278,7 @@ highslib = library('highs', highslib_srcs, dependencies: _deps, cpp_args: _args, + c_args: _args, link_with: _linkto, include_directories: _incdirs, pic: true, From a42c59853c1122d54cf09069387bf78fbaf65614 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 7 Oct 2023 13:08:01 +0000 Subject: [PATCH 13/19] MAINT: Bump meson version and fix pip installation Co-authored-by: rgommers --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e2170cb63f..a442d61d76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ setup = ['-Dwith_pybind11=True', # ^-- collects pybind11, see https://github.com/ERGO-Code/HiGHS/pull/1343#discussion_r1252446966 ] dist = ['--include-subprojects'] +install = ['--skip-subprojects'] [tool.cibuildwheel] build = "*" From 94f53c5eef949ea4df671afb9cb4caa584debe00 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sat, 14 Oct 2023 21:41:44 +0000 Subject: [PATCH 14/19] BLD: Bring in the atomic_dep from SciPy --- meson.build | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/meson.build b/meson.build index e0bec32063..ba6a2c7647 100644 --- a/meson.build +++ b/meson.build @@ -84,6 +84,45 @@ threads_dep = dependency('threads', required: true) _deps += threads_dep +# Determine whether it is necessary to link libatomic. This could be the case +# e.g. on 32-bit platforms when atomic operations are used on 64-bit types. +# The check is copied from SciPy which in turn came from +# Mesa . +null_dep = dependency('', required : false) +atomic_dep = null_dep +code_non_lockfree = ''' + #include + int main() { + struct { + uint64_t *v; + } x; + return (int)__atomic_load_n(x.v, __ATOMIC_ACQUIRE) & + (int)__atomic_add_fetch(x.v, (uint64_t)1, __ATOMIC_ACQ_REL); + } +''' +if cc.get_id() != 'msvc' + if not cc.links( + code_non_lockfree, + name : 'Check atomic builtins without -latomic' + ) + atomic_dep = cc.find_library('atomic', required: false) + if atomic_dep.found() + # From SciPy + # We're not sure that with `-latomic` things will work for all compilers, + # so verify and only keep libatomic as a dependency if this works. + if not cc.links( + code_non_lockfree, + dependencies: atomic_dep, + name : 'Check atomic builtins with -latomic' + ) + atomic_dep = null_dep + endif + endif + endif +endif + +_deps += atomic_dep + # Optional zlib_dep = dependency('zlib', required: get_option('use_zlib')) From 39a892e82b89c32bbf39de1634eff4545a1db423 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 10 Sep 2023 22:44:34 +0000 Subject: [PATCH 15/19] MAINT: Rework to HIGHS_INT64 work with pybind11 --- highspy/highs_bindings.cpp | 54 ++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index 04e3c07755..1a674c3da2 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -42,20 +42,22 @@ HighsStatus highs_passModelPointers( const double* col_upper_ptr = static_cast(col_upper_info.ptr); const double* row_lower_ptr = static_cast(row_lower_info.ptr); const double* row_upper_ptr = static_cast(row_upper_info.ptr); + const double* a_value_ptr = static_cast(a_value_info.ptr); + const double* q_value_ptr = static_cast(q_value_info.ptr); const HighsInt* a_start_ptr = static_cast(a_start_info.ptr); const HighsInt* a_index_ptr = static_cast(a_index_info.ptr); - const double* a_value_ptr = static_cast(a_value_info.ptr); const HighsInt* q_start_ptr = static_cast(q_start_info.ptr); const HighsInt* q_index_ptr = static_cast(q_index_info.ptr); - const double* q_value_ptr = static_cast(q_value_info.ptr); const HighsInt* integrality_ptr = static_cast(integrality_info.ptr); - return h->passModel(num_col, num_row, num_nz, q_num_nz, a_format, q_format, - sense, offset, col_cost_ptr, col_lower_ptr, col_upper_ptr, - row_lower_ptr, row_upper_ptr, a_start_ptr, a_index_ptr, - a_value_ptr, q_start_ptr, q_index_ptr, q_value_ptr, - integrality_ptr); + return h->passModel( + static_cast(num_col), static_cast(num_row), + static_cast(num_nz), static_cast(q_num_nz), + static_cast(a_format), static_cast(q_format), + static_cast(sense), offset, col_cost_ptr, col_lower_ptr, + col_upper_ptr, row_lower_ptr, row_upper_ptr, a_start_ptr, a_index_ptr, + a_value_ptr, q_start_ptr, q_index_ptr, q_value_ptr, integrality_ptr); } HighsStatus highs_passLp(Highs* h, HighsLp& lp) { return h->passModel(lp); } @@ -90,10 +92,12 @@ HighsStatus highs_passLpPointers( const HighsInt* integrality_ptr = static_cast(integrality_info.ptr); - return h->passModel(num_col, num_row, num_nz, a_format, sense, offset, - col_cost_ptr, col_lower_ptr, col_upper_ptr, row_lower_ptr, - row_upper_ptr, a_start_ptr, a_index_ptr, a_value_ptr, - integrality_ptr); + return h->passModel( + static_cast(num_col), static_cast(num_row), + static_cast(num_nz), static_cast(a_format), + static_cast(sense), offset, col_cost_ptr, col_lower_ptr, + col_upper_ptr, row_lower_ptr, row_upper_ptr, a_start_ptr, a_index_ptr, + a_value_ptr, integrality_ptr); } HighsStatus highs_passHessian(Highs* h, HighsHessian& hessian) { @@ -148,7 +152,7 @@ HighsStatus highs_addRow(Highs* h, double lower, double upper, py::buffer_info indices_info = indices.request(); py::buffer_info values_info = values.request(); - HighsInt* indices_ptr = static_cast(indices_info.ptr); + HighsInt* indices_ptr = reinterpret_cast(indices_info.ptr); double* values_ptr = static_cast(values_info.ptr); return h->addRow(lower, upper, num_new_nz, indices_ptr, values_ptr); @@ -167,8 +171,8 @@ HighsStatus highs_addRows(Highs* h, HighsInt num_row, py::array_t lower, double* lower_ptr = static_cast(lower_info.ptr); double* upper_ptr = static_cast(upper_info.ptr); - HighsInt* starts_ptr = static_cast(starts_info.ptr); - HighsInt* indices_ptr = static_cast(indices_info.ptr); + HighsInt* starts_ptr = reinterpret_cast(starts_info.ptr); + HighsInt* indices_ptr = reinterpret_cast(indices_info.ptr); double* values_ptr = static_cast(values_info.ptr); return h->addRows(num_row, lower_ptr, upper_ptr, num_new_nz, starts_ptr, @@ -181,7 +185,7 @@ HighsStatus highs_addCol(Highs* h, double cost, double lower, double upper, py::buffer_info indices_info = indices.request(); py::buffer_info values_info = values.request(); - HighsInt* indices_ptr = static_cast(indices_info.ptr); + HighsInt* indices_ptr = reinterpret_cast(indices_info.ptr); double* values_ptr = static_cast(values_info.ptr); return h->addCol(cost, lower, upper, num_new_nz, indices_ptr, values_ptr); @@ -202,8 +206,8 @@ HighsStatus highs_addCols(Highs* h, HighsInt num_col, py::array_t cost, double* cost_ptr = static_cast(cost_info.ptr); double* lower_ptr = static_cast(lower_info.ptr); double* upper_ptr = static_cast(upper_info.ptr); - HighsInt* starts_ptr = static_cast(starts_info.ptr); - HighsInt* indices_ptr = static_cast(indices_info.ptr); + HighsInt* starts_ptr = reinterpret_cast(starts_info.ptr); + const HighsInt* indices_ptr = reinterpret_cast(indices_info.ptr); double* values_ptr = static_cast(values_info.ptr); return h->addCols(num_col, cost_ptr, lower_ptr, upper_ptr, num_new_nz, @@ -363,7 +367,8 @@ std::tuple highs_getCol( double cost, lower, upper; HighsInt get_num_col; HighsInt get_num_nz; - HighsStatus status = h->getCols(1, &col, get_num_col, &cost, &lower, &upper, + HighsInt col_ = static_cast(col); + HighsStatus status = h->getCols(1, &col_, get_num_col, &cost, &lower, &upper, get_num_nz, nullptr, nullptr, nullptr); return std::make_tuple(status, cost, lower, upper, get_num_nz); } @@ -373,7 +378,8 @@ highs_getColEntries(Highs* h, HighsInt col) { double cost, lower, upper; HighsInt get_num_col; HighsInt get_num_nz; - h->getCols(1, &col, get_num_col, nullptr, nullptr, nullptr, get_num_nz, + HighsInt col_ = static_cast(col); + h->getCols(1, &col_, get_num_col, nullptr, nullptr, nullptr, get_num_nz, nullptr, nullptr, nullptr); get_num_nz = get_num_nz > 0 ? get_num_nz : 1; HighsInt start; @@ -382,7 +388,7 @@ highs_getColEntries(Highs* h, HighsInt col) { HighsInt* index_ptr = static_cast(index.data()); double* value_ptr = static_cast(value.data()); HighsStatus status = - h->getCols(1, &col, get_num_col, nullptr, nullptr, nullptr, get_num_nz, + h->getCols(1, &col_, get_num_col, nullptr, nullptr, nullptr, get_num_nz, &start, index_ptr, value_ptr); return std::make_tuple(status, py::cast(index), py::cast(value)); } @@ -392,7 +398,8 @@ std::tuple highs_getRow(Highs* h, double cost, lower, upper; HighsInt get_num_row; HighsInt get_num_nz; - HighsStatus status = h->getRows(1, &row, get_num_row, &lower, &upper, + HighsInt row_ = static_cast(row); + HighsStatus status = h->getRows(1, &row_, get_num_row, &lower, &upper, get_num_nz, nullptr, nullptr, nullptr); return std::make_tuple(status, lower, upper, get_num_nz); } @@ -402,7 +409,8 @@ highs_getRowEntries(Highs* h, HighsInt row) { double cost, lower, upper; HighsInt get_num_row; HighsInt get_num_nz; - h->getRows(1, &row, get_num_row, nullptr, nullptr, get_num_nz, nullptr, + HighsInt row_ = static_cast(row); + h->getRows(1, &row_, get_num_row, nullptr, nullptr, get_num_nz, nullptr, nullptr, nullptr); get_num_nz = get_num_nz > 0 ? get_num_nz : 1; HighsInt start; @@ -410,7 +418,7 @@ highs_getRowEntries(Highs* h, HighsInt row) { std::vector value(get_num_nz); HighsInt* index_ptr = static_cast(index.data()); double* value_ptr = static_cast(value.data()); - HighsStatus status = h->getRows(1, &row, get_num_row, nullptr, nullptr, + HighsStatus status = h->getRows(1, &row_, get_num_row, nullptr, nullptr, get_num_nz, &start, index_ptr, value_ptr); return std::make_tuple(status, py::cast(index), py::cast(value)); } From 4a3bf393dc6c45385c56c98e33e064b577e99b40 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 15 Oct 2023 03:12:13 +0000 Subject: [PATCH 16/19] MAINT: Update for newer meson version --- check/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check/meson.build b/check/meson.build index ca4478c28b..f02b350f48 100644 --- a/check/meson.build +++ b/check/meson.build @@ -1,7 +1,7 @@ # -------------------------- Configuration test_data_conf = configuration_data() test_data_conf.set_quoted('HIGHS_DIR', - meson.source_root()) + meson.project_source_root()) configure_file( input: 'HCheckConfig.h.meson.in', output: 'HCheckConfig.h', From 8728eb14205e379d73fd80b92443505ef073c816 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 15 Oct 2023 03:12:27 +0000 Subject: [PATCH 17/19] CI: Rework to test the python_api --- .github/workflows/build-meson.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-meson.yml b/.github/workflows/build-meson.yml index 8183005325..b53c4e4aa5 100644 --- a/.github/workflows/build-meson.yml +++ b/.github/workflows/build-meson.yml @@ -31,11 +31,13 @@ jobs: - name: Build and test shell: bash -l {0} run: | - meson setup bbdir_test -Duse_zlib=enabled -Dwith_tests=True --prefix $CONDA_PREFIX - meson test -C bbdir_test + meson setup bbdir -Duse_zlib=enabled -Dwith_tests=True + meson test -C bbdir - name: Test compiled highspy shell: bash -l {0} run: | - meson configure bbdir_test -Dwith_pybind11=True - meson compile -C bbdir_test - PYTHONPATH=$(pwd)/bbdir_test/highspy python examples/call_highs_from_python.py + meson configure bbdir -Dwith_pybind11=True + meson compile -C bbdir + LD_LIBRARY_PATH=$(pwd)/bbdir/src \ + PYTHONPATH=$(pwd)/bbdir \ + python examples/call_highs_from_python.py From 947b47ac12052f60fcaeca643ac10b7c3c9f9e00 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 15 Oct 2023 12:09:21 +0000 Subject: [PATCH 18/19] BLD: Be more 32-bit friendly --- meson.build | 3 ++- src/meson.build | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index ba6a2c7647..9dbe0c1680 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,8 @@ project('highs', 'cpp', meson_version: '>= 1.2.0', default_options : ['warning_level=1', 'cpp_std=c++17', - 'wrap_mode=forcefallback']) + 'wrap_mode=forcefallback', + 'highsint64=false']) # Add C++ compiler options diff --git a/src/meson.build b/src/meson.build index 414ac03088..589b802187 100644 --- a/src/meson.build +++ b/src/meson.build @@ -10,8 +10,14 @@ conf_data.set('HIGHS_VERSION_PATCH', meson.project_version().split('.')[2]) conf_data.set('ZLIB_FOUND', zlib_dep.found()) -conf_data.set('HIGHSINT64', - get_option('highsint64')) +# 64 bit numbers +if host_machine.cpu_family() == 'x86_64' + # Get user's option, if it's not provided, enable highsint64 by default on x86_64 + highsint64_opt = get_option('highsint64') + conf_data.set('HIGHSINT64', highsint64_opt) +else + conf_data.set('HIGHSINT64', false) +endif # Commit hash commit_hash_cmd = run_command('git', 'rev-parse', '--short', 'HEAD', From 704a264a4123178ab09776864ba5bcbbdb69e3f7 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Sun, 15 Oct 2023 12:34:46 +0000 Subject: [PATCH 19/19] BLD: Be more robust w.r.t git --- src/meson.build | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/meson.build b/src/meson.build index 589b802187..341c63a42c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -19,12 +19,18 @@ else conf_data.set('HIGHSINT64', false) endif # Commit hash -commit_hash_cmd = run_command('git', 'rev-parse', - '--short', 'HEAD', - check: true) -commit_hash = commit_hash_cmd.stdout().strip() -conf_data.set_quoted('HIGHS_GITHASH', - commit_hash) +commit_hash = 'unknown' # Default value +git = find_program('git', required: false) +if git.found() + commit_hash_cmd = run_command(git, 'rev-parse', '--short', + '-C', meson.project_source_root(), 'HEAD', + check: false, # Don't abort on failure + ) + if commit_hash_cmd.returncode() == 0 + commit_hash = commit_hash_cmd.stdout().strip() + endif +endif +conf_data.set_quoted('HIGHS_GITHASH', commit_hash) # Date python_getdate = ''' import datetime