From ed652d13986aa3de75586a0ddbca06cffd4288cd Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 11 Feb 2025 18:44:19 +0200 Subject: [PATCH 01/25] Add pybind11 as submodule --- .gitmodules | 4 ++++ ThirdParty/pybind11 | 1 + 2 files changed, 5 insertions(+) create mode 160000 ThirdParty/pybind11 diff --git a/.gitmodules b/.gitmodules index 6226a9fe..ba0f4f81 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,7 @@ [submodule "ThirdParty/CppAD"] path = ThirdParty/CppAD url = https://github.com/coin-or/CppAD +[submodule "ThirdParty/pybind11"] + path = ThirdParty/pybind11 + url = ../../pybind/pybind11 + branch = stable diff --git a/ThirdParty/pybind11 b/ThirdParty/pybind11 new file mode 160000 index 00000000..58c382a8 --- /dev/null +++ b/ThirdParty/pybind11 @@ -0,0 +1 @@ +Subproject commit 58c382a8e3d7081364d2f5c62e7f429f0412743b From b933a02e9230fb08cf5b24fce956fd3a65e5f396 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 11 Feb 2025 18:44:51 +0200 Subject: [PATCH 02/25] Add first draft of shotpy --- CMakeLists.txt | 24 +++++++++++++ src/Solver.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec956bb8..795dda7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,10 @@ set(CBC_DIR "/opt/Cbc-2.10" CACHE STRING "The base directory where Cbc is locate option(HAS_IPOPT "Is Ipopt available" OFF) set(IPOPT_DIR "/opt/ipopt" CACHE STRING "The base directory where Ipopt is located (if available).") +# Python +option(HAS_PYTHON "Is Python available" OFF) +set(IPOPT_DIR "/opt/ipopt" CACHE STRING "The base directory where Ipopt is located (if available).") + # Create also the executable option(GENERATE_EXE "Should the SHOT executable be generated (requires at least that either OS or GAMS is available)" ON) @@ -77,6 +81,7 @@ set(BOOST_DIR "ThirdParty/boost") set(CPPAD_DIR "ThirdParty/CppAD") set(EIGEN_DIR "ThirdParty/eigen") set(MCPP_DIR "ThirdParty/mc++") +set(PYBIND11_DIR "ThirdParty/pybind11") set(SPDLOG_DIR "ThirdParty/spdlog") set(TINYXML2_DIR "ThirdParty/tinyxml2") @@ -339,6 +344,7 @@ include_directories(SYSTEM "${EIGEN_DIR}") include_directories(SYSTEM "${MCPP_DIR}/include") #include_directories(SYSTEM "${MCPP_DIR}/3rdparty/cpplapack/include") #include_directories(SYSTEM "${MCPP_DIR}/3rdparty/fadbad++") +include_directories(SYSTEM "${PYBIND11_DIR}/include") include_directories(SYSTEM "${SPDLOG_DIR}/include") # Make sure the source file lists are in the correct format @@ -426,6 +432,11 @@ add_library( ) target_link_libraries(SHOTResults SHOTModel) +add_subdirectory(${PYBIND11_DIR}) +pybind11_add_module(shotpy src/Solver.cpp) + +target_link_libraries(shotpy PRIVATE SHOTSolver) + # Creates the primal strategy library add_library( SHOTPrimalStrategy STATIC @@ -628,6 +639,19 @@ if(GENERATE_EXE) endif(HAS_GAMS) endif(GENERATE_EXE) + +if(HAS_PYTHON) + find_package(Python3 REQUIRED COMPONENTS Interpreter Development) + include_directories(SYSTEM ${Python3_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} ${Python3_LIBRARIES}) + message("-- The following Python libraries will be used from: ${Python3_LIBRARIES}") + message(" ${Python3_INCLUDE_DIRS}") + +# Is this needed? +# add_definitions(-DHAS_PYTHON) + +endif(HAS_PYTHON) + # Extra flags for Visual Studio compilers if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") diff --git a/src/Solver.cpp b/src/Solver.cpp index 57350b6c..248053a3 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -8,6 +8,9 @@ Please see the README and LICENSE files for more information. */ +#include +#include + #include "Solver.h" #include "DualSolver.h" @@ -2053,4 +2056,98 @@ std::vector Solver::getPrimalSolutions() { return (env->results- E_TerminationReason Solver::getTerminationReason() { return (env->results->terminationReason); } E_ModelReturnStatus Solver::getModelReturnStatus() { return (env->results->getModelReturnStatus()); } + + + +namespace py = pybind11; + + +PYBIND11_MODULE(shotpy, m) { + m.doc() = "pybind11 example plugin"; // optional module docstring + + py::class_(m, "Solver") + .def(py::init()) + .def("finalizeSolution", &Solver::finalizeSolution) + .def("getAbsoluteObjectiveGap", &Solver::getAbsoluteObjectiveGap) + .def("getCurrentDualBound", &Solver::getCurrentDualBound) + .def("getModelReturnStatus", &Solver::getModelReturnStatus) + .def("getOptions", &Solver::getOptions) + .def("getOptionsOSoL", &Solver::getOptionsOSoL) + + .def("getPrimalBound", &Solver::getPrimalBound) + .def("getPrimalSolution", &Solver::getPrimalSolution) + .def("getPrimalSolutions", &Solver::getPrimalSolutions) + .def("getRelativeObjectiveGap", &Solver::getRelativeObjectiveGap) + .def("getResultsOSrL", &Solver::getResultsOSrL) + .def("getResultsSol", &Solver::getResultsSol) + .def("getResultsTrace", &Solver::getResultsTrace) + + .def("getTerminationReason", &Solver::getTerminationReason) + .def("hasPrimalSolution", &Solver::hasPrimalSolution) + .def("setLogFile", &Solver::setLogFile) + .def("setOptionsFromFile", &Solver::setOptionsFromFile) + .def("setOptionsFromOSoL", &Solver::setOptionsFromOSoL) + .def("setOptionsFromString", &Solver::setOptionsFromString) + .def("setProblem", py::overload_cast(&Solver::setProblem)) + .def("solveProblem", &Solver::solveProblem) + ; + + py::enum_(m, "E_PrimalSolutionSource", py::arithmetic()) + .value("Rootsearch", E_PrimalSolutionSource::Rootsearch) + .value("RootsearchFixedIntegers", E_PrimalSolutionSource::RootsearchFixedIntegers) + .value("NLPFixedIntegers", E_PrimalSolutionSource::NLPFixedIntegers) + .value("NLPRelaxed", E_PrimalSolutionSource::NLPRelaxed) + .value("MIPSolutionPool", E_PrimalSolutionSource::MIPSolutionPool) + .value("LPFixedIntegers", E_PrimalSolutionSource::LPFixedIntegers) + .value("MIPCallback", E_PrimalSolutionSource::MIPCallback) + .value("InteriorPointSearch", E_PrimalSolutionSource::InteriorPointSearch) + ; + + py::enum_(m, "E_ModelReturnStatus", py::arithmetic()) + .value("None", E_ModelReturnStatus::None) + .value("OptimalGlobal", E_ModelReturnStatus::OptimalGlobal) + .value("Unbounded", E_ModelReturnStatus::Unbounded) + .value("UnboundedNoSolution", E_ModelReturnStatus::UnboundedNoSolution) + .value("InfeasibleGlobal", E_ModelReturnStatus::InfeasibleGlobal) + .value("InfeasibleLocal", E_ModelReturnStatus::InfeasibleLocal) + .value("FeasibleSolution", E_ModelReturnStatus::FeasibleSolution) + .value("NoSolutionReturned", E_ModelReturnStatus::NoSolutionReturned) + .value("ErrorUnknown", E_ModelReturnStatus::ErrorUnknown) + .value("ErrorNoSolution", E_ModelReturnStatus::ErrorNoSolution) + ; + + py::enum_(m, "E_TerminationReason", py::arithmetic()) + .value("ConstraintTolerance", E_TerminationReason::ConstraintTolerance) + .value("ObjectiveStagnation", E_TerminationReason::ObjectiveStagnation) + .value("IterationLimit", E_TerminationReason::IterationLimit) + .value("TimeLimit", E_TerminationReason::TimeLimit) + .value("InfeasibleProblem", E_TerminationReason::InfeasibleProblem) + .value("UnboundedProblem", E_TerminationReason::UnboundedProblem) + .value("Error", E_TerminationReason::Error) + .value("AbsoluteGap", E_TerminationReason::AbsoluteGap) + .value("RelativeGap", E_TerminationReason::RelativeGap) + .value("UserAbort", E_TerminationReason::UserAbort) + .value("NoDualCutsAdded", E_TerminationReason::NoDualCutsAdded) + .value("None", E_TerminationReason::None) + .value("NumericIssues", E_TerminationReason::NumericIssues) + ; + + py::class_(m, "PrimalSolution") + .def_readwrite("point", &PrimalSolution::point) + .def_readwrite("sourceType", &PrimalSolution::sourceType) + .def_readwrite("objValue", &PrimalSolution::objValue) + .def_readwrite("iterFound", &PrimalSolution::iterFound) + .def_readwrite("maxDevatingConstraintLinear", &PrimalSolution::maxDevatingConstraintLinear) + .def_readwrite("maxDevatingConstraintQuadratic", &PrimalSolution::maxDevatingConstraintQuadratic) + .def_readwrite("maxDevatingConstraintNonlinear", &PrimalSolution::maxDevatingConstraintNonlinear) + .def_readwrite("maxIntegerToleranceError", &PrimalSolution::maxIntegerToleranceError) + .def_readwrite("boundProjectionPerformed", &PrimalSolution::boundProjectionPerformed) + .def_readwrite("integerRoundingPerformed", &PrimalSolution::integerRoundingPerformed) + .def_readwrite("displayed", &PrimalSolution::displayed) + ; + +} + + + } // namespace SHOT From e215fe7ce5a2acb74082779cbf9a4591e9dfb717 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 11 Feb 2025 18:50:50 +0200 Subject: [PATCH 03/25] Add more methods --- src/Solver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Solver.cpp b/src/Solver.cpp index 248053a3..d3eca6e6 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2084,12 +2084,19 @@ PYBIND11_MODULE(shotpy, m) { .def("getTerminationReason", &Solver::getTerminationReason) .def("hasPrimalSolution", &Solver::hasPrimalSolution) + + .def("outputSolverHeader", &Solver::outputSolverHeader) + .def("outputOptionsReport", &Solver::outputOptionsReport) + .def("outputProblemInstanceReport", &Solver::outputProblemInstanceReport) + .def("outputSolutionReport", &Solver::outputSolutionReport) + .def("setLogFile", &Solver::setLogFile) .def("setOptionsFromFile", &Solver::setOptionsFromFile) .def("setOptionsFromOSoL", &Solver::setOptionsFromOSoL) .def("setOptionsFromString", &Solver::setOptionsFromString) .def("setProblem", py::overload_cast(&Solver::setProblem)) .def("solveProblem", &Solver::solveProblem) + .def("updateLogLevels", &Solver::updateLogLevels) ; py::enum_(m, "E_PrimalSolutionSource", py::arithmetic()) From 7aa7ad526488e5a699173d203540f4bfef0696c8 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 11 Feb 2025 19:12:22 +0200 Subject: [PATCH 04/25] Add SolutionStatistics --- src/Solver.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index d3eca6e6..7898c06b 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2081,7 +2081,9 @@ PYBIND11_MODULE(shotpy, m) { .def("getResultsOSrL", &Solver::getResultsOSrL) .def("getResultsSol", &Solver::getResultsSol) .def("getResultsTrace", &Solver::getResultsTrace) - + + .def("getSetSolutionStatistics", &Solver::getSetSolutionStatistics) + .def("getTerminationReason", &Solver::getTerminationReason) .def("hasPrimalSolution", &Solver::hasPrimalSolution) @@ -2153,6 +2155,58 @@ PYBIND11_MODULE(shotpy, m) { .def_readwrite("displayed", &PrimalSolution::displayed) ; + + + py::class_(m, "SolutionStatistics") + .def_readwrite("numberOfIterations", &SolutionStatistics::numberOfIterations) + .def_readwrite("numberOfProblemsLP", &SolutionStatistics::numberOfProblemsLP) + .def_readwrite("numberOfProblemsQP ", &SolutionStatistics::numberOfProblemsQP) + .def_readwrite("numberOfProblemsQCQP", &SolutionStatistics::numberOfProblemsQCQP) + .def_readwrite("numberOfProblemsFeasibleMILP", &SolutionStatistics::numberOfProblemsFeasibleMILP) + .def_readwrite("numberOfProblemsOptimalMILP", &SolutionStatistics::numberOfProblemsOptimalMILP) + .def_readwrite("numberOfProblemsFeasibleMIQP", &SolutionStatistics::numberOfProblemsFeasibleMIQP) + .def_readwrite("numberOfProblemsOptimalMIQP", &SolutionStatistics::numberOfProblemsOptimalMIQP) + .def_readwrite("numberOfProblemsFeasibleMIQCQP", &SolutionStatistics::numberOfProblemsFeasibleMIQCQP) + .def_readwrite("numberOfProblemsOptimalMIQCQP", &SolutionStatistics::numberOfProblemsOptimalMIQCQP) + .def_readwrite("numberOfFunctionEvalutions", &SolutionStatistics::numberOfFunctionEvalutions) + .def_readwrite("numberOfGradientEvaluations", &SolutionStatistics::numberOfGradientEvaluations) + .def_readwrite("numberOfProblemsMinimaxLP", &SolutionStatistics::numberOfProblemsMinimaxLP) + .def_readwrite("numberOfProblemsFixedNLP", &SolutionStatistics::numberOfProblemsFixedNLP) + .def_readwrite("numberOfConstraintsRemovedInPresolve", &SolutionStatistics::numberOfConstraintsRemovedInPresolve) + .def_readwrite("numberOfVariableBoundsTightenedInPresolve", &SolutionStatistics::numberOfVariableBoundsTightenedInPresolve) + .def_readwrite("numberOfHyperplanesWithConvexSource", &SolutionStatistics::numberOfHyperplanesWithConvexSource) + .def_readwrite("numberOfHyperplanesWithNonconvexSource", &SolutionStatistics::numberOfHyperplanesWithNonconvexSource) + .def_readwrite("numberOfIntegerCuts", &SolutionStatistics::numberOfIntegerCuts) + .def_readwrite("numberOfIterationsWithDualStagnation", &SolutionStatistics::numberOfIterationsWithDualStagnation) + .def_readwrite("lastIterationWithSignificantDualUpdate", &SolutionStatistics::lastIterationWithSignificantDualUpdate) + .def_readwrite("numberOfIterationsWithPrimalStagnation", &SolutionStatistics::numberOfIterationsWithPrimalStagnation) + .def_readwrite("lastIterationWithSignificantPrimalUpdate", &SolutionStatistics::lastIterationWithSignificantPrimalUpdate) + .def_readwrite("numberOfIterationsWithoutNLPCallMIP", &SolutionStatistics::numberOfIterationsWithoutNLPCallMIP) + .def_readwrite("iterationLastPrimalBoundUpdate", &SolutionStatistics::iterationLastPrimalBoundUpdate) + .def_readwrite("iterationLastDualBoundUpdate", &SolutionStatistics::iterationLastDualBoundUpdate) + .def_readwrite("iterationLastLazyAdded", &SolutionStatistics::iterationLastLazyAdded) + .def_readwrite("iterationLastDualCutAdded", &SolutionStatistics::iterationLastDualCutAdded) + .def_readwrite("timeLastDualBoundUpdate", &SolutionStatistics::timeLastDualBoundUpdate) + .def_readwrite("timeLastFixedNLPCall", &SolutionStatistics::timeLastFixedNLPCall) + .def_readwrite("numberOfOriginalInteriorPoints", &SolutionStatistics::numberOfOriginalInteriorPoints) + .def_readwrite("numberOfFoundPrimalSolutions", &SolutionStatistics::numberOfFoundPrimalSolutions) + .def_readwrite("numberOfExploredNodes", &SolutionStatistics::numberOfExploredNodes) + .def_readwrite("numberOfOpenNodes", &SolutionStatistics::numberOfOpenNodes) + .def_readwrite("numberOfPrimalReductionCutsUpdatesWithoutEffect", &SolutionStatistics::numberOfPrimalReductionCutsUpdatesWithoutEffect) + .def_readwrite("numberOfDualRepairsSinceLastPrimalUpdate", &SolutionStatistics::numberOfDualRepairsSinceLastPrimalUpdate) + .def_readwrite("numberOfPrimalReductionsPerformed", &SolutionStatistics::numberOfPrimalReductionsPerformed) + .def_readwrite("numberOfSuccessfulDualRepairsPerformed", &SolutionStatistics::numberOfSuccessfulDualRepairsPerformed) + .def_readwrite("numberOfUnsuccessfulDualRepairsPerformed", &SolutionStatistics::numberOfUnsuccessfulDualRepairsPerformed) + .def_readwrite("numberOfPrimalImprovementsAfterInfeasibilityRepair", &SolutionStatistics::numberOfPrimalImprovementsAfterInfeasibilityRepair) + .def_readwrite("numberOfPrimalImprovementsAfterReductionCut", &SolutionStatistics::numberOfPrimalImprovementsAfterReductionCut) + .def_readwrite("hasInfeasibilityRepairBeenPerformedSincePrimalImprovement", &SolutionStatistics::hasInfeasibilityRepairBeenPerformedSincePrimalImprovement) + .def_readwrite("hasReductionCutBeenAddedSincePrimalImprovement", &SolutionStatistics::hasReductionCutBeenAddedSincePrimalImprovement) + .def("getNumberOfTotalDualProblems", &SolutionStatistics::getNumberOfTotalDualProblems) + ; + + + + } From 5ec1cae9216c9c6c4cdb2f68f214ebcf59d1c6d5 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 16 Feb 2025 13:49:52 +0200 Subject: [PATCH 05/25] added getsettingsasmarkup --- src/Solver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index 7898c06b..7f6e4440 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2073,7 +2073,6 @@ PYBIND11_MODULE(shotpy, m) { .def("getModelReturnStatus", &Solver::getModelReturnStatus) .def("getOptions", &Solver::getOptions) .def("getOptionsOSoL", &Solver::getOptionsOSoL) - .def("getPrimalBound", &Solver::getPrimalBound) .def("getPrimalSolution", &Solver::getPrimalSolution) .def("getPrimalSolutions", &Solver::getPrimalSolutions) @@ -2083,7 +2082,8 @@ PYBIND11_MODULE(shotpy, m) { .def("getResultsTrace", &Solver::getResultsTrace) .def("getSetSolutionStatistics", &Solver::getSetSolutionStatistics) - + .def("getSettingsAsMarkup", &Solver::getSettingsAsMarkup) + .def("getTerminationReason", &Solver::getTerminationReason) .def("hasPrimalSolution", &Solver::hasPrimalSolution) From bdd44d544bacbfc017ee49cdb508ea722d83bb44 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 16 Feb 2025 13:50:24 +0200 Subject: [PATCH 06/25] Remove finalizesolution --- src/Solver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index 7f6e4440..d80e7423 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2067,7 +2067,6 @@ PYBIND11_MODULE(shotpy, m) { py::class_(m, "Solver") .def(py::init()) - .def("finalizeSolution", &Solver::finalizeSolution) .def("getAbsoluteObjectiveGap", &Solver::getAbsoluteObjectiveGap) .def("getCurrentDualBound", &Solver::getCurrentDualBound) .def("getModelReturnStatus", &Solver::getModelReturnStatus) From a8570ca605b10f68a15fec61cc3432b5b55c63fa Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 16 Feb 2025 14:09:28 +0200 Subject: [PATCH 07/25] added updateSetting --- src/Solver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Solver.cpp b/src/Solver.cpp index d80e7423..6d0002e6 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2098,6 +2098,10 @@ PYBIND11_MODULE(shotpy, m) { .def("setProblem", py::overload_cast(&Solver::setProblem)) .def("solveProblem", &Solver::solveProblem) .def("updateLogLevels", &Solver::updateLogLevels) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) ; py::enum_(m, "E_PrimalSolutionSource", py::arithmetic()) From 49dfaa24ade539bdcb72286dc48e2dba9f5d34e9 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 16 Feb 2025 14:47:54 +0200 Subject: [PATCH 08/25] Modified HAS_PYTHON checks --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 795dda7e..14a3b110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,10 +432,11 @@ add_library( ) target_link_libraries(SHOTResults SHOTModel) -add_subdirectory(${PYBIND11_DIR}) -pybind11_add_module(shotpy src/Solver.cpp) - -target_link_libraries(shotpy PRIVATE SHOTSolver) +if(HAS_PYTHON) + add_subdirectory(${PYBIND11_DIR}) + pybind11_add_module(shotpy src/Solver.cpp) + target_link_libraries(shotpy PRIVATE SHOTSolver) +endif(HAS_PYTHON) # Creates the primal strategy library add_library( From 146e6fa21531b9dddaa134ea0215ac15064252cd Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Thu, 20 Feb 2025 17:10:04 +0200 Subject: [PATCH 09/25] Cleanup --- src/Solver.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index 6d0002e6..ce7df377 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2058,12 +2058,11 @@ E_TerminationReason Solver::getTerminationReason() { return (env->results->termi E_ModelReturnStatus Solver::getModelReturnStatus() { return (env->results->getModelReturnStatus()); } - namespace py = pybind11; PYBIND11_MODULE(shotpy, m) { - m.doc() = "pybind11 example plugin"; // optional module docstring + m.doc() = "shotpy"; py::class_(m, "Solver") .def(py::init()) @@ -2158,8 +2157,6 @@ PYBIND11_MODULE(shotpy, m) { .def_readwrite("displayed", &PrimalSolution::displayed) ; - - py::class_(m, "SolutionStatistics") .def_readwrite("numberOfIterations", &SolutionStatistics::numberOfIterations) .def_readwrite("numberOfProblemsLP", &SolutionStatistics::numberOfProblemsLP) @@ -2206,12 +2203,6 @@ PYBIND11_MODULE(shotpy, m) { .def_readwrite("hasReductionCutBeenAddedSincePrimalImprovement", &SolutionStatistics::hasReductionCutBeenAddedSincePrimalImprovement) .def("getNumberOfTotalDualProblems", &SolutionStatistics::getNumberOfTotalDualProblems) ; - - - - } - - } // namespace SHOT From 09b824a829612d52434351f244f15d621af93107 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Thu, 20 Feb 2025 17:12:07 +0200 Subject: [PATCH 10/25] Added tests for python --- __init__.py | 0 test/__init__.py | 0 test/shotpy.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 __init__.py create mode 100644 test/__init__.py create mode 100644 test/shotpy.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/shotpy.py b/test/shotpy.py new file mode 100644 index 00000000..220222d0 --- /dev/null +++ b/test/shotpy.py @@ -0,0 +1,59 @@ + +from build import shotpy + +if __name__ == '__main__': + solver = shotpy.Solver() + + # solver.setOptionsFromFile('filename') + + print('getSettingsAsMarkup', solver.getSettingsAsMarkup() + + solver.setLogFile('testlogfile.txt') + + solver.updateLogLevels() + + solver.setProblem('test/data/alan.osil') + + solver.solveProblem() + + solver.outputSolverHeader() + solver.outputOptionsReport() + solver.outputProblemInstanceReport() + solver.outputSolutionReport() + + print('getOptionsOSol()', solver.getOptionsOSoL()) + + print('getOptions()', solver.getOptions()) + + print('getResultsOSrL', solver.getResultsOSrL()) + print('getResultsTrace', solver.getResultsTrace()) + print('getResultsSol', solver.getResultsSol()) + + + print('getCurrentDualBound', solver.getCurrentDualBound()) + print('getPrimalBound', solver.getPrimalBound()) + print('getAbsoluteObjectiveGap', solver.getAbsoluteObjectiveGap()) + print('getRelativeObjectiveGap', solver.getRelativeObjectiveGap()) + + + print('hasPrimalSolution', solver.hasPrimalSolution()) + primalsolution = solver.getPrimalSolution() + print('primalsolution.point', primalsolution.point) + print('primalsolution.sourceType', primalsolution.sourceType) + print('primalsolution.objValue', primalsolution.objValue) + print('primalsolution.displayed', primalsolution.displayed) + + primalsolutions = solver.getPrimalSolutions() + for primalsolution in primalsolutions: + print('primalsolution.point', primalsolution.point) + print('primalsolution.sourceType', primalsolution.sourceType) + print('primalsolution.objValue', primalsolution.objValue) + print('primalsolution.displayed', primalsolution.displayed) + + print('getModelReturnStatus', solver.getModelReturnStatus()) + print('getTerminationReason', solver.getTerminationReason()) + + getsetsolutionstatistics = solver.getSetSolutionStatistics() + print('numberofiterations', getsetsolutionstatistics.numberOfIterations) + + solver.updateSetting('name', 'category', False) From febb90d522e41bb81cedabc58ad3bc0ab5f1392b Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 16 Mar 2025 12:04:03 +0200 Subject: [PATCH 11/25] add getSetting --- src/Solver.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index ce7df377..7f9659a5 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2081,7 +2081,12 @@ PYBIND11_MODULE(shotpy, m) { .def("getSetSolutionStatistics", &Solver::getSetSolutionStatistics) .def("getSettingsAsMarkup", &Solver::getSettingsAsMarkup) - + + .def("getBoolSetting", py::overload_cast(&Solver::getSetting)) + .def("getStringSetting", py::overload_cast(&Solver::getSetting)) + .def("getIntSetting", py::overload_cast(&Solver::getSetting)) + .def("getDoubleSetting", py::overload_cast(&Solver::getSetting)) + .def("getTerminationReason", &Solver::getTerminationReason) .def("hasPrimalSolution", &Solver::hasPrimalSolution) From 785cb3ad2653ace78fb0cb05736bf99c74d54b84 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 16 Mar 2025 12:04:20 +0200 Subject: [PATCH 12/25] updated test.py --- test.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 test.py diff --git a/test.py b/test.py new file mode 100644 index 00000000..08bccd2e --- /dev/null +++ b/test.py @@ -0,0 +1,90 @@ + +from build import shotpy + +if __name__ == '__main__': + solver = shotpy.Solver() + + solver.setProblem('test/data/alan.osil') + + solver.solveProblem() + + + result = solver.getResultsOSrL() + + modelreturnstatus = solver.getModelReturnStatus() + print('modelreturnstatus', modelreturnstatus) + + eterminationreason = solver.getTerminationReason() + print('eterminationreason', eterminationreason) + + primalsolutions = solver.getPrimalSolutions() + print('primalsolutions', primalsolutions) + + hasprimalsolution = solver.hasPrimalSolution() + print('hasprimalsolution', hasprimalsolution) + + currentdualbound = solver.getCurrentDualBound() + + primal = solver.getPrimalBound() + absolute = solver.getAbsoluteObjectiveGap() + relative = solver.getRelativeObjectiveGap() + + primalsolution = solver.getPrimalSolution() + + + getsetsolutionstatistics = solver.getSetSolutionStatistics() + print('numberofiterations', getsetsolutionstatistics.numberOfIterations) + + print(primalsolution.point) + print(primalsolution.sourceType) + print(primalsolution.objValue) + print(primalsolution.displayed) + + solver.getIntSetting('test', 'test') + solver.getBoolSetting('test', 'test') + solver.updateSetting('test', 'test', 'test') + + # displayed + # print(dir(test)) + + # print(currentdualbound, primal, absolute, relative) + + + # environment = shotpy.Environment() + + # report = shotpy.Report(environment) + + # report.outputProblemInstanceReport() + + + + + + # clas.setOptionsFromFile('test.txt') + + # clas.doc + # clas.setOptionsFromString('teststu') + + + + + # print(environment) + # print(environment.report) + # env = clas.getEnvironment() + # print(env) + # print(dir(clas)) + + + # clas.solveProblem() + # result = clas.getResultsOSrL() + # print(result, type(result)) + # clas.finalizeSolution() + # help(py_solver) + # clas.setLogFile('test.txt') + + + + # clas.setOptionsFromFile('test.txt') + + # clas.doc + # clas.setOptionsFromString('teststu') \ No newline at end of file From a480da1f4b538c74c97c680d3697f33dd4b96a99 Mon Sep 17 00:00:00 2001 From: andreaslundell Date: Fri, 28 Mar 2025 16:58:05 +0200 Subject: [PATCH 13/25] Update pybind --- .gitmodules | 4 ---- ThirdParty/pybind11 | 1 - 2 files changed, 5 deletions(-) delete mode 160000 ThirdParty/pybind11 diff --git a/.gitmodules b/.gitmodules index ba0f4f81..6226a9fe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,7 +4,3 @@ [submodule "ThirdParty/CppAD"] path = ThirdParty/CppAD url = https://github.com/coin-or/CppAD -[submodule "ThirdParty/pybind11"] - path = ThirdParty/pybind11 - url = ../../pybind/pybind11 - branch = stable diff --git a/ThirdParty/pybind11 b/ThirdParty/pybind11 deleted file mode 160000 index 58c382a8..00000000 --- a/ThirdParty/pybind11 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 58c382a8e3d7081364d2f5c62e7f429f0412743b From 4d705afd8d6cd25f7faea1907545f29907d70022 Mon Sep 17 00:00:00 2001 From: andreaslundell Date: Sun, 30 Mar 2025 20:41:19 +0300 Subject: [PATCH 14/25] Some fixes, preliminary test.py --- .gitmodules | 4 ++ CMakeLists.txt | 2 +- ThirdParty/pybind11 | 1 + test.py | 90 --------------------------------------------- test/test.py | 66 +++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 91 deletions(-) create mode 160000 ThirdParty/pybind11 delete mode 100644 test.py create mode 100644 test/test.py diff --git a/.gitmodules b/.gitmodules index 6226a9fe..4eb46206 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,7 @@ [submodule "ThirdParty/CppAD"] path = ThirdParty/CppAD url = https://github.com/coin-or/CppAD +[submodule "ThirdParty/pybind11"] + path = ThirdParty/pybind11 + url = https://github.com/pybind/pybind11 + branch = stable diff --git a/CMakeLists.txt b/CMakeLists.txt index 14a3b110..a3127515 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.20) include(ExternalProject) # For ccache diff --git a/ThirdParty/pybind11 b/ThirdParty/pybind11 new file mode 160000 index 00000000..58c382a8 --- /dev/null +++ b/ThirdParty/pybind11 @@ -0,0 +1 @@ +Subproject commit 58c382a8e3d7081364d2f5c62e7f429f0412743b diff --git a/test.py b/test.py deleted file mode 100644 index 08bccd2e..00000000 --- a/test.py +++ /dev/null @@ -1,90 +0,0 @@ - -from build import shotpy - -if __name__ == '__main__': - solver = shotpy.Solver() - - solver.setProblem('test/data/alan.osil') - - solver.solveProblem() - - - result = solver.getResultsOSrL() - - modelreturnstatus = solver.getModelReturnStatus() - print('modelreturnstatus', modelreturnstatus) - - eterminationreason = solver.getTerminationReason() - print('eterminationreason', eterminationreason) - - primalsolutions = solver.getPrimalSolutions() - print('primalsolutions', primalsolutions) - - hasprimalsolution = solver.hasPrimalSolution() - print('hasprimalsolution', hasprimalsolution) - - currentdualbound = solver.getCurrentDualBound() - - primal = solver.getPrimalBound() - absolute = solver.getAbsoluteObjectiveGap() - relative = solver.getRelativeObjectiveGap() - - primalsolution = solver.getPrimalSolution() - - - getsetsolutionstatistics = solver.getSetSolutionStatistics() - print('numberofiterations', getsetsolutionstatistics.numberOfIterations) - - print(primalsolution.point) - print(primalsolution.sourceType) - print(primalsolution.objValue) - print(primalsolution.displayed) - - solver.getIntSetting('test', 'test') - solver.getBoolSetting('test', 'test') - solver.updateSetting('test', 'test', 'test') - - # displayed - # print(dir(test)) - - # print(currentdualbound, primal, absolute, relative) - - - # environment = shotpy.Environment() - - # report = shotpy.Report(environment) - - # report.outputProblemInstanceReport() - - - - - - # clas.setOptionsFromFile('test.txt') - - # clas.doc - # clas.setOptionsFromString('teststu') - - - - - # print(environment) - # print(environment.report) - # env = clas.getEnvironment() - # print(env) - # print(dir(clas)) - - - # clas.solveProblem() - # result = clas.getResultsOSrL() - # print(result, type(result)) - # clas.finalizeSolution() - # help(py_solver) - # clas.setLogFile('test.txt') - - - - # clas.setOptionsFromFile('test.txt') - - # clas.doc - # clas.setOptionsFromString('teststu') \ No newline at end of file diff --git a/test/test.py b/test/test.py new file mode 100644 index 00000000..f5d3073c --- /dev/null +++ b/test/test.py @@ -0,0 +1,66 @@ +import shotpy + +def solveProblem(problemFile, correctObjectiveValue): + + solver = shotpy.Solver() + loglevel = 6 + solver.updateSetting('Console.LogLevel', 'Output', loglevel) + + + if (solver.getIntSetting('Console.LogLevel', 'Output') != loglevel): + print("Failed to set log level") + return + + if (not solver.setProblem(problemFile)): + print("Failed to read problem file from " + problemFile) + return + else: + print("Problem file read from " + problemFile) + + + solver.outputProblemInstanceReport() + + if (not solver.solveProblem()): + print("Failed to solve problem in " + problemFile) + return + + osrl = solver.getResultsOSrL() + + solutions = solver.getPrimalSolutions() + + if (len(solutions) == 0): + print("No solution found") + return + else : + print("Number of solution found", len(solutions)) + + + for solution in solutions: + print("Solution " + solutions.index(solution).__str__()) + print("\tObjective value " + str(solution.objValue)) + print("\tSource " + str(solution.sourceType)) + #print("\tSource description: " + str(solution.sourceDescription)) # not working + print("\tPoint " + str(solution.point)) + #print("\tMax deviation " + str(solution.maxDevatingConstraintNonlinear.value)) # not working + + objValue = solutions[0].objValue + + if (abs(objValue - correctObjectiveValue) > 1e-4): + print("Objective value is incorrect") + return + else: + print("Objective value is correct") + + stats = solver.getSetSolutionStatistics() + print("Number of iterations " + str(stats.numberOfIterations)) + + #print(solver.getResultsOSrL()) + + #if (solver.getTerminationReason() != E_TerminationReason.AbsoluteGap): #not working + # print("Termination reason is AbsoluteGap") + +if __name__ == '__main__': + + solveProblem('test/data/alan.osil', 2.925) + solveProblem('test/data/meanvarxsc.osil', 14.36923211) + solveProblem('test/data/fo7_2.osil', 17.74934573) From f7e246cc2b22f30815f8cd1184d687e5fc03f74f Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 17:01:04 +0300 Subject: [PATCH 15/25] Rename getSetSolutionStatistics --- src/Solver.cpp | 2 +- src/Solver.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index 7f9659a5..73b97475 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2079,7 +2079,7 @@ PYBIND11_MODULE(shotpy, m) { .def("getResultsSol", &Solver::getResultsSol) .def("getResultsTrace", &Solver::getResultsTrace) - .def("getSetSolutionStatistics", &Solver::getSetSolutionStatistics) + .def("getSolutionStatistics", &Solver::getSolutionStatistics) .def("getSettingsAsMarkup", &Solver::getSettingsAsMarkup) .def("getBoolSetting", py::overload_cast(&Solver::getSetting)) diff --git a/src/Solver.h b/src/Solver.h index e8fd56bb..106e1350 100644 --- a/src/Solver.h +++ b/src/Solver.h @@ -117,7 +117,7 @@ class DllExport Solver PrimalSolution getPrimalSolution(); std::vector getPrimalSolutions(); - SolutionStatistics getSetSolutionStatistics() { return env->solutionStatistics; }; + SolutionStatistics getSolutionStatistics() { return env->solutionStatistics; }; E_TerminationReason getTerminationReason(); E_ModelReturnStatus getModelReturnStatus(); From c2b13d1b85c23341e75000110e4c8ced3914379c Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 17:15:35 +0300 Subject: [PATCH 16/25] Rename enums --- src/Solver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index 73b97475..7b95be7a 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2108,7 +2108,7 @@ PYBIND11_MODULE(shotpy, m) { .def("updateSetting", py::overload_cast(&Solver::updateSetting)) ; - py::enum_(m, "E_PrimalSolutionSource", py::arithmetic()) + py::enum_(m, "PrimalSolutionSource", py::arithmetic()) .value("Rootsearch", E_PrimalSolutionSource::Rootsearch) .value("RootsearchFixedIntegers", E_PrimalSolutionSource::RootsearchFixedIntegers) .value("NLPFixedIntegers", E_PrimalSolutionSource::NLPFixedIntegers) @@ -2119,7 +2119,7 @@ PYBIND11_MODULE(shotpy, m) { .value("InteriorPointSearch", E_PrimalSolutionSource::InteriorPointSearch) ; - py::enum_(m, "E_ModelReturnStatus", py::arithmetic()) + py::enum_(m, "ModelReturnStatus", py::arithmetic()) .value("None", E_ModelReturnStatus::None) .value("OptimalGlobal", E_ModelReturnStatus::OptimalGlobal) .value("Unbounded", E_ModelReturnStatus::Unbounded) @@ -2132,7 +2132,7 @@ PYBIND11_MODULE(shotpy, m) { .value("ErrorNoSolution", E_ModelReturnStatus::ErrorNoSolution) ; - py::enum_(m, "E_TerminationReason", py::arithmetic()) + py::enum_(m, "TerminationReason", py::arithmetic()) .value("ConstraintTolerance", E_TerminationReason::ConstraintTolerance) .value("ObjectiveStagnation", E_TerminationReason::ObjectiveStagnation) .value("IterationLimit", E_TerminationReason::IterationLimit) From ef916f5d2dc3bccedebf35100b9261a9254f55a9 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 17:16:02 +0300 Subject: [PATCH 17/25] Minor changes --- test/shotpy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/shotpy.py b/test/shotpy.py index 220222d0..c1c79f37 100644 --- a/test/shotpy.py +++ b/test/shotpy.py @@ -6,7 +6,7 @@ # solver.setOptionsFromFile('filename') - print('getSettingsAsMarkup', solver.getSettingsAsMarkup() + print('getSettingsAsMarkup', solver.getSettingsAsMarkup()) solver.setLogFile('testlogfile.txt') @@ -53,7 +53,7 @@ print('getModelReturnStatus', solver.getModelReturnStatus()) print('getTerminationReason', solver.getTerminationReason()) - getsetsolutionstatistics = solver.getSetSolutionStatistics() - print('numberofiterations', getsetsolutionstatistics.numberOfIterations) + getsolutionstatistics = solver.getSolutionStatistics() + print('numberofiterations', getsolutionstatistics.numberOfIterations) solver.updateSetting('name', 'category', False) From ac7b9fe86f60833e7a44ac8ecf638414788b3139 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 17:23:17 +0300 Subject: [PATCH 18/25] Add PairIndexValue --- src/Solver.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Solver.cpp b/src/Solver.cpp index 7b95be7a..8decb8fb 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2148,6 +2148,11 @@ PYBIND11_MODULE(shotpy, m) { .value("NumericIssues", E_TerminationReason::NumericIssues) ; + py::class_(m, "PairIndexValue") + .def_readwrite("index", &PairIndexValue::index) + .def_readwrite("value", &PairIndexValue::value) + ; + py::class_(m, "PrimalSolution") .def_readwrite("point", &PrimalSolution::point) .def_readwrite("sourceType", &PrimalSolution::sourceType) From 845ed2fedadc00cd87ddc735d6a731f4263cc5f2 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 17:27:12 +0300 Subject: [PATCH 19/25] Added missing sourceDescription --- src/Solver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Solver.cpp b/src/Solver.cpp index 8decb8fb..d8e71e66 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2156,6 +2156,7 @@ PYBIND11_MODULE(shotpy, m) { py::class_(m, "PrimalSolution") .def_readwrite("point", &PrimalSolution::point) .def_readwrite("sourceType", &PrimalSolution::sourceType) + .def_readwrite("sourceDescription", &PrimalSolution::sourceDescription) .def_readwrite("objValue", &PrimalSolution::objValue) .def_readwrite("iterFound", &PrimalSolution::iterFound) .def_readwrite("maxDevatingConstraintLinear", &PrimalSolution::maxDevatingConstraintLinear) From 7c573078c0d028c9de887f812da556922d602707 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 17:29:52 +0300 Subject: [PATCH 20/25] Fix some tests --- test/test.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test.py b/test/test.py index f5d3073c..1fcee408 100644 --- a/test/test.py +++ b/test/test.py @@ -1,4 +1,4 @@ -import shotpy +shotpy def solveProblem(problemFile, correctObjectiveValue): @@ -39,9 +39,9 @@ def solveProblem(problemFile, correctObjectiveValue): print("Solution " + solutions.index(solution).__str__()) print("\tObjective value " + str(solution.objValue)) print("\tSource " + str(solution.sourceType)) - #print("\tSource description: " + str(solution.sourceDescription)) # not working + print("\tSource description: " + str(solution.sourceDescription)) print("\tPoint " + str(solution.point)) - #print("\tMax deviation " + str(solution.maxDevatingConstraintNonlinear.value)) # not working + print("\tMax deviation " + str(solution.maxDevatingConstraintNonlinear.value)) objValue = solutions[0].objValue @@ -51,13 +51,13 @@ def solveProblem(problemFile, correctObjectiveValue): else: print("Objective value is correct") - stats = solver.getSetSolutionStatistics() + stats = solver.getSolutionStatistics() print("Number of iterations " + str(stats.numberOfIterations)) - #print(solver.getResultsOSrL()) + # print(solver.getResultsOSrL()) - #if (solver.getTerminationReason() != E_TerminationReason.AbsoluteGap): #not working - # print("Termination reason is AbsoluteGap") + terminationreason = solver.getTerminationReason() + print("Termination reason is:", terminationreason) if __name__ == '__main__': From cd24ca92d3e992008d7a8349090952f048df6828 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Tue, 1 Apr 2025 18:00:00 +0300 Subject: [PATCH 21/25] Only compile if HAS_PYTHON=ON --- src/Solver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index d8e71e66..91d5879c 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -8,8 +8,10 @@ Please see the README and LICENSE files for more information. */ +#ifdef HAS_PYTHON #include #include +#endif #include "Solver.h" @@ -2058,9 +2060,9 @@ E_TerminationReason Solver::getTerminationReason() { return (env->results->termi E_ModelReturnStatus Solver::getModelReturnStatus() { return (env->results->getModelReturnStatus()); } +#ifdef HAS_PYTHON namespace py = pybind11; - PYBIND11_MODULE(shotpy, m) { m.doc() = "shotpy"; @@ -2215,5 +2217,5 @@ PYBIND11_MODULE(shotpy, m) { .def("getNumberOfTotalDualProblems", &SolutionStatistics::getNumberOfTotalDualProblems) ; } - +#endif } // namespace SHOT From 69a624d1f1b722a49336c5433d75f9d2196948d4 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Wed, 2 Apr 2025 18:49:57 +0300 Subject: [PATCH 22/25] Minor fix --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3127515..3f8a95ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,6 @@ set(IPOPT_DIR "/opt/ipopt" CACHE STRING "The base directory where Ipopt is locat # Python option(HAS_PYTHON "Is Python available" OFF) -set(IPOPT_DIR "/opt/ipopt" CACHE STRING "The base directory where Ipopt is located (if available).") # Create also the executable option(GENERATE_EXE "Should the SHOT executable be generated (requires at least that either OS or GAMS is available)" From 57b2f806e7f1340586d1cd63a7ee3730d8ebc147 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sat, 3 May 2025 16:21:29 +0300 Subject: [PATCH 23/25] typo --- src/Solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Solver.cpp b/src/Solver.cpp index 91d5879c..0dba28be 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -2085,7 +2085,7 @@ PYBIND11_MODULE(shotpy, m) { .def("getSettingsAsMarkup", &Solver::getSettingsAsMarkup) .def("getBoolSetting", py::overload_cast(&Solver::getSetting)) - .def("getStringSetting", py::overload_cast(&Solver::getSetting)) + .def("getStringSetting", py::overload_cast(&Solver::getSetting)) .def("getIntSetting", py::overload_cast(&Solver::getSetting)) .def("getDoubleSetting", py::overload_cast(&Solver::getSetting)) From bdbf4d89f88205a6079da3b6321743bdcc4f90c1 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Sun, 4 May 2025 15:24:42 +0300 Subject: [PATCH 24/25] Split pybind11 to own cpp file --- CMakeLists.txt | 5 +- src/Shotpy.cpp | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Solver.cpp | 164 ------------------------------------- 3 files changed, 216 insertions(+), 168 deletions(-) create mode 100644 src/Shotpy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f8a95ce..10e2c0a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -433,7 +433,7 @@ target_link_libraries(SHOTResults SHOTModel) if(HAS_PYTHON) add_subdirectory(${PYBIND11_DIR}) - pybind11_add_module(shotpy src/Solver.cpp) + pybind11_add_module(shotpy src/Shotpy.cpp) target_link_libraries(shotpy PRIVATE SHOTSolver) endif(HAS_PYTHON) @@ -647,9 +647,6 @@ if(HAS_PYTHON) message("-- The following Python libraries will be used from: ${Python3_LIBRARIES}") message(" ${Python3_INCLUDE_DIRS}") -# Is this needed? -# add_definitions(-DHAS_PYTHON) - endif(HAS_PYTHON) # Extra flags for Visual Studio compilers diff --git a/src/Shotpy.cpp b/src/Shotpy.cpp new file mode 100644 index 00000000..ae48fa56 --- /dev/null +++ b/src/Shotpy.cpp @@ -0,0 +1,215 @@ +/** + The Supporting Hyperplane Optimization Toolkit (SHOT). + + @author Andreas Lundell, Åbo Akademi University + + @section LICENSE + This software is licensed under the Eclipse Public License 2.0. + Please see the README and LICENSE files for more information. +*/ + +#include +#include + +#include "Solver.h" + +#include "DualSolver.h" +#include "PrimalSolver.h" +#include "Report.h" +#include "Results.h" +#include "Settings.h" +#include "TaskHandler.h" +#include "Timing.h" +#include "Utilities.h" + +#ifdef HAS_GAMS +#include "ModelingSystem/ModelingSystemGAMS.h" +#endif +#ifdef HAS_AMPL +#include "ModelingSystem/ModelingSystemAMPL.h" +#endif +#include "ModelingSystem/ModelingSystemOSiL.h" + +#include "SolutionStrategy/SolutionStrategySingleTree.h" +#include "SolutionStrategy/SolutionStrategyMultiTree.h" +#include "SolutionStrategy/SolutionStrategyMIQCQP.h" +#include "SolutionStrategy/SolutionStrategyNLP.h" + +#include "../Tasks/TaskPerformBoundTightening.h" +#include "../Tasks/TaskReformulateProblem.h" + +#include + +#ifdef HAS_STD_FILESYSTEM +#include +namespace fs = std; +#endif + +#ifdef HAS_STD_EXPERIMENTAL_FILESYSTEM +#include +namespace fs = std::experimental; +#endif + +#ifdef HAS_GUROBI +#include "gurobi_c++.h" +#endif + +namespace SHOT +{ +namespace py = pybind11; + +PYBIND11_MODULE(shotpy, m) { + m.doc() = "shotpy"; + + py::class_(m, "Solver") + .def(py::init()) + .def("getAbsoluteObjectiveGap", &Solver::getAbsoluteObjectiveGap) + .def("getCurrentDualBound", &Solver::getCurrentDualBound) + .def("getModelReturnStatus", &Solver::getModelReturnStatus) + .def("getOptions", &Solver::getOptions) + .def("getOptionsOSoL", &Solver::getOptionsOSoL) + .def("getPrimalBound", &Solver::getPrimalBound) + .def("getPrimalSolution", &Solver::getPrimalSolution) + .def("getPrimalSolutions", &Solver::getPrimalSolutions) + .def("getRelativeObjectiveGap", &Solver::getRelativeObjectiveGap) + .def("getResultsOSrL", &Solver::getResultsOSrL) + .def("getResultsSol", &Solver::getResultsSol) + .def("getResultsTrace", &Solver::getResultsTrace) + + .def("getSolutionStatistics", &Solver::getSolutionStatistics) + .def("getSettingsAsMarkup", &Solver::getSettingsAsMarkup) + + .def("getBoolSetting", py::overload_cast(&Solver::getSetting)) + .def("getStringSetting", py::overload_cast(&Solver::getSetting)) + .def("getIntSetting", py::overload_cast(&Solver::getSetting)) + .def("getDoubleSetting", py::overload_cast(&Solver::getSetting)) + + .def("getTerminationReason", &Solver::getTerminationReason) + .def("hasPrimalSolution", &Solver::hasPrimalSolution) + + .def("outputSolverHeader", &Solver::outputSolverHeader) + .def("outputOptionsReport", &Solver::outputOptionsReport) + .def("outputProblemInstanceReport", &Solver::outputProblemInstanceReport) + .def("outputSolutionReport", &Solver::outputSolutionReport) + + .def("setLogFile", &Solver::setLogFile) + .def("setOptionsFromFile", &Solver::setOptionsFromFile) + .def("setOptionsFromOSoL", &Solver::setOptionsFromOSoL) + .def("setOptionsFromString", &Solver::setOptionsFromString) + .def("setProblem", py::overload_cast(&Solver::setProblem)) + .def("solveProblem", &Solver::solveProblem) + .def("updateLogLevels", &Solver::updateLogLevels) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + .def("updateSetting", py::overload_cast(&Solver::updateSetting)) + ; + + py::enum_(m, "PrimalSolutionSource", py::arithmetic()) + .value("Rootsearch", E_PrimalSolutionSource::Rootsearch) + .value("RootsearchFixedIntegers", E_PrimalSolutionSource::RootsearchFixedIntegers) + .value("NLPFixedIntegers", E_PrimalSolutionSource::NLPFixedIntegers) + .value("NLPRelaxed", E_PrimalSolutionSource::NLPRelaxed) + .value("MIPSolutionPool", E_PrimalSolutionSource::MIPSolutionPool) + .value("LPFixedIntegers", E_PrimalSolutionSource::LPFixedIntegers) + .value("MIPCallback", E_PrimalSolutionSource::MIPCallback) + .value("InteriorPointSearch", E_PrimalSolutionSource::InteriorPointSearch) + ; + + py::enum_(m, "ModelReturnStatus", py::arithmetic()) + .value("None", E_ModelReturnStatus::None) + .value("OptimalGlobal", E_ModelReturnStatus::OptimalGlobal) + .value("Unbounded", E_ModelReturnStatus::Unbounded) + .value("UnboundedNoSolution", E_ModelReturnStatus::UnboundedNoSolution) + .value("InfeasibleGlobal", E_ModelReturnStatus::InfeasibleGlobal) + .value("InfeasibleLocal", E_ModelReturnStatus::InfeasibleLocal) + .value("FeasibleSolution", E_ModelReturnStatus::FeasibleSolution) + .value("NoSolutionReturned", E_ModelReturnStatus::NoSolutionReturned) + .value("ErrorUnknown", E_ModelReturnStatus::ErrorUnknown) + .value("ErrorNoSolution", E_ModelReturnStatus::ErrorNoSolution) + ; + + py::enum_(m, "TerminationReason", py::arithmetic()) + .value("ConstraintTolerance", E_TerminationReason::ConstraintTolerance) + .value("ObjectiveStagnation", E_TerminationReason::ObjectiveStagnation) + .value("IterationLimit", E_TerminationReason::IterationLimit) + .value("TimeLimit", E_TerminationReason::TimeLimit) + .value("InfeasibleProblem", E_TerminationReason::InfeasibleProblem) + .value("UnboundedProblem", E_TerminationReason::UnboundedProblem) + .value("Error", E_TerminationReason::Error) + .value("AbsoluteGap", E_TerminationReason::AbsoluteGap) + .value("RelativeGap", E_TerminationReason::RelativeGap) + .value("UserAbort", E_TerminationReason::UserAbort) + .value("NoDualCutsAdded", E_TerminationReason::NoDualCutsAdded) + .value("None", E_TerminationReason::None) + .value("NumericIssues", E_TerminationReason::NumericIssues) + ; + + py::class_(m, "PairIndexValue") + .def_readwrite("index", &PairIndexValue::index) + .def_readwrite("value", &PairIndexValue::value) + ; + + py::class_(m, "PrimalSolution") + .def_readwrite("point", &PrimalSolution::point) + .def_readwrite("sourceType", &PrimalSolution::sourceType) + .def_readwrite("sourceDescription", &PrimalSolution::sourceDescription) + .def_readwrite("objValue", &PrimalSolution::objValue) + .def_readwrite("iterFound", &PrimalSolution::iterFound) + .def_readwrite("maxDevatingConstraintLinear", &PrimalSolution::maxDevatingConstraintLinear) + .def_readwrite("maxDevatingConstraintQuadratic", &PrimalSolution::maxDevatingConstraintQuadratic) + .def_readwrite("maxDevatingConstraintNonlinear", &PrimalSolution::maxDevatingConstraintNonlinear) + .def_readwrite("maxIntegerToleranceError", &PrimalSolution::maxIntegerToleranceError) + .def_readwrite("boundProjectionPerformed", &PrimalSolution::boundProjectionPerformed) + .def_readwrite("integerRoundingPerformed", &PrimalSolution::integerRoundingPerformed) + .def_readwrite("displayed", &PrimalSolution::displayed) + ; + + py::class_(m, "SolutionStatistics") + .def_readwrite("numberOfIterations", &SolutionStatistics::numberOfIterations) + .def_readwrite("numberOfProblemsLP", &SolutionStatistics::numberOfProblemsLP) + .def_readwrite("numberOfProblemsQP ", &SolutionStatistics::numberOfProblemsQP) + .def_readwrite("numberOfProblemsQCQP", &SolutionStatistics::numberOfProblemsQCQP) + .def_readwrite("numberOfProblemsFeasibleMILP", &SolutionStatistics::numberOfProblemsFeasibleMILP) + .def_readwrite("numberOfProblemsOptimalMILP", &SolutionStatistics::numberOfProblemsOptimalMILP) + .def_readwrite("numberOfProblemsFeasibleMIQP", &SolutionStatistics::numberOfProblemsFeasibleMIQP) + .def_readwrite("numberOfProblemsOptimalMIQP", &SolutionStatistics::numberOfProblemsOptimalMIQP) + .def_readwrite("numberOfProblemsFeasibleMIQCQP", &SolutionStatistics::numberOfProblemsFeasibleMIQCQP) + .def_readwrite("numberOfProblemsOptimalMIQCQP", &SolutionStatistics::numberOfProblemsOptimalMIQCQP) + .def_readwrite("numberOfFunctionEvalutions", &SolutionStatistics::numberOfFunctionEvalutions) + .def_readwrite("numberOfGradientEvaluations", &SolutionStatistics::numberOfGradientEvaluations) + .def_readwrite("numberOfProblemsMinimaxLP", &SolutionStatistics::numberOfProblemsMinimaxLP) + .def_readwrite("numberOfProblemsFixedNLP", &SolutionStatistics::numberOfProblemsFixedNLP) + .def_readwrite("numberOfConstraintsRemovedInPresolve", &SolutionStatistics::numberOfConstraintsRemovedInPresolve) + .def_readwrite("numberOfVariableBoundsTightenedInPresolve", &SolutionStatistics::numberOfVariableBoundsTightenedInPresolve) + .def_readwrite("numberOfHyperplanesWithConvexSource", &SolutionStatistics::numberOfHyperplanesWithConvexSource) + .def_readwrite("numberOfHyperplanesWithNonconvexSource", &SolutionStatistics::numberOfHyperplanesWithNonconvexSource) + .def_readwrite("numberOfIntegerCuts", &SolutionStatistics::numberOfIntegerCuts) + .def_readwrite("numberOfIterationsWithDualStagnation", &SolutionStatistics::numberOfIterationsWithDualStagnation) + .def_readwrite("lastIterationWithSignificantDualUpdate", &SolutionStatistics::lastIterationWithSignificantDualUpdate) + .def_readwrite("numberOfIterationsWithPrimalStagnation", &SolutionStatistics::numberOfIterationsWithPrimalStagnation) + .def_readwrite("lastIterationWithSignificantPrimalUpdate", &SolutionStatistics::lastIterationWithSignificantPrimalUpdate) + .def_readwrite("numberOfIterationsWithoutNLPCallMIP", &SolutionStatistics::numberOfIterationsWithoutNLPCallMIP) + .def_readwrite("iterationLastPrimalBoundUpdate", &SolutionStatistics::iterationLastPrimalBoundUpdate) + .def_readwrite("iterationLastDualBoundUpdate", &SolutionStatistics::iterationLastDualBoundUpdate) + .def_readwrite("iterationLastLazyAdded", &SolutionStatistics::iterationLastLazyAdded) + .def_readwrite("iterationLastDualCutAdded", &SolutionStatistics::iterationLastDualCutAdded) + .def_readwrite("timeLastDualBoundUpdate", &SolutionStatistics::timeLastDualBoundUpdate) + .def_readwrite("timeLastFixedNLPCall", &SolutionStatistics::timeLastFixedNLPCall) + .def_readwrite("numberOfOriginalInteriorPoints", &SolutionStatistics::numberOfOriginalInteriorPoints) + .def_readwrite("numberOfFoundPrimalSolutions", &SolutionStatistics::numberOfFoundPrimalSolutions) + .def_readwrite("numberOfExploredNodes", &SolutionStatistics::numberOfExploredNodes) + .def_readwrite("numberOfOpenNodes", &SolutionStatistics::numberOfOpenNodes) + .def_readwrite("numberOfPrimalReductionCutsUpdatesWithoutEffect", &SolutionStatistics::numberOfPrimalReductionCutsUpdatesWithoutEffect) + .def_readwrite("numberOfDualRepairsSinceLastPrimalUpdate", &SolutionStatistics::numberOfDualRepairsSinceLastPrimalUpdate) + .def_readwrite("numberOfPrimalReductionsPerformed", &SolutionStatistics::numberOfPrimalReductionsPerformed) + .def_readwrite("numberOfSuccessfulDualRepairsPerformed", &SolutionStatistics::numberOfSuccessfulDualRepairsPerformed) + .def_readwrite("numberOfUnsuccessfulDualRepairsPerformed", &SolutionStatistics::numberOfUnsuccessfulDualRepairsPerformed) + .def_readwrite("numberOfPrimalImprovementsAfterInfeasibilityRepair", &SolutionStatistics::numberOfPrimalImprovementsAfterInfeasibilityRepair) + .def_readwrite("numberOfPrimalImprovementsAfterReductionCut", &SolutionStatistics::numberOfPrimalImprovementsAfterReductionCut) + .def_readwrite("hasInfeasibilityRepairBeenPerformedSincePrimalImprovement", &SolutionStatistics::hasInfeasibilityRepairBeenPerformedSincePrimalImprovement) + .def_readwrite("hasReductionCutBeenAddedSincePrimalImprovement", &SolutionStatistics::hasReductionCutBeenAddedSincePrimalImprovement) + .def("getNumberOfTotalDualProblems", &SolutionStatistics::getNumberOfTotalDualProblems) + ; +} +} diff --git a/src/Solver.cpp b/src/Solver.cpp index 0dba28be..5ae811f2 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -8,11 +8,6 @@ Please see the README and LICENSE files for more information. */ -#ifdef HAS_PYTHON -#include -#include -#endif - #include "Solver.h" #include "DualSolver.h" @@ -2059,163 +2054,4 @@ E_TerminationReason Solver::getTerminationReason() { return (env->results->termi E_ModelReturnStatus Solver::getModelReturnStatus() { return (env->results->getModelReturnStatus()); } - -#ifdef HAS_PYTHON -namespace py = pybind11; - -PYBIND11_MODULE(shotpy, m) { - m.doc() = "shotpy"; - - py::class_(m, "Solver") - .def(py::init()) - .def("getAbsoluteObjectiveGap", &Solver::getAbsoluteObjectiveGap) - .def("getCurrentDualBound", &Solver::getCurrentDualBound) - .def("getModelReturnStatus", &Solver::getModelReturnStatus) - .def("getOptions", &Solver::getOptions) - .def("getOptionsOSoL", &Solver::getOptionsOSoL) - .def("getPrimalBound", &Solver::getPrimalBound) - .def("getPrimalSolution", &Solver::getPrimalSolution) - .def("getPrimalSolutions", &Solver::getPrimalSolutions) - .def("getRelativeObjectiveGap", &Solver::getRelativeObjectiveGap) - .def("getResultsOSrL", &Solver::getResultsOSrL) - .def("getResultsSol", &Solver::getResultsSol) - .def("getResultsTrace", &Solver::getResultsTrace) - - .def("getSolutionStatistics", &Solver::getSolutionStatistics) - .def("getSettingsAsMarkup", &Solver::getSettingsAsMarkup) - - .def("getBoolSetting", py::overload_cast(&Solver::getSetting)) - .def("getStringSetting", py::overload_cast(&Solver::getSetting)) - .def("getIntSetting", py::overload_cast(&Solver::getSetting)) - .def("getDoubleSetting", py::overload_cast(&Solver::getSetting)) - - .def("getTerminationReason", &Solver::getTerminationReason) - .def("hasPrimalSolution", &Solver::hasPrimalSolution) - - .def("outputSolverHeader", &Solver::outputSolverHeader) - .def("outputOptionsReport", &Solver::outputOptionsReport) - .def("outputProblemInstanceReport", &Solver::outputProblemInstanceReport) - .def("outputSolutionReport", &Solver::outputSolutionReport) - - .def("setLogFile", &Solver::setLogFile) - .def("setOptionsFromFile", &Solver::setOptionsFromFile) - .def("setOptionsFromOSoL", &Solver::setOptionsFromOSoL) - .def("setOptionsFromString", &Solver::setOptionsFromString) - .def("setProblem", py::overload_cast(&Solver::setProblem)) - .def("solveProblem", &Solver::solveProblem) - .def("updateLogLevels", &Solver::updateLogLevels) - .def("updateSetting", py::overload_cast(&Solver::updateSetting)) - .def("updateSetting", py::overload_cast(&Solver::updateSetting)) - .def("updateSetting", py::overload_cast(&Solver::updateSetting)) - .def("updateSetting", py::overload_cast(&Solver::updateSetting)) - ; - - py::enum_(m, "PrimalSolutionSource", py::arithmetic()) - .value("Rootsearch", E_PrimalSolutionSource::Rootsearch) - .value("RootsearchFixedIntegers", E_PrimalSolutionSource::RootsearchFixedIntegers) - .value("NLPFixedIntegers", E_PrimalSolutionSource::NLPFixedIntegers) - .value("NLPRelaxed", E_PrimalSolutionSource::NLPRelaxed) - .value("MIPSolutionPool", E_PrimalSolutionSource::MIPSolutionPool) - .value("LPFixedIntegers", E_PrimalSolutionSource::LPFixedIntegers) - .value("MIPCallback", E_PrimalSolutionSource::MIPCallback) - .value("InteriorPointSearch", E_PrimalSolutionSource::InteriorPointSearch) - ; - - py::enum_(m, "ModelReturnStatus", py::arithmetic()) - .value("None", E_ModelReturnStatus::None) - .value("OptimalGlobal", E_ModelReturnStatus::OptimalGlobal) - .value("Unbounded", E_ModelReturnStatus::Unbounded) - .value("UnboundedNoSolution", E_ModelReturnStatus::UnboundedNoSolution) - .value("InfeasibleGlobal", E_ModelReturnStatus::InfeasibleGlobal) - .value("InfeasibleLocal", E_ModelReturnStatus::InfeasibleLocal) - .value("FeasibleSolution", E_ModelReturnStatus::FeasibleSolution) - .value("NoSolutionReturned", E_ModelReturnStatus::NoSolutionReturned) - .value("ErrorUnknown", E_ModelReturnStatus::ErrorUnknown) - .value("ErrorNoSolution", E_ModelReturnStatus::ErrorNoSolution) - ; - - py::enum_(m, "TerminationReason", py::arithmetic()) - .value("ConstraintTolerance", E_TerminationReason::ConstraintTolerance) - .value("ObjectiveStagnation", E_TerminationReason::ObjectiveStagnation) - .value("IterationLimit", E_TerminationReason::IterationLimit) - .value("TimeLimit", E_TerminationReason::TimeLimit) - .value("InfeasibleProblem", E_TerminationReason::InfeasibleProblem) - .value("UnboundedProblem", E_TerminationReason::UnboundedProblem) - .value("Error", E_TerminationReason::Error) - .value("AbsoluteGap", E_TerminationReason::AbsoluteGap) - .value("RelativeGap", E_TerminationReason::RelativeGap) - .value("UserAbort", E_TerminationReason::UserAbort) - .value("NoDualCutsAdded", E_TerminationReason::NoDualCutsAdded) - .value("None", E_TerminationReason::None) - .value("NumericIssues", E_TerminationReason::NumericIssues) - ; - - py::class_(m, "PairIndexValue") - .def_readwrite("index", &PairIndexValue::index) - .def_readwrite("value", &PairIndexValue::value) - ; - - py::class_(m, "PrimalSolution") - .def_readwrite("point", &PrimalSolution::point) - .def_readwrite("sourceType", &PrimalSolution::sourceType) - .def_readwrite("sourceDescription", &PrimalSolution::sourceDescription) - .def_readwrite("objValue", &PrimalSolution::objValue) - .def_readwrite("iterFound", &PrimalSolution::iterFound) - .def_readwrite("maxDevatingConstraintLinear", &PrimalSolution::maxDevatingConstraintLinear) - .def_readwrite("maxDevatingConstraintQuadratic", &PrimalSolution::maxDevatingConstraintQuadratic) - .def_readwrite("maxDevatingConstraintNonlinear", &PrimalSolution::maxDevatingConstraintNonlinear) - .def_readwrite("maxIntegerToleranceError", &PrimalSolution::maxIntegerToleranceError) - .def_readwrite("boundProjectionPerformed", &PrimalSolution::boundProjectionPerformed) - .def_readwrite("integerRoundingPerformed", &PrimalSolution::integerRoundingPerformed) - .def_readwrite("displayed", &PrimalSolution::displayed) - ; - - py::class_(m, "SolutionStatistics") - .def_readwrite("numberOfIterations", &SolutionStatistics::numberOfIterations) - .def_readwrite("numberOfProblemsLP", &SolutionStatistics::numberOfProblemsLP) - .def_readwrite("numberOfProblemsQP ", &SolutionStatistics::numberOfProblemsQP) - .def_readwrite("numberOfProblemsQCQP", &SolutionStatistics::numberOfProblemsQCQP) - .def_readwrite("numberOfProblemsFeasibleMILP", &SolutionStatistics::numberOfProblemsFeasibleMILP) - .def_readwrite("numberOfProblemsOptimalMILP", &SolutionStatistics::numberOfProblemsOptimalMILP) - .def_readwrite("numberOfProblemsFeasibleMIQP", &SolutionStatistics::numberOfProblemsFeasibleMIQP) - .def_readwrite("numberOfProblemsOptimalMIQP", &SolutionStatistics::numberOfProblemsOptimalMIQP) - .def_readwrite("numberOfProblemsFeasibleMIQCQP", &SolutionStatistics::numberOfProblemsFeasibleMIQCQP) - .def_readwrite("numberOfProblemsOptimalMIQCQP", &SolutionStatistics::numberOfProblemsOptimalMIQCQP) - .def_readwrite("numberOfFunctionEvalutions", &SolutionStatistics::numberOfFunctionEvalutions) - .def_readwrite("numberOfGradientEvaluations", &SolutionStatistics::numberOfGradientEvaluations) - .def_readwrite("numberOfProblemsMinimaxLP", &SolutionStatistics::numberOfProblemsMinimaxLP) - .def_readwrite("numberOfProblemsFixedNLP", &SolutionStatistics::numberOfProblemsFixedNLP) - .def_readwrite("numberOfConstraintsRemovedInPresolve", &SolutionStatistics::numberOfConstraintsRemovedInPresolve) - .def_readwrite("numberOfVariableBoundsTightenedInPresolve", &SolutionStatistics::numberOfVariableBoundsTightenedInPresolve) - .def_readwrite("numberOfHyperplanesWithConvexSource", &SolutionStatistics::numberOfHyperplanesWithConvexSource) - .def_readwrite("numberOfHyperplanesWithNonconvexSource", &SolutionStatistics::numberOfHyperplanesWithNonconvexSource) - .def_readwrite("numberOfIntegerCuts", &SolutionStatistics::numberOfIntegerCuts) - .def_readwrite("numberOfIterationsWithDualStagnation", &SolutionStatistics::numberOfIterationsWithDualStagnation) - .def_readwrite("lastIterationWithSignificantDualUpdate", &SolutionStatistics::lastIterationWithSignificantDualUpdate) - .def_readwrite("numberOfIterationsWithPrimalStagnation", &SolutionStatistics::numberOfIterationsWithPrimalStagnation) - .def_readwrite("lastIterationWithSignificantPrimalUpdate", &SolutionStatistics::lastIterationWithSignificantPrimalUpdate) - .def_readwrite("numberOfIterationsWithoutNLPCallMIP", &SolutionStatistics::numberOfIterationsWithoutNLPCallMIP) - .def_readwrite("iterationLastPrimalBoundUpdate", &SolutionStatistics::iterationLastPrimalBoundUpdate) - .def_readwrite("iterationLastDualBoundUpdate", &SolutionStatistics::iterationLastDualBoundUpdate) - .def_readwrite("iterationLastLazyAdded", &SolutionStatistics::iterationLastLazyAdded) - .def_readwrite("iterationLastDualCutAdded", &SolutionStatistics::iterationLastDualCutAdded) - .def_readwrite("timeLastDualBoundUpdate", &SolutionStatistics::timeLastDualBoundUpdate) - .def_readwrite("timeLastFixedNLPCall", &SolutionStatistics::timeLastFixedNLPCall) - .def_readwrite("numberOfOriginalInteriorPoints", &SolutionStatistics::numberOfOriginalInteriorPoints) - .def_readwrite("numberOfFoundPrimalSolutions", &SolutionStatistics::numberOfFoundPrimalSolutions) - .def_readwrite("numberOfExploredNodes", &SolutionStatistics::numberOfExploredNodes) - .def_readwrite("numberOfOpenNodes", &SolutionStatistics::numberOfOpenNodes) - .def_readwrite("numberOfPrimalReductionCutsUpdatesWithoutEffect", &SolutionStatistics::numberOfPrimalReductionCutsUpdatesWithoutEffect) - .def_readwrite("numberOfDualRepairsSinceLastPrimalUpdate", &SolutionStatistics::numberOfDualRepairsSinceLastPrimalUpdate) - .def_readwrite("numberOfPrimalReductionsPerformed", &SolutionStatistics::numberOfPrimalReductionsPerformed) - .def_readwrite("numberOfSuccessfulDualRepairsPerformed", &SolutionStatistics::numberOfSuccessfulDualRepairsPerformed) - .def_readwrite("numberOfUnsuccessfulDualRepairsPerformed", &SolutionStatistics::numberOfUnsuccessfulDualRepairsPerformed) - .def_readwrite("numberOfPrimalImprovementsAfterInfeasibilityRepair", &SolutionStatistics::numberOfPrimalImprovementsAfterInfeasibilityRepair) - .def_readwrite("numberOfPrimalImprovementsAfterReductionCut", &SolutionStatistics::numberOfPrimalImprovementsAfterReductionCut) - .def_readwrite("hasInfeasibilityRepairBeenPerformedSincePrimalImprovement", &SolutionStatistics::hasInfeasibilityRepairBeenPerformedSincePrimalImprovement) - .def_readwrite("hasReductionCutBeenAddedSincePrimalImprovement", &SolutionStatistics::hasReductionCutBeenAddedSincePrimalImprovement) - .def("getNumberOfTotalDualProblems", &SolutionStatistics::getNumberOfTotalDualProblems) - ; -} -#endif } // namespace SHOT From d3c3ed96bc939ea5181f796f5e4e0023bfb4dd73 Mon Sep 17 00:00:00 2001 From: Johan Ekman Date: Wed, 4 Jun 2025 19:47:35 +0300 Subject: [PATCH 25/25] Modify test --- test/shotpy.py | 59 -------------------------------------------------- test/test.py | 20 ++++++++++++----- 2 files changed, 15 insertions(+), 64 deletions(-) delete mode 100644 test/shotpy.py diff --git a/test/shotpy.py b/test/shotpy.py deleted file mode 100644 index c1c79f37..00000000 --- a/test/shotpy.py +++ /dev/null @@ -1,59 +0,0 @@ - -from build import shotpy - -if __name__ == '__main__': - solver = shotpy.Solver() - - # solver.setOptionsFromFile('filename') - - print('getSettingsAsMarkup', solver.getSettingsAsMarkup()) - - solver.setLogFile('testlogfile.txt') - - solver.updateLogLevels() - - solver.setProblem('test/data/alan.osil') - - solver.solveProblem() - - solver.outputSolverHeader() - solver.outputOptionsReport() - solver.outputProblemInstanceReport() - solver.outputSolutionReport() - - print('getOptionsOSol()', solver.getOptionsOSoL()) - - print('getOptions()', solver.getOptions()) - - print('getResultsOSrL', solver.getResultsOSrL()) - print('getResultsTrace', solver.getResultsTrace()) - print('getResultsSol', solver.getResultsSol()) - - - print('getCurrentDualBound', solver.getCurrentDualBound()) - print('getPrimalBound', solver.getPrimalBound()) - print('getAbsoluteObjectiveGap', solver.getAbsoluteObjectiveGap()) - print('getRelativeObjectiveGap', solver.getRelativeObjectiveGap()) - - - print('hasPrimalSolution', solver.hasPrimalSolution()) - primalsolution = solver.getPrimalSolution() - print('primalsolution.point', primalsolution.point) - print('primalsolution.sourceType', primalsolution.sourceType) - print('primalsolution.objValue', primalsolution.objValue) - print('primalsolution.displayed', primalsolution.displayed) - - primalsolutions = solver.getPrimalSolutions() - for primalsolution in primalsolutions: - print('primalsolution.point', primalsolution.point) - print('primalsolution.sourceType', primalsolution.sourceType) - print('primalsolution.objValue', primalsolution.objValue) - print('primalsolution.displayed', primalsolution.displayed) - - print('getModelReturnStatus', solver.getModelReturnStatus()) - print('getTerminationReason', solver.getTerminationReason()) - - getsolutionstatistics = solver.getSolutionStatistics() - print('numberofiterations', getsolutionstatistics.numberOfIterations) - - solver.updateSetting('name', 'category', False) diff --git a/test/test.py b/test/test.py index 1fcee408..2d52cc71 100644 --- a/test/test.py +++ b/test/test.py @@ -1,4 +1,14 @@ -shotpy +import os, sys +from pathlib import Path + +# Establish parent folders and add build folder to sys.path +parent = str(Path(__file__).absolute().parent.parent) +build_folder = os.path.join(parent, 'build') +sys.path.append(build_folder) + +# Import shotpy, which in this case is shotpy lib file from build folder +import shotpy + def solveProblem(problemFile, correctObjectiveValue): @@ -17,7 +27,7 @@ def solveProblem(problemFile, correctObjectiveValue): else: print("Problem file read from " + problemFile) - + solver.outputProblemInstanceReport() if (not solver.solveProblem()): @@ -61,6 +71,6 @@ def solveProblem(problemFile, correctObjectiveValue): if __name__ == '__main__': - solveProblem('test/data/alan.osil', 2.925) - solveProblem('test/data/meanvarxsc.osil', 14.36923211) - solveProblem('test/data/fo7_2.osil', 17.74934573) + solveProblem('data/alan.osil', 2.925) + solveProblem('data/meanvarxsc.osil', 14.36923211) + solveProblem('data/fo7_2.osil', 17.74934573)