From 19be80066b4cdd2a51e2bd42b32938b73729a8f6 Mon Sep 17 00:00:00 2001 From: Nicholas McKibben Date: Wed, 14 Dec 2022 21:04:11 -0700 Subject: [PATCH 1/4] WIP: scipy callback concept for IPX --- CMakeLists.txt | 4 ++-- check/TestIpx.cpp | 2 +- src/Highs.h | 6 ++++++ src/ipm/IpxWrapper.cpp | 8 +++++--- src/ipm/IpxWrapper.h | 3 ++- src/ipm/ipx/ipm.cc | 3 ++- src/ipm/ipx/ipm.h | 4 +++- src/ipm/ipx/ipx_c.cc | 2 +- src/ipm/ipx/ipx_internal.h | 1 + src/ipm/ipx/lp_solver.cc | 22 +++++++++++----------- src/ipm/ipx/lp_solver.h | 12 +++++++----- src/lp_data/Highs.cpp | 8 +++++--- src/lp_data/HighsInterface.cpp | 3 ++- src/lp_data/HighsLpSolverObject.h | 10 ++++++++-- src/scipy.h | 18 ++++++++++++++++++ 15 files changed, 74 insertions(+), 32 deletions(-) create mode 100644 src/scipy.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2004e9c322..940f15cbc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,11 @@ cmake_minimum_required(VERSION 3.15) # set preference for clang compiler and intel compiler over gcc and other compilers include(Platform/${CMAKE_SYSTEM_NAME}-Determine-C OPTIONAL) include(Platform/${CMAKE_SYSTEM_NAME}-C OPTIONAL) -set(CMAKE_C_COMPILER_NAMES clang icc cc ${CMAKE_C_COMPILER_NAMES}) +set(CMAKE_C_COMPILER_NAMES cc clang icc ${CMAKE_C_COMPILER_NAMES}) include(Platform/${CMAKE_SYSTEM_NAME}-Determine-CXX OPTIONAL) include(Platform/${CMAKE_SYSTEM_NAME}-CXX OPTIONAL) -set(CMAKE_CXX_COMPILER_NAMES clang++ icpc c++ ${CMAKE_CXX_COMPILER_NAMES}) +set(CMAKE_CXX_COMPILER_NAMES c++ clang++ icpc ${CMAKE_CXX_COMPILER_NAMES}) # set default build type before project call, as it otherwise seems to fail for some plattforms if(NOT CMAKE_BUILD_TYPE) diff --git a/check/TestIpx.cpp b/check/TestIpx.cpp index 42f7bad20c..725eb338e8 100644 --- a/check/TestIpx.cpp +++ b/check/TestIpx.cpp @@ -52,7 +52,7 @@ TEST_CASE("test-ipx", "[highs_ipx]") { highs::parallel::initialize_scheduler(); - Int status = lps.Solve(); + Int status = lps.Solve(+[](ipx::Info*){}); bool is_solved = status == IPX_STATUS_solved; REQUIRE(is_solved); diff --git a/src/Highs.h b/src/Highs.h index db4ec90a78..013e871ca1 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -25,6 +25,8 @@ #include "presolve/ICrash.h" #include "presolve/PresolveComponent.h" +#include "scipy.h" + /** * @brief Class to set parameters and run HiGHS */ @@ -1115,6 +1117,10 @@ class Highs { bool written_log_header = false; + scipy::clbk_t scipy_clbk_ = +[](ipx::Info* info){ + std::cout << info->iter << "\n"; + }; + void exactResizeModel() { this->model_.lp_.exactResize(); this->model_.hessian_.exactResize(); diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 37367cacd2..13b8cb4127 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -26,7 +26,8 @@ using std::min; HighsStatus solveLpIpx(HighsLpSolverObject& solver_object) { return solveLpIpx(solver_object.options_, solver_object.timer_, solver_object.lp_, solver_object.basis_, solver_object.solution_, - solver_object.model_status_, solver_object.highs_info_); + solver_object.model_status_, solver_object.highs_info_, + solver_object.scipy_clbk_); } HighsStatus solveLpIpx(const HighsOptions& options, @@ -35,7 +36,8 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsBasis& highs_basis, HighsSolution& highs_solution, HighsModelStatus& model_status, - HighsInfo& highs_info) { + HighsInfo& highs_info, + scipy::clbk_t scipy_clbk) { // Use IPX to try to solve the LP // // Can return HighsModelStatus (HighsStatus) values: @@ -141,7 +143,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, } // Use IPX to solve the LP! - ipx::Int solve_status = lps.Solve(); + ipx::Int solve_status = lps.Solve(scipy_clbk); const bool report_solve_data = kHighsAnalysisLevelSolverSummaryData & options.highs_analysis_level; // Get solver and solution information. diff --git a/src/ipm/IpxWrapper.h b/src/ipm/IpxWrapper.h index c2e8cab6b2..ea34c6d2d3 100644 --- a/src/ipm/IpxWrapper.h +++ b/src/ipm/IpxWrapper.h @@ -29,7 +29,8 @@ HighsStatus solveLpIpx(HighsLpSolverObject& solver_object); HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, HighsBasis& highs_basis, HighsSolution& highs_solution, - HighsModelStatus& model_status, HighsInfo& highs_info); + HighsModelStatus& model_status, HighsInfo& highs_info, + scipy::clbk_t scipy_clbk); void fillInIpxData(const HighsLp& lp, ipx::Int& num_col, ipx::Int& num_row, std::vector& obj, std::vector& col_lb, diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index db5e4910fe..822aade40a 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -39,7 +39,7 @@ void IPM::StartingPoint(KKTSolver* kkt, Iterate* iterate, Info* info) { } } -void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { +void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info, scipy::clbk_t scipy_clbk) { const Model& model = iterate->model(); const Int m = model.rows(); const Int n = model.cols(); @@ -74,6 +74,7 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info) { if (info->errflag) break; MakeStep(step); + scipy_clbk(info); info->iter++; PrintOutput(); } diff --git a/src/ipm/ipx/ipm.h b/src/ipm/ipx/ipm.h index a928cae9d0..0358735b59 100644 --- a/src/ipm/ipx/ipm.h +++ b/src/ipm/ipx/ipm.h @@ -5,6 +5,8 @@ #include "ipm/ipx/kkt_solver.h" #include "ipm/ipx/iterate.h" +#include "scipy.h" + namespace ipx { // IPM implements an interior point method based on KKTSolver and Iterate. @@ -33,7 +35,7 @@ class IPM { // IPX_STATUS_no_progress if no progress over a number of iterations, // IPX_STATUS_time_limit if interrupted by time limit, // IPX_STATUS_failed if the KKT solver failed with info->errflag. - void Driver(KKTSolver* kkt, Iterate* iterate, Info* info); + void Driver(KKTSolver* kkt, Iterate* iterate, Info* info, scipy::clbk_t scipy_clbk); Int maxiter() const { return maxiter_; } void maxiter(Int i) { maxiter_ = i; } diff --git a/src/ipm/ipx/ipx_c.cc b/src/ipm/ipx/ipx_c.cc index 91e885692e..db475e18e9 100644 --- a/src/ipm/ipx/ipx_c.cc +++ b/src/ipm/ipx/ipx_c.cc @@ -77,7 +77,7 @@ ipxint ipx_load_ipm_starting_point(void* self, const double* x, ipxint ipx_solve(void* self) { LpSolver* solver = static_cast(self); - return solver->Solve(); + return solver->Solve(nullptr); } struct ipx_info ipx_get_info(void* self) { diff --git a/src/ipm/ipx/ipx_internal.h b/src/ipm/ipx/ipx_internal.h index 46bceb3e22..1c1a87625f 100644 --- a/src/ipm/ipx/ipx_internal.h +++ b/src/ipm/ipx/ipx_internal.h @@ -15,6 +15,7 @@ using Int = ipxint; struct Info : public ipx_info { Info() { std::memset(this, 0, sizeof(Info)); } + }; struct Parameters : public ipx_parameters { diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index d31adf3f92..6f7d8b6799 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -47,7 +47,7 @@ Int LpSolver::LoadIPMStartingPoint(const double* x, const double* xl, return 0; } -Int LpSolver::Solve() { +Int LpSolver::Solve(scipy::clbk_t scipy_clbk) { if (model_.empty()) return info_.status = IPX_STATUS_no_model; ClearSolution(); @@ -55,7 +55,7 @@ Int LpSolver::Solve() { control_.OpenLogfile(); control_.Log() << "IPX version 1.0\n"; try { - InteriorPointSolve(); + InteriorPointSolve(scipy_clbk); const bool run_crossover_on = control_.run_crossover() == 1; const bool run_crossover_choose = control_.run_crossover() == -1; const bool run_crossover_not_off = run_crossover_choose || run_crossover_on; @@ -346,7 +346,7 @@ void LpSolver::ClearSolution() { } -void LpSolver::InteriorPointSolve() { +void LpSolver::InteriorPointSolve(scipy::clbk_t scipy_clbk) { control_.Log() << "Interior Point Solve\n"; // Allocate new iterate and set tolerances for IPM termination test. @@ -356,7 +356,7 @@ void LpSolver::InteriorPointSolve() { if (control_.run_crossover()) iterate_->start_crossover_tol(control_.start_crossover_tol()); - RunIPM(); + RunIPM(scipy_clbk); iterate_->Postprocess(); iterate_->EvaluatePostsolved(&info_); @@ -371,7 +371,7 @@ void LpSolver::InteriorPointSolve() { } } -void LpSolver::RunIPM() { +void LpSolver::RunIPM(scipy::clbk_t scipy_clbk) { IPM ipm(control_); if (x_start_.size() != 0) { @@ -384,14 +384,14 @@ void LpSolver::RunIPM() { ComputeStartingPoint(ipm); if (info_.status_ipm != IPX_STATUS_not_run) return; - RunInitialIPM(ipm); + RunInitialIPM(ipm, scipy_clbk); if (info_.status_ipm != IPX_STATUS_not_run) return; } BuildStartingBasis(); if (info_.status_ipm != IPX_STATUS_not_run) return; - RunMainIPM(ipm); + RunMainIPM(ipm, scipy_clbk); } void LpSolver::MakeIPMStartingPointValid() { @@ -460,7 +460,7 @@ void LpSolver::ComputeStartingPoint(IPM& ipm) { info_.time_ipm1 += timer.Elapsed(); } -void LpSolver::RunInitialIPM(IPM& ipm) { +void LpSolver::RunInitialIPM(IPM& ipm, scipy::clbk_t scipy_clbk) { Timer timer; KKTSolverDiag kkt(control_, model_); @@ -474,7 +474,7 @@ void LpSolver::RunInitialIPM(IPM& ipm) { } else { ipm.maxiter(std::min(switchiter, control_.ipm_maxiter())); } - ipm.Driver(&kkt, iterate_.get(), &info_); + ipm.Driver(&kkt, iterate_.get(), &info_, scipy_clbk); switch (info_.status_ipm) { case IPX_STATUS_optimal: // If the IPM reached its termination criterion in the initial @@ -532,11 +532,11 @@ void LpSolver::BuildStartingBasis() { } } -void LpSolver::RunMainIPM(IPM& ipm) { +void LpSolver::RunMainIPM(IPM& ipm, scipy::clbk_t scipy_clbk) { KKTSolverBasis kkt(control_, *basis_); Timer timer; ipm.maxiter(control_.ipm_maxiter()); - ipm.Driver(&kkt, iterate_.get(), &info_); + ipm.Driver(&kkt, iterate_.get(), &info_, scipy_clbk); info_.time_ipm2 = timer.Elapsed(); } diff --git a/src/ipm/ipx/lp_solver.h b/src/ipm/ipx/lp_solver.h index d9d6b798fb..1281488a92 100644 --- a/src/ipm/ipx/lp_solver.h +++ b/src/ipm/ipx/lp_solver.h @@ -8,6 +8,8 @@ #include "ipm/ipx/iterate.h" #include "ipm/ipx/model.h" +#include "scipy.h" + namespace ipx { class LpSolver { @@ -68,7 +70,7 @@ class LpSolver { // Solves the model that is currently loaded in the object. // Returns GetInfo().status. - Int Solve(); + Int Solve(scipy::clbk_t scipy_clbk); // Returns the solver info from the last call to Solve(). See the reference // documentation for the meaning of Info values. @@ -161,13 +163,13 @@ class LpSolver { private: void ClearSolution(); - void InteriorPointSolve(); - void RunIPM(); + void InteriorPointSolve(scipy::clbk_t scipy_clbk); + void RunIPM(scipy::clbk_t scipy_clbk); void MakeIPMStartingPointValid(); void ComputeStartingPoint(IPM& ipm); - void RunInitialIPM(IPM& ipm); + void RunInitialIPM(IPM& ipm, scipy::clbk_t scipy_clbk); void BuildStartingBasis(); - void RunMainIPM(IPM& ipm); + void RunMainIPM(IPM& ipm, scipy::clbk_t scipy_clbk); void BuildCrossoverStartingPoint(); void RunCrossover(); void PrintSummary(); diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index db76a2d0c7..37715ddcd7 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1428,7 +1428,8 @@ HighsStatus Highs::getRanging() { // Create a HighsLpSolverObject of references to data in the Highs // class, and the scaled/unscaled model status HighsLpSolverObject solver_object(model_.lp_, basis_, solution_, info_, - ekk_instance_, options_, timer_); + ekk_instance_, options_, timer_, + scipy_clbk_); solver_object.model_status_ = model_status_; return getRangingData(this->ranging_, solver_object); } @@ -1730,7 +1731,8 @@ HighsStatus Highs::setBasis(const HighsBasis& basis, HighsBasis modifiable_basis = basis; modifiable_basis.was_alien = true; HighsLpSolverObject solver_object(model_.lp_, modifiable_basis, solution_, - info_, ekk_instance_, options_, timer_); + info_, ekk_instance_, options_, timer_, + scipy_clbk_); HighsStatus return_status = formSimplexLpBasisAndFactor(solver_object); if (return_status != HighsStatus::kOk) return HighsStatus::kError; // Update the HiGHS basis @@ -2752,7 +2754,7 @@ HighsStatus Highs::callSolveLp(HighsLp& lp, const string message) { // Create a HighsLpSolverObject of references to data in the Highs // class, and the scaled/unscaled model status HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, - options_, timer_); + options_, timer_, scipy_clbk_); // Check that the model is column-wise assert(model_.lp_.a_matrix_.isColwise()); diff --git a/src/lp_data/HighsInterface.cpp b/src/lp_data/HighsInterface.cpp index cb7b59f24b..97acf48d10 100644 --- a/src/lp_data/HighsInterface.cpp +++ b/src/lp_data/HighsInterface.cpp @@ -1093,7 +1093,8 @@ HighsStatus Highs::getBasicVariablesInterface(HighsInt* basic_variables) { // // Create a HighsLpSolverObject HighsLpSolverObject solver_object(lp, basis_, solution_, info_, - ekk_instance_, options_, timer_); + ekk_instance_, options_, timer_, + scipy_clbk_); const bool only_from_known_basis = true; return_status = interpretCallStatus( options_.log_options, diff --git a/src/lp_data/HighsLpSolverObject.h b/src/lp_data/HighsLpSolverObject.h index 8900e56b2b..dbb23c8966 100644 --- a/src/lp_data/HighsLpSolverObject.h +++ b/src/lp_data/HighsLpSolverObject.h @@ -20,18 +20,22 @@ #include "lp_data/HighsOptions.h" #include "simplex/HEkk.h" +#include "scipy.h" + class HighsLpSolverObject { public: HighsLpSolverObject(HighsLp& lp, HighsBasis& basis, HighsSolution& solution, HighsInfo& highs_info, HEkk& ekk_instance, - HighsOptions& options, HighsTimer& timer) + HighsOptions& options, HighsTimer& timer, + scipy::clbk_t scipy_clbk) : lp_(lp), basis_(basis), solution_(solution), highs_info_(highs_info), ekk_instance_(ekk_instance), options_(options), - timer_(timer) {} + timer_(timer), + scipy_clbk_(scipy_clbk) {} HighsLp& lp_; HighsBasis& basis_; @@ -42,6 +46,8 @@ class HighsLpSolverObject { HighsTimer& timer_; HighsModelStatus model_status_ = HighsModelStatus::kNotset; + + scipy::clbk_t scipy_clbk_; }; #endif // LP_DATA_HIGHS_LP_SOLVER_OBJECT_H_ diff --git a/src/scipy.h b/src/scipy.h new file mode 100644 index 0000000000..c72783f05b --- /dev/null +++ b/src/scipy.h @@ -0,0 +1,18 @@ +// +// Created by nmckibben on 12/14/22. +// + +#ifndef HIGHS_SCIPY_H +#define HIGHS_SCIPY_H + +namespace ipx { + class Info; +} + +namespace scipy { + typedef void(*clbk_t)(ipx::Info*); +} + +#include "ipm.h" + +#endif // HIGHS_SCIPY_H From 6e16fadf20d1cc0a04891e1994dfe05940d4113e Mon Sep 17 00:00:00 2001 From: Nicholas McKibben Date: Sat, 17 Dec 2022 10:51:10 -0700 Subject: [PATCH 2/4] WIP: refactor to make HighsClbk first-class def --- check/TestIpx.cpp | 2 +- src/Highs.h | 7 +++---- src/ipm/IpxWrapper.cpp | 8 +++++--- src/ipm/IpxWrapper.h | 2 +- src/ipm/ipx/ipm.cc | 10 ++++++++-- src/ipm/ipx/ipm.h | 8 ++++---- src/ipm/ipx/ipx_c.cc | 2 +- src/ipm/ipx/ipx_internal.h | 2 ++ src/ipm/ipx/ipx_parameters.h | 2 ++ src/ipm/ipx/lp_solver.cc | 22 +++++++++++----------- src/ipm/ipx/lp_solver.h | 12 +++++------- src/lp_data/Highs.cpp | 6 +++--- src/lp_data/HighsInterface.cpp | 2 +- src/lp_data/HighsLpSolverObject.h | 10 ++++------ src/scipy.h | 18 ------------------ src/util/HighsClbk.h | 17 +++++++++++++++++ 16 files changed, 68 insertions(+), 62 deletions(-) delete mode 100644 src/scipy.h create mode 100644 src/util/HighsClbk.h diff --git a/check/TestIpx.cpp b/check/TestIpx.cpp index 725eb338e8..42f7bad20c 100644 --- a/check/TestIpx.cpp +++ b/check/TestIpx.cpp @@ -52,7 +52,7 @@ TEST_CASE("test-ipx", "[highs_ipx]") { highs::parallel::initialize_scheduler(); - Int status = lps.Solve(+[](ipx::Info*){}); + Int status = lps.Solve(); bool is_solved = status == IPX_STATUS_solved; REQUIRE(is_solved); diff --git a/src/Highs.h b/src/Highs.h index 013e871ca1..40db27bf7c 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -18,6 +18,7 @@ #include +#include "HighsClbk.h" #include "lp_data/HighsLpUtils.h" #include "lp_data/HighsRanging.h" #include "lp_data/HighsSolutionDebug.h" @@ -25,8 +26,6 @@ #include "presolve/ICrash.h" #include "presolve/PresolveComponent.h" -#include "scipy.h" - /** * @brief Class to set parameters and run HiGHS */ @@ -1117,8 +1116,8 @@ class Highs { bool written_log_header = false; - scipy::clbk_t scipy_clbk_ = +[](ipx::Info* info){ - std::cout << info->iter << "\n"; + HighsClbk clbk_fun_ = +[](HighsClbkInfo* info){ + std::cout << "iteration: " << info->iteration_ << "\n"; }; void exactResizeModel() { diff --git a/src/ipm/IpxWrapper.cpp b/src/ipm/IpxWrapper.cpp index 13b8cb4127..a7106edc5a 100644 --- a/src/ipm/IpxWrapper.cpp +++ b/src/ipm/IpxWrapper.cpp @@ -18,6 +18,7 @@ #include +#include "HighsClbk.h" #include "lp_data/HighsOptions.h" #include "lp_data/HighsSolution.h" @@ -27,7 +28,7 @@ HighsStatus solveLpIpx(HighsLpSolverObject& solver_object) { return solveLpIpx(solver_object.options_, solver_object.timer_, solver_object.lp_, solver_object.basis_, solver_object.solution_, solver_object.model_status_, solver_object.highs_info_, - solver_object.scipy_clbk_); + solver_object.clbk_fun_); } HighsStatus solveLpIpx(const HighsOptions& options, @@ -37,7 +38,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsSolution& highs_solution, HighsModelStatus& model_status, HighsInfo& highs_info, - scipy::clbk_t scipy_clbk) { + HighsClbk clbk_fun) { // Use IPX to try to solve the LP // // Can return HighsModelStatus (HighsStatus) values: @@ -92,6 +93,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, } else if (options.log_dev_level == kHighsLogDevLevelVerbose) { parameters.debug = 4; } + parameters.clbk_fun = clbk_fun; // Just test feasibility and optimality tolerances for now // ToDo Set more parameters parameters.ipm_feasibility_tol = min(options.primal_feasibility_tolerance, @@ -143,7 +145,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, } // Use IPX to solve the LP! - ipx::Int solve_status = lps.Solve(scipy_clbk); + ipx::Int solve_status = lps.Solve(); const bool report_solve_data = kHighsAnalysisLevelSolverSummaryData & options.highs_analysis_level; // Get solver and solution information. diff --git a/src/ipm/IpxWrapper.h b/src/ipm/IpxWrapper.h index ea34c6d2d3..ca555c6c35 100644 --- a/src/ipm/IpxWrapper.h +++ b/src/ipm/IpxWrapper.h @@ -30,7 +30,7 @@ HighsStatus solveLpIpx(const HighsOptions& options, HighsTimer& timer, const HighsLp& lp, HighsBasis& highs_basis, HighsSolution& highs_solution, HighsModelStatus& model_status, HighsInfo& highs_info, - scipy::clbk_t scipy_clbk); + HighsClbk clbk_t); void fillInIpxData(const HighsLp& lp, ipx::Int& num_col, ipx::Int& num_row, std::vector& obj, std::vector& col_lb, diff --git a/src/ipm/ipx/ipm.cc b/src/ipm/ipx/ipm.cc index 822aade40a..b846220f6c 100644 --- a/src/ipm/ipx/ipm.cc +++ b/src/ipm/ipx/ipm.cc @@ -39,7 +39,8 @@ void IPM::StartingPoint(KKTSolver* kkt, Iterate* iterate, Info* info) { } } -void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info, scipy::clbk_t scipy_clbk) { +void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info, + HighsClbk clbk_fun) { const Model& model = iterate->model(); const Int m = model.rows(); const Int n = model.cols(); @@ -74,8 +75,13 @@ void IPM::Driver(KKTSolver* kkt, Iterate* iterate, Info* info, scipy::clbk_t sci if (info->errflag) break; MakeStep(step); - scipy_clbk(info); info->iter++; + if (clbk_fun) { + HighsClbkInfo clbk_info { + info->iter + }; + clbk_fun(&clbk_info); + } PrintOutput(); } diff --git a/src/ipm/ipx/ipm.h b/src/ipm/ipx/ipm.h index 0358735b59..e6ef0571bd 100644 --- a/src/ipm/ipx/ipm.h +++ b/src/ipm/ipx/ipm.h @@ -1,11 +1,10 @@ #ifndef IPX_IPM_H_ #define IPX_IPM_H_ +#include "HighsClbk.h" #include "ipm/ipx/control.h" -#include "ipm/ipx/kkt_solver.h" #include "ipm/ipx/iterate.h" - -#include "scipy.h" +#include "ipm/ipx/kkt_solver.h" namespace ipx { @@ -35,7 +34,8 @@ class IPM { // IPX_STATUS_no_progress if no progress over a number of iterations, // IPX_STATUS_time_limit if interrupted by time limit, // IPX_STATUS_failed if the KKT solver failed with info->errflag. - void Driver(KKTSolver* kkt, Iterate* iterate, Info* info, scipy::clbk_t scipy_clbk); + void Driver(KKTSolver* kkt, Iterate* iterate, Info* info, + HighsClbk clbk_fun); Int maxiter() const { return maxiter_; } void maxiter(Int i) { maxiter_ = i; } diff --git a/src/ipm/ipx/ipx_c.cc b/src/ipm/ipx/ipx_c.cc index db475e18e9..91e885692e 100644 --- a/src/ipm/ipx/ipx_c.cc +++ b/src/ipm/ipx/ipx_c.cc @@ -77,7 +77,7 @@ ipxint ipx_load_ipm_starting_point(void* self, const double* x, ipxint ipx_solve(void* self) { LpSolver* solver = static_cast(self); - return solver->Solve(nullptr); + return solver->Solve(); } struct ipx_info ipx_get_info(void* self) { diff --git a/src/ipm/ipx/ipx_internal.h b/src/ipm/ipx/ipx_internal.h index 1c1a87625f..ba2e0d4362 100644 --- a/src/ipm/ipx/ipx_internal.h +++ b/src/ipm/ipx/ipx_internal.h @@ -4,6 +4,7 @@ #include #include +#include "HighsClbk.h" #include "ipm/ipx/ipx_config.h" #include "ipm/ipx/ipx_info.h" #include "ipm/ipx/ipx_parameters.h" @@ -24,6 +25,7 @@ struct Parameters : public ipx_parameters { logfile = nullptr; print_interval = 5.0; analyse_basis_data = false; + HighsClbk clbk_fun = nullptr; time_limit = -1.0; dualize = -1; scale = 1; diff --git a/src/ipm/ipx/ipx_parameters.h b/src/ipm/ipx/ipx_parameters.h index fb626de7b8..898cabfd35 100644 --- a/src/ipm/ipx/ipx_parameters.h +++ b/src/ipm/ipx/ipx_parameters.h @@ -1,6 +1,7 @@ #ifndef IPX_PARAMETERS_H_ #define IPX_PARAMETERS_H_ +#include "HighsClbk.h" #include "ipm/ipx/ipx_config.h" #ifdef __cplusplus @@ -13,6 +14,7 @@ struct ipx_parameters { double print_interval; double time_limit; bool analyse_basis_data; + HighsClbk clbk_fun; /* Preprocessing */ ipxint dualize; diff --git a/src/ipm/ipx/lp_solver.cc b/src/ipm/ipx/lp_solver.cc index 6f7d8b6799..55780549bb 100644 --- a/src/ipm/ipx/lp_solver.cc +++ b/src/ipm/ipx/lp_solver.cc @@ -47,7 +47,7 @@ Int LpSolver::LoadIPMStartingPoint(const double* x, const double* xl, return 0; } -Int LpSolver::Solve(scipy::clbk_t scipy_clbk) { +Int LpSolver::Solve() { if (model_.empty()) return info_.status = IPX_STATUS_no_model; ClearSolution(); @@ -55,7 +55,7 @@ Int LpSolver::Solve(scipy::clbk_t scipy_clbk) { control_.OpenLogfile(); control_.Log() << "IPX version 1.0\n"; try { - InteriorPointSolve(scipy_clbk); + InteriorPointSolve(); const bool run_crossover_on = control_.run_crossover() == 1; const bool run_crossover_choose = control_.run_crossover() == -1; const bool run_crossover_not_off = run_crossover_choose || run_crossover_on; @@ -346,7 +346,7 @@ void LpSolver::ClearSolution() { } -void LpSolver::InteriorPointSolve(scipy::clbk_t scipy_clbk) { +void LpSolver::InteriorPointSolve() { control_.Log() << "Interior Point Solve\n"; // Allocate new iterate and set tolerances for IPM termination test. @@ -356,7 +356,7 @@ void LpSolver::InteriorPointSolve(scipy::clbk_t scipy_clbk) { if (control_.run_crossover()) iterate_->start_crossover_tol(control_.start_crossover_tol()); - RunIPM(scipy_clbk); + RunIPM(); iterate_->Postprocess(); iterate_->EvaluatePostsolved(&info_); @@ -371,7 +371,7 @@ void LpSolver::InteriorPointSolve(scipy::clbk_t scipy_clbk) { } } -void LpSolver::RunIPM(scipy::clbk_t scipy_clbk) { +void LpSolver::RunIPM() { IPM ipm(control_); if (x_start_.size() != 0) { @@ -384,14 +384,14 @@ void LpSolver::RunIPM(scipy::clbk_t scipy_clbk) { ComputeStartingPoint(ipm); if (info_.status_ipm != IPX_STATUS_not_run) return; - RunInitialIPM(ipm, scipy_clbk); + RunInitialIPM(ipm); if (info_.status_ipm != IPX_STATUS_not_run) return; } BuildStartingBasis(); if (info_.status_ipm != IPX_STATUS_not_run) return; - RunMainIPM(ipm, scipy_clbk); + RunMainIPM(ipm); } void LpSolver::MakeIPMStartingPointValid() { @@ -460,7 +460,7 @@ void LpSolver::ComputeStartingPoint(IPM& ipm) { info_.time_ipm1 += timer.Elapsed(); } -void LpSolver::RunInitialIPM(IPM& ipm, scipy::clbk_t scipy_clbk) { +void LpSolver::RunInitialIPM(IPM& ipm) { Timer timer; KKTSolverDiag kkt(control_, model_); @@ -474,7 +474,7 @@ void LpSolver::RunInitialIPM(IPM& ipm, scipy::clbk_t scipy_clbk) { } else { ipm.maxiter(std::min(switchiter, control_.ipm_maxiter())); } - ipm.Driver(&kkt, iterate_.get(), &info_, scipy_clbk); + ipm.Driver(&kkt, iterate_.get(), &info_, GetParameters().clbk_fun); switch (info_.status_ipm) { case IPX_STATUS_optimal: // If the IPM reached its termination criterion in the initial @@ -532,11 +532,11 @@ void LpSolver::BuildStartingBasis() { } } -void LpSolver::RunMainIPM(IPM& ipm, scipy::clbk_t scipy_clbk) { +void LpSolver::RunMainIPM(IPM& ipm) { KKTSolverBasis kkt(control_, *basis_); Timer timer; ipm.maxiter(control_.ipm_maxiter()); - ipm.Driver(&kkt, iterate_.get(), &info_, scipy_clbk); + ipm.Driver(&kkt, iterate_.get(), &info_, GetParameters().clbk_fun); info_.time_ipm2 = timer.Elapsed(); } diff --git a/src/ipm/ipx/lp_solver.h b/src/ipm/ipx/lp_solver.h index 1281488a92..d9d6b798fb 100644 --- a/src/ipm/ipx/lp_solver.h +++ b/src/ipm/ipx/lp_solver.h @@ -8,8 +8,6 @@ #include "ipm/ipx/iterate.h" #include "ipm/ipx/model.h" -#include "scipy.h" - namespace ipx { class LpSolver { @@ -70,7 +68,7 @@ class LpSolver { // Solves the model that is currently loaded in the object. // Returns GetInfo().status. - Int Solve(scipy::clbk_t scipy_clbk); + Int Solve(); // Returns the solver info from the last call to Solve(). See the reference // documentation for the meaning of Info values. @@ -163,13 +161,13 @@ class LpSolver { private: void ClearSolution(); - void InteriorPointSolve(scipy::clbk_t scipy_clbk); - void RunIPM(scipy::clbk_t scipy_clbk); + void InteriorPointSolve(); + void RunIPM(); void MakeIPMStartingPointValid(); void ComputeStartingPoint(IPM& ipm); - void RunInitialIPM(IPM& ipm, scipy::clbk_t scipy_clbk); + void RunInitialIPM(IPM& ipm); void BuildStartingBasis(); - void RunMainIPM(IPM& ipm, scipy::clbk_t scipy_clbk); + void RunMainIPM(IPM& ipm); void BuildCrossoverStartingPoint(); void RunCrossover(); void PrintSummary(); diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 37715ddcd7..16674e05ae 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -1429,7 +1429,7 @@ HighsStatus Highs::getRanging() { // class, and the scaled/unscaled model status HighsLpSolverObject solver_object(model_.lp_, basis_, solution_, info_, ekk_instance_, options_, timer_, - scipy_clbk_); + clbk_fun_); solver_object.model_status_ = model_status_; return getRangingData(this->ranging_, solver_object); } @@ -1732,7 +1732,7 @@ HighsStatus Highs::setBasis(const HighsBasis& basis, modifiable_basis.was_alien = true; HighsLpSolverObject solver_object(model_.lp_, modifiable_basis, solution_, info_, ekk_instance_, options_, timer_, - scipy_clbk_); + clbk_fun_); HighsStatus return_status = formSimplexLpBasisAndFactor(solver_object); if (return_status != HighsStatus::kOk) return HighsStatus::kError; // Update the HiGHS basis @@ -2754,7 +2754,7 @@ HighsStatus Highs::callSolveLp(HighsLp& lp, const string message) { // Create a HighsLpSolverObject of references to data in the Highs // class, and the scaled/unscaled model status HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, - options_, timer_, scipy_clbk_); + options_, timer_, clbk_fun_); // Check that the model is column-wise assert(model_.lp_.a_matrix_.isColwise()); diff --git a/src/lp_data/HighsInterface.cpp b/src/lp_data/HighsInterface.cpp index 97acf48d10..1b803b8da3 100644 --- a/src/lp_data/HighsInterface.cpp +++ b/src/lp_data/HighsInterface.cpp @@ -1094,7 +1094,7 @@ HighsStatus Highs::getBasicVariablesInterface(HighsInt* basic_variables) { // Create a HighsLpSolverObject HighsLpSolverObject solver_object(lp, basis_, solution_, info_, ekk_instance_, options_, timer_, - scipy_clbk_); + clbk_fun_); const bool only_from_known_basis = true; return_status = interpretCallStatus( options_.log_options, diff --git a/src/lp_data/HighsLpSolverObject.h b/src/lp_data/HighsLpSolverObject.h index dbb23c8966..5d625f207f 100644 --- a/src/lp_data/HighsLpSolverObject.h +++ b/src/lp_data/HighsLpSolverObject.h @@ -16,18 +16,17 @@ #ifndef LP_DATA_HIGHS_LP_SOLVER_OBJECT_H_ #define LP_DATA_HIGHS_LP_SOLVER_OBJECT_H_ +#include "HighsClbk.h" #include "lp_data/HighsInfo.h" #include "lp_data/HighsOptions.h" #include "simplex/HEkk.h" -#include "scipy.h" - class HighsLpSolverObject { public: HighsLpSolverObject(HighsLp& lp, HighsBasis& basis, HighsSolution& solution, HighsInfo& highs_info, HEkk& ekk_instance, HighsOptions& options, HighsTimer& timer, - scipy::clbk_t scipy_clbk) + HighsClbk clbk_fun) : lp_(lp), basis_(basis), solution_(solution), @@ -35,7 +34,7 @@ class HighsLpSolverObject { ekk_instance_(ekk_instance), options_(options), timer_(timer), - scipy_clbk_(scipy_clbk) {} + clbk_fun_(clbk_fun) {} HighsLp& lp_; HighsBasis& basis_; @@ -44,10 +43,9 @@ class HighsLpSolverObject { HEkk& ekk_instance_; HighsOptions& options_; HighsTimer& timer_; + HighsClbk clbk_fun_; HighsModelStatus model_status_ = HighsModelStatus::kNotset; - - scipy::clbk_t scipy_clbk_; }; #endif // LP_DATA_HIGHS_LP_SOLVER_OBJECT_H_ diff --git a/src/scipy.h b/src/scipy.h deleted file mode 100644 index c72783f05b..0000000000 --- a/src/scipy.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by nmckibben on 12/14/22. -// - -#ifndef HIGHS_SCIPY_H -#define HIGHS_SCIPY_H - -namespace ipx { - class Info; -} - -namespace scipy { - typedef void(*clbk_t)(ipx::Info*); -} - -#include "ipm.h" - -#endif // HIGHS_SCIPY_H diff --git a/src/util/HighsClbk.h b/src/util/HighsClbk.h new file mode 100644 index 0000000000..8f01f6fec8 --- /dev/null +++ b/src/util/HighsClbk.h @@ -0,0 +1,17 @@ +// +// Created by nmckibben on 12/14/22. +// + +#ifndef HIGHS_HIGHSCLBK_H +#define HIGHS_HIGHSCLBK_H + +#include "HighsInt.h" + +struct HighsClbkInfo { + explicit HighsClbkInfo(const HighsInt& iteration) + : iteration_(iteration) {} + const HighsInt& iteration_; +}; +typedef void(*HighsClbk)(HighsClbkInfo*); + +#endif // HIGHS_HIGHSCLBK_H From ed20bb7e4d6ee9ad6221fb0f837de3ecb5c349b5 Mon Sep 17 00:00:00 2001 From: Nicholas McKibben Date: Sat, 17 Dec 2022 11:38:07 -0700 Subject: [PATCH 3/4] Add setClbk --- src/Highs.h | 10 +++++++--- src/util/HighsClbk.h | 4 ---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Highs.h b/src/Highs.h index 40db27bf7c..c33cf13405 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1008,6 +1008,12 @@ class Highs { HighsStatus setHighsOptionValue(const std::string& option, const HighsInt value); + void setClbk(HighsClbk clbk_fun) { + clbk_fun_ = +[](HighsClbkInfo* info){ + std::cout << "iteration: " << info->iteration_ << "\n"; + }; + } + #ifdef HIGHSINT64 HighsStatus setHighsOptionValue(const std::string& option, const int value //!< The option value @@ -1116,9 +1122,7 @@ class Highs { bool written_log_header = false; - HighsClbk clbk_fun_ = +[](HighsClbkInfo* info){ - std::cout << "iteration: " << info->iteration_ << "\n"; - }; + HighsClbk clbk_fun_ = nullptr; void exactResizeModel() { this->model_.lp_.exactResize(); diff --git a/src/util/HighsClbk.h b/src/util/HighsClbk.h index 8f01f6fec8..f48d29a807 100644 --- a/src/util/HighsClbk.h +++ b/src/util/HighsClbk.h @@ -1,7 +1,3 @@ -// -// Created by nmckibben on 12/14/22. -// - #ifndef HIGHS_HIGHSCLBK_H #define HIGHS_HIGHSCLBK_H From b56c1a56bb00e073d35b80ffee0444900ece5c1f Mon Sep 17 00:00:00 2001 From: Nicholas McKibben Date: Sat, 17 Dec 2022 12:08:02 -0700 Subject: [PATCH 4/4] Set clbk in Highs::setClbk method --- src/Highs.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Highs.h b/src/Highs.h index c33cf13405..6babedd611 100644 --- a/src/Highs.h +++ b/src/Highs.h @@ -1008,11 +1008,7 @@ class Highs { HighsStatus setHighsOptionValue(const std::string& option, const HighsInt value); - void setClbk(HighsClbk clbk_fun) { - clbk_fun_ = +[](HighsClbkInfo* info){ - std::cout << "iteration: " << info->iteration_ << "\n"; - }; - } + void setClbk(HighsClbk clbk_fun) { clbk_fun_ = clbk_fun; } #ifdef HIGHSINT64 HighsStatus setHighsOptionValue(const std::string& option,