Skip to content

WIP: LP Justifications #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft

WIP: LP Justifications #72

wants to merge 28 commits into from

Conversation

mmcilree
Copy link
Collaborator

At the moment among_test.cc is catastrophically broken and really can't figure out what I'm screwing up

among (1, 3) [2, 4, 6, 8] [variant((1, 10)), variant((1, 10)), variant((1, 10))]  with proofs: expecting 784 solutions
among_test(98136,0x104794580) malloc: Heap corruption detected, free list is damaged at 0x6000034f3590
*** Incorrect guard value: 261878723984
among_test(98136,0x104794580) malloc: *** set a breakpoint in malloc_error_break to debug

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

@mmcilree mmcilree requested a review from ciaranm March 11, 2025 15:00
@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

Are you using soplex still? It's causing build issues...

@mmcilree
Copy link
Collaborator Author

Got rid of soplex - will see if that helps

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

I've had to hack HiGHS to get it to build for me, because I don't understand CMake. I'm fairly sure I know what the problem is, though (or at least one of the problems). Looks like you're storing a constraint state of an optional<HighsBasis>, and that that doesn't work because stuff needs to be copyable. I'm going to try changing it to a shared_ptr and see what happens.

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

OK so this is enough hackery to get among_TEST to build on my machine and to run cleanly, although it'll fail to verify some proofs:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 42d3f27..aea1ce8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,7 +17,7 @@ include(CheckCXXCompilerFlag)
 unset(COMPILER_SUPPORTS_MARCH_NATIVE CACHE)
 CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE)
 if (COMPILER_SUPPORTS_MARCH_NATIVE)
-    add_compile_options(-march=native)
+    #    add_compile_options(-march=native)
 endif (COMPILER_SUPPORTS_MARCH_NATIVE)
 
 unset(COMPILER_SUPPORTS_NO_RESTRICT CACHE)
@@ -46,11 +46,11 @@ add_compile_options(-pthread)
 add_link_options(-pthread)
 
 if (NOT GCS_DEBUG_MODE)
-    add_compile_options(-O3)
+    add_compile_options(-O1)
 endif (NOT GCS_DEBUG_MODE)
 
 if (GCS_DEBUG_MODE)
-    add_compile_options(-O0)
+    #    add_compile_options(-O0)
     #    set(ENV{ASAN_OPTIONS} "detect_leaks=1")
     #    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g")
 
@@ -63,8 +63,7 @@ option(GCS_ENABLE_DOXYGEN "Add a doxygen target to generate the documentation" O
 option(GCS_ENABLE_XCSP "Support for the XCSP modelling language" ON)
 option(GCS_ENABLE_MINIZINC "Support for the MiniZinc modelling language" ON)
 option(GCS_ENABLE_EXAMPLES "Build examples" ON)
-option(GCS_DEBUG_MODE "Disable compiler optimisation for debugging" ON)
-option(GCS_ENABLE_SOPLEX "(Experimental) Required for WIP LP justifications." ON)
+option(GCS_DEBUG_MODE "Disable compiler optimisation for debugging" OFF)
 option(GCS_ENABLE_HIGHS "(Experimental) Required for WIP LP justifications." ON)
 
 include_directories(.)
@@ -146,30 +145,17 @@ if (GCS_ENABLE_MINIZINC)
     FetchContent_MakeAvailable(json)
 endif (GCS_ENABLE_MINIZINC)
 
-if (GCS_ENABLE_SOPLEX)
-    FetchContent_Declare(
-            soplex
-            GIT_REPOSITORY https://github.com/scipopt/soplex.git
-            GIT_TAG release-712
-    )
-    block()
-        set(CMAKE_CXX_STANDARD 17)
-        set(CMAKE_CXX_STANDARD_REQUIRED ON)
-        set(CMAKE_CXX_EXTENSIONS ON)
-        FetchContent_MakeAvailable(soplex)
-    endblock()
-endif (GCS_ENABLE_SOPLEX)
-
 if (GCS_ENABLE_HIGHS)
     block()
         set(CMAKE_BUILD_TYPE Debug)
         set(DEBUG_MEMORY "Thread")
-        FetchContent_Declare(
+        ExternalProject_Add(
                 HiGHS
+                PREFIX ${CMAKE_SOURCE_DIR}/HiGHS
                 GIT_REPOSITORY https://github.com/ERGO-Code/HiGHS.git
                 GIT_TAG v1.8.1
+                INSTALL_COMMAND ""
         )
-        FetchContent_MakeAvailable(HiGHS)
     endblock()
 endif (GCS_ENABLE_HIGHS)
 
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 131c427..e0bdf21 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 include_directories(${CMAKE_SOURCE_DIR}/generator/src/generator/include)
+link_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build/lib)
 add_subdirectory(auto_table)
 add_subdirectory(black_hole)
 add_subdirectory(cake)
@@ -28,7 +29,4 @@ add_subdirectory(tour)
 add_subdirectory(talent)
 add_subdirectory(tutorial_proof)
 
-# Temporary for messing around
-add_subdirectory(soplex_test)
-
 add_subdirectory(highs_test)
diff --git a/examples/highs_test/CMakeLists.txt b/examples/highs_test/CMakeLists.txt
index b7292ab..dd54222 100644
--- a/examples/highs_test/CMakeLists.txt
+++ b/examples/highs_test/CMakeLists.txt
@@ -1,7 +1,10 @@
 add_executable(highs_test highs_test.cc)
 target_link_libraries(highs_test PUBLIC glasgow_constraint_solver)
 
+include_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS/src)
+include_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build)
+
 find_package(Boost 1.74 COMPONENTS program_options REQUIRED)
 include_directories(${Boost_INCLUDE_DIR})
 target_link_libraries(highs_test LINK_PUBLIC ${Boost_LIBRARIES})
-target_link_libraries(highs_test LINK_PUBLIC highs)
\ No newline at end of file
+target_link_libraries(highs_test LINK_PUBLIC highs)
diff --git a/gcs/CMakeLists.txt b/gcs/CMakeLists.txt
index ab60c36..4743f75 100644
--- a/gcs/CMakeLists.txt
+++ b/gcs/CMakeLists.txt
@@ -70,6 +70,9 @@ add_library(glasgow_constraint_solver
 )
 
 include_directories(${CMAKE_SOURCE_DIR}/generator/src/generator/include)
+include_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS/src)
+include_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build)
+link_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build/lib)
 
 # This appears to be necessary for the python install to work,
 # Not sure why exactly...
diff --git a/gcs/innards/proofs/lp_justifier.cc b/gcs/innards/proofs/lp_justifier.cc
index 9f8aa42..b31c43c 100644
--- a/gcs/innards/proofs/lp_justifier.cc
+++ b/gcs/innards/proofs/lp_justifier.cc
@@ -15,12 +15,14 @@
 using std::iota;
 using std::make_optional;
 using std::make_pair;
+using std::make_shared;
 using std::map;
 using std::max;
 using std::move;
 using std::nullopt;
 using std::optional;
 using std::pair;
+using std::shared_ptr;
 using std::stringstream;
 using std::to_string;
 using std::tuple;
@@ -246,7 +248,7 @@ struct LPJustifier::Imp
 
     auto pass_and_solve_model(const WeightedPseudoBooleanLessEqual & inference,
         HighsModel & restricted_model, vector<double> rhs_updated, vector<long> new_row_num, vector<long> old_row_num,
-        optional<HighsBasis> & optional_last_basis, optional<HighsBasis> & optional_current_basis) -> const HighsSolution
+        shared_ptr<HighsBasis> & optional_last_basis, shared_ptr<HighsBasis> & optional_current_basis) -> const HighsSolution
     {
         HighsStatus return_status;
         bool inferring_contradiction = inference.lhs.terms.empty() && inference.rhs <= -1_i;
@@ -307,13 +309,13 @@ struct LPJustifier::Imp
 
         // Save the basis for next time
         if (optional_last_basis) {
-            auto new_basis = this->highs.getBasis();
-            for (unsigned int row = 0; row < new_basis.row_status.size(); ++row) {
-                optional_last_basis->row_status[old_row_num[row]] = new_basis.row_status[row];
-            }
+//            auto new_basis = this->highs.getBasis();
+//            for (unsigned int row = 0; row < new_basis.row_status.size(); ++row) {
+//                optional_last_basis->row_status[old_row_num[row]] = new_basis.row_status[row];
+//            }
         }
         else {
-            optional_last_basis = highs.getBasis();
+//            optional_last_basis.reset(new HighsBasis(highs.getBasis()));
         }
 
         // Check it worked
@@ -481,7 +483,7 @@ void LPJustifier::initialise_with_vars(State & state, vector<IntegerVariableID>
     _imp->model.lp_.a_matrix_.value_ = value;
     _imp->model.lp_.setMatrixDimensions();
 
-    optional<HighsBasis> optional_last_basis = nullopt;
+    shared_ptr<HighsBasis> optional_last_basis = nullptr;
     _imp->last_basis_handle = state.add_constraint_state(optional_last_basis);
 }
 
@@ -530,8 +532,9 @@ auto LPJustifier::compute_justification(const State & state, ProofLogger & logge
     auto restricted_model = _imp->model;
     auto rhs_updated = _imp->constraints_rhs;
 
-    auto & optional_last_basis = any_cast<optional<HighsBasis> &>(state.get_constraint_state(_imp->last_basis_handle));
-    optional<HighsBasis> optional_current_basis = optional_last_basis ? make_optional(HighsBasis{}) : nullopt;
+    shared_ptr<HighsBasis> optional_last_basis, optional_current_basis;
+//    auto & optional_last_basis = any_cast<shared_ptr<HighsBasis> &>(state.get_constraint_state(_imp->last_basis_handle));
+//    shared_ptr<HighsBasis> optional_current_basis = optional_last_basis ? make_shared<HighsBasis>() : shared_ptr<HighsBasis>();
 
     vector<HighsInt> mask;
     vector<long> new_row_num(restricted_model.lp_.num_row_, 0);
@@ -563,15 +566,15 @@ auto LPJustifier::compute_justification(const State & state, ProofLogger & logge
     create(to_delete, mask.data(), restricted_model.lp_.num_row_);
     restricted_model.lp_.deleteRows(to_delete);
 
-    if (optional_current_basis) {
-        // Set the current basis based on the last basis, excluding deleted rows
-        optional_current_basis->col_status = optional_last_basis->col_status;
-        optional_current_basis->row_status = optional_last_basis->row_status;
-        for (unsigned int row = 0; row < optional_current_basis->row_status.size(); ++row) {
-            if (mask[row] == 1)
-                optional_current_basis->row_status[row] = HighsBasisStatus::kNonbasic;
-        }
-    }
+//    if (optional_current_basis) {
+//        // Set the current basis based on the last basis, excluding deleted rows
+//        optional_current_basis->col_status = optional_last_basis->col_status;
+//        optional_current_basis->row_status = optional_last_basis->row_status;
+//        for (unsigned int row = 0; row < optional_current_basis->row_status.size(); ++row) {
+//            if (mask[row] == 1)
+//                optional_current_basis->row_status[row] = HighsBasisStatus::kNonbasic;
+//        }
+//    }
 
     restricted_model.lp_.col_cost_ = rhs_updated;
     restricted_model.lp_.col_lower_ = vector<double>(_imp->model.lp_.num_col_, 0.0);
diff --git a/minicp_benchmarks/CMakeLists.txt b/minicp_benchmarks/CMakeLists.txt
index 4661eb6..359f3ea 100644
--- a/minicp_benchmarks/CMakeLists.txt
+++ b/minicp_benchmarks/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 include_directories(${CMAKE_SOURCE_DIR}/generator/src/generator/include)
+link_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build/lib)
 add_subdirectory(magic_series)
 add_subdirectory(magic_square)
 add_subdirectory(n_queens)
diff --git a/minizinc/CMakeLists.txt b/minizinc/CMakeLists.txt
index 5f36746..d55c3f6 100644
--- a/minizinc/CMakeLists.txt
+++ b/minizinc/CMakeLists.txt
@@ -1,4 +1,7 @@
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
+
+link_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build/lib)
+
 add_executable(fzn-glasgow fzn_glasgow.cc)
 target_link_libraries(fzn-glasgow PUBLIC glasgow_constraint_solver)
 
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2b8c13d..4a42119 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,5 +1,8 @@
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 include_directories(${CMAKE_SOURCE_DIR}/generator/src/generator/include)
+include_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS/src)
+include_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build)
+link_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build/lib)
 add_subdirectory(break_table)
 add_subdirectory(break_table_with_constants)
 add_subdirectory(break_views)
@@ -7,4 +10,4 @@ add_subdirectory(up_proof)
 add_subdirectory(consolidate_unary)
 add_subdirectory(highs_model_test)
 add_subdirectory(tiny_all_diff)
-add_subdirectory(baby_tu_debug)
\ No newline at end of file
+add_subdirectory(baby_tu_debug)
diff --git a/xcsp/CMakeLists.txt b/xcsp/CMakeLists.txt
index be63645..af73738 100644
--- a/xcsp/CMakeLists.txt
+++ b/xcsp/CMakeLists.txt
@@ -5,6 +5,8 @@ ExternalProject_Add(XCSP3-CPP-Parser
     GIT_TAG 807f475c5b705d085215de61b811c1fbf0152398
     INSTALL_COMMAND "")
 
+link_directories(${CMAKE_SOURCE_DIR}/HiGHS/src/HiGHS-build/lib)
+
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 add_executable(xcsp_glasgow_constraint_solver xcsp_glasgow_constraint_solver.cc)
 

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

Basically, your problem is that std::optional<> doesn't quite behave the way you hope it does. I don't know enough of what a HighsBasis is to tell you what you should use, but I'm guessing a std::shared_ptr<> might be what you want?

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

I've turned Zoom on in case you want to discuss

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

Looks like commenting things out works, but a simple conversion to a shared_ptr isn't enough. I'm a bit confused by the last and current logic, as well: do you actually need two variables here? Anyway, this seems to work, at least until it encounters a failing test:

diff --git a/gcs/innards/proofs/lp_justifier.cc b/gcs/innards/proofs/lp_justifier.cc
index 9f8aa42..2a1ab47 100644
--- a/gcs/innards/proofs/lp_justifier.cc
+++ b/gcs/innards/proofs/lp_justifier.cc
@@ -9,18 +9,21 @@
 #include <sstream>
 
 // TODO: Remove later
+#include <iostream>
 #include <iomanip>
 //
 
 using std::iota;
 using std::make_optional;
 using std::make_pair;
+using std::make_shared;
 using std::map;
 using std::max;
 using std::move;
 using std::nullopt;
 using std::optional;
 using std::pair;
+using std::shared_ptr;
 using std::stringstream;
 using std::to_string;
 using std::tuple;
@@ -32,10 +35,6 @@ using namespace gcs::lp_innards;
 
 namespace
 {
-    // Dirty GPT code
-#include "Highs.h" // Include the HiGHS header
-#include <iostream>
-
     void printHighsLP(const HighsLp & lp)
     {
         // Print start_ initializer
@@ -246,7 +245,7 @@ struct LPJustifier::Imp
 
     auto pass_and_solve_model(const WeightedPseudoBooleanLessEqual & inference,
         HighsModel & restricted_model, vector<double> rhs_updated, vector<long> new_row_num, vector<long> old_row_num,
-        optional<HighsBasis> & optional_last_basis, optional<HighsBasis> & optional_current_basis) -> const HighsSolution
+        shared_ptr<HighsBasis> & optional_basis) -> const HighsSolution
     {
         HighsStatus return_status;
         bool inferring_contradiction = inference.lhs.terms.empty() && inference.rhs <= -1_i;
@@ -297,24 +296,24 @@ struct LPJustifier::Imp
         const HighsLp & lp = this->highs.getLp();
 
         this->highs.setBasis();
-        if (optional_last_basis) {
+        if (optional_basis) {
             // Use the basis
-            // this->highs.setBasis(*optional_current_basis);
+            this->highs.setBasis(*optional_basis);
         }
 
         // Now solve the model
         return_status = this->highs.run();
 
         // Save the basis for next time
-        if (optional_last_basis) {
-            auto new_basis = this->highs.getBasis();
-            for (unsigned int row = 0; row < new_basis.row_status.size(); ++row) {
-                optional_last_basis->row_status[old_row_num[row]] = new_basis.row_status[row];
-            }
-        }
-        else {
-            optional_last_basis = highs.getBasis();
-        }
+//        if (optional_last_basis) {
+//            auto new_basis = this->highs.getBasis();
+//            for (unsigned int row = 0; row < new_basis.row_status.size(); ++row) {
+//                optional_last_basis->row_status[old_row_num[row]] = new_basis.row_status[row];
+//            }
+//        }
+//        else {
+          optional_basis = make_shared<HighsBasis>(highs.getBasis());
+//        }
 
         // Check it worked
         const HighsModelStatus & model_status = this->highs.getModelStatus();
@@ -481,7 +480,7 @@ void LPJustifier::initialise_with_vars(State & state, vector<IntegerVariableID>
     _imp->model.lp_.a_matrix_.value_ = value;
     _imp->model.lp_.setMatrixDimensions();
 
-    optional<HighsBasis> optional_last_basis = nullopt;
+    shared_ptr<HighsBasis> optional_last_basis = nullptr;
     _imp->last_basis_handle = state.add_constraint_state(optional_last_basis);
 }
 
@@ -530,8 +529,7 @@ auto LPJustifier::compute_justification(const State & state, ProofLogger & logge
     auto restricted_model = _imp->model;
     auto rhs_updated = _imp->constraints_rhs;
 
-    auto & optional_last_basis = any_cast<optional<HighsBasis> &>(state.get_constraint_state(_imp->last_basis_handle));
-    optional<HighsBasis> optional_current_basis = optional_last_basis ? make_optional(HighsBasis{}) : nullopt;
+    auto & optional_basis = any_cast<shared_ptr<HighsBasis> &>(state.get_constraint_state(_imp->last_basis_handle));
 
     vector<HighsInt> mask;
     vector<long> new_row_num(restricted_model.lp_.num_row_, 0);
@@ -563,13 +561,11 @@ auto LPJustifier::compute_justification(const State & state, ProofLogger & logge
     create(to_delete, mask.data(), restricted_model.lp_.num_row_);
     restricted_model.lp_.deleteRows(to_delete);
 
-    if (optional_current_basis) {
+    if (optional_basis) {
         // Set the current basis based on the last basis, excluding deleted rows
-        optional_current_basis->col_status = optional_last_basis->col_status;
-        optional_current_basis->row_status = optional_last_basis->row_status;
-        for (unsigned int row = 0; row < optional_current_basis->row_status.size(); ++row) {
+        for (unsigned int row = 0; row < optional_basis->row_status.size(); ++row) {
             if (mask[row] == 1)
-                optional_current_basis->row_status[row] = HighsBasisStatus::kNonbasic;
+                optional_basis->row_status[row] = HighsBasisStatus::kNonbasic;
         }
     }
 
@@ -584,14 +580,14 @@ auto LPJustifier::compute_justification(const State & state, ProofLogger & logge
     HighsSolution solution_already;
     // Compute solution already, even if the justification isn't called
     if (compute_bounds)
-        solution_already = _imp->pass_and_solve_model(inference, restricted_model, rhs_updated, new_row_num, old_row_num, optional_last_basis, optional_current_basis);
+        solution_already = _imp->pass_and_solve_model(inference, restricted_model, rhs_updated, new_row_num, old_row_num, optional_basis);
 
-    return [&state = state, &logger, inference, &imp = _imp, &optional_last_basis, &optional_current_basis,
+    return [&state = state, &logger, inference, &imp = _imp, &optional_basis,
                restricted_model = move(restricted_model), rhs_updated, new_row_num, old_row_num, compute_bounds, solution_already = move(solution_already)](const Reason &) {
         HighsSolution solution;
         HighsModel restr_model = move(restricted_model);
         if (! compute_bounds)
-            solution = imp->pass_and_solve_model(inference, restr_model, rhs_updated, new_row_num, old_row_num, optional_last_basis, optional_current_basis);
+            solution = imp->pass_and_solve_model(inference, restr_model, rhs_updated, new_row_num, old_row_num, optional_basis);
         else
             solution = solution_already;
 

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

Although the "restore on backtrack" logic for that is probably wrong, so actually no not that, and I guess you do need two variables. Still, the shared_ptr<> thing seems to at least fix the memory issues.

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

Hrm this is interesting. If I do the following instead:

diff --git a/gcs/innards/proofs/lp_justifier.cc b/gcs/innards/proofs/lp_justifier.cc
index 9f8aa42..90048a9 100644
--- a/gcs/innards/proofs/lp_justifier.cc
+++ b/gcs/innards/proofs/lp_justifier.cc
@@ -9,18 +9,21 @@
 #include <sstream>
 
 // TODO: Remove later
+#include <iostream>
 #include <iomanip>
 //
 
 using std::iota;
 using std::make_optional;
 using std::make_pair;
+using std::make_shared;
 using std::map;
 using std::max;
 using std::move;
 using std::nullopt;
 using std::optional;
 using std::pair;
+using std::shared_ptr;
 using std::stringstream;
 using std::to_string;
 using std::tuple;
@@ -32,10 +35,6 @@ using namespace gcs::lp_innards;
 
 namespace
 {
-    // Dirty GPT code
-#include "Highs.h" // Include the HiGHS header
-#include <iostream>
-
     void printHighsLP(const HighsLp & lp)
     {
         // Print start_ initializer
@@ -309,7 +308,7 @@ struct LPJustifier::Imp
         if (optional_last_basis) {
             auto new_basis = this->highs.getBasis();
             for (unsigned int row = 0; row < new_basis.row_status.size(); ++row) {
-                optional_last_basis->row_status[old_row_num[row]] = new_basis.row_status[row];
+                optional_last_basis->row_status.at(old_row_num.at(row)) = new_basis.row_status.at(row);
             }
         }
         else {
@@ -481,7 +480,7 @@ void LPJustifier::initialise_with_vars(State & state, vector<IntegerVariableID>
     _imp->model.lp_.a_matrix_.value_ = value;
     _imp->model.lp_.setMatrixDimensions();
 
-    optional<HighsBasis> optional_last_basis = nullopt;
+    ConstraintState optional_last_basis = optional<HighsBasis>{};
     _imp->last_basis_handle = state.add_constraint_state(optional_last_basis);
 }
 
@@ -530,7 +529,7 @@ auto LPJustifier::compute_justification(const State & state, ProofLogger & logge
     auto restricted_model = _imp->model;
     auto rhs_updated = _imp->constraints_rhs;
 
-    auto & optional_last_basis = any_cast<optional<HighsBasis> &>(state.get_constraint_state(_imp->last_basis_handle));
+    optional<HighsBasis> & optional_last_basis = any_cast<optional<HighsBasis> &>(state.get_constraint_state(_imp->last_basis_handle));
     optional<HighsBasis> optional_current_basis = optional_last_basis ? make_optional(HighsBasis{}) : nullopt;
 
     vector<HighsInt> mask;

then I get

$ veripb --help >/dev/null
among (1, 3) [2, 4, 6, 8] [variant((1, 10)), variant((1, 10)), variant((1, 10))]  with proofs: expecting 784 solutions
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 18) >= this->size() (which is 13)

which suggests your basis has changed size maybe?

@mmcilree
Copy link
Collaborator Author

Hmm, right. I think what I'll do is take out the basis stuff for now and make sure everything is working as it should, see if I can do GCC and among with passing proofs.

The paper is probably at least submittable with a slow implementation, but not a broken one. If I have extra time I'll see if I can re-add the hot starts.

@ciaranm
Copy link
Owner

ciaranm commented Mar 11, 2025

Sounds sensible to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants