diff --git a/app/RunHighs.cpp b/app/RunHighs.cpp index 733f2d8c001..2acde399140 100644 --- a/app/RunHighs.cpp +++ b/app/RunHighs.cpp @@ -140,7 +140,7 @@ int main(int argc, char** argv) { // Shut down task executor for explicit release of memory. // Valgrind still reachable otherwise. - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return runHighsReturn(highs, run_status); } diff --git a/check/TestAlienBasis.cpp b/check/TestAlienBasis.cpp index e715e5bdeb7..cfa9b18d998 100644 --- a/check/TestAlienBasis.cpp +++ b/check/TestAlienBasis.cpp @@ -610,7 +610,7 @@ void testAlienBasis(const bool avgas, const HighsInt seed) { highs.run(); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("AlienBasis-reuse-basis", "[highs_test_alien_basis]") { @@ -677,7 +677,7 @@ TEST_CASE("AlienBasis-reuse-basis", "[highs_test_alien_basis]") { highs.run(); if (dev_run) highs.writeSolution("", 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("AlienBasis-singular-basis", "[highs_test_alien_basis]") { @@ -716,5 +716,5 @@ TEST_CASE("AlienBasis-singular-basis", "[highs_test_alien_basis]") { return_status = highs.getBasicVariables(basic_variables.data()); REQUIRE(return_status == HighsStatus::kError); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestBasis.cpp b/check/TestBasis.cpp index 19b9df1f9ac..408f0fcfbf0 100644 --- a/check/TestBasis.cpp +++ b/check/TestBasis.cpp @@ -80,7 +80,7 @@ TEST_CASE("Basis-file", "[highs_basis_file]") { std::remove(basis_file.c_str()); std::remove(invalid_basis_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Basis-data", "[highs_basis_data]") { @@ -110,7 +110,7 @@ TEST_CASE("Basis-data", "[highs_basis_data]") { testBasisRestart(highs, basis_file, false); testBasisReloadModel(highs, basis_file, false); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("set-pathological-basis", "[highs_basis_data]") { @@ -147,7 +147,7 @@ TEST_CASE("set-pathological-basis", "[highs_basis_data]") { highs.run(); REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnbounded); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Basis-no-basic", "[highs_basis_data]") { @@ -173,7 +173,7 @@ TEST_CASE("Basis-no-basic", "[highs_basis_data]") { REQUIRE(highs.getInfo().objective_function_value == -0.5); REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Basis-singular", "[highs_basis_data]") { @@ -352,6 +352,6 @@ TEST_CASE("Basis-read", "[highs_basis_data]") { REQUIRE(basis2.col_status[0] == status_after); std::remove(basis_file.c_str()); - h1.resetGlobalScheduler(true); - h2.resetGlobalScheduler(true); + h1.resetThreadScheduler(true); + h2.resetThreadScheduler(true); } diff --git a/check/TestBasisSolves.cpp b/check/TestBasisSolves.cpp index b74aeacdb47..f7ed8198a8e 100644 --- a/check/TestBasisSolves.cpp +++ b/check/TestBasisSolves.cpp @@ -564,7 +564,7 @@ TEST_CASE("Basis-solves", "[highs_basis_solves]") { highs.run(); REQUIRE(highs.getInfo().simplex_iteration_count == 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Kappa", "[highs_basis_solves]") { @@ -613,7 +613,7 @@ TEST_CASE("Kappa", "[highs_basis_solves]") { REQUIRE(highs.getKappa(kappa) == HighsStatus::kError); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("2913", "[highs_basis_solves]") { @@ -665,5 +665,5 @@ TEST_CASE("2913", "[highs_basis_solves]") { } std::remove(basis_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestCallbacks.cpp b/check/TestCallbacks.cpp index 07d50ef3edb..622d9787215 100644 --- a/check/TestCallbacks.cpp +++ b/check/TestCallbacks.cpp @@ -386,7 +386,7 @@ TEST_CASE("highs-callback-logging", "[highs_callback]") { highs.readModel(filename); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-solution-basis-logging", "[highs_callback]") { @@ -403,7 +403,7 @@ TEST_CASE("highs-callback-solution-basis-logging", "[highs_callback]") { if (dev_run) highs.writeSolution("", kSolutionStylePretty); if (dev_run) highs.writeBasis(""); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-simplex-interrupt", "[highs_callback]") { @@ -420,7 +420,7 @@ TEST_CASE("highs-callback-simplex-interrupt", "[highs_callback]") { REQUIRE(highs.getInfo().simplex_iteration_count > adlittle_simplex_iteration_limit); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-ipm-interrupt", "[highs_callback]") { @@ -447,7 +447,7 @@ TEST_CASE("highs-callback-ipm-interrupt", "[highs_callback]") { REQUIRE(highs.getInfo().ipm_iteration_count == adlittle_ipm_iteration_limit + 1); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-mip-interrupt", "[highs_callback]") { @@ -463,7 +463,7 @@ TEST_CASE("highs-callback-mip-interrupt", "[highs_callback]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kInterrupt); REQUIRE(highs.getInfo().objective_function_value > egout_optimal_objective); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-mip-improving", "[highs_callback]") { @@ -478,7 +478,7 @@ TEST_CASE("highs-callback-mip-improving", "[highs_callback]") { highs.readModel(filename); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-mip-data", "[highs_callback]") { @@ -492,7 +492,7 @@ TEST_CASE("highs-callback-mip-data", "[highs_callback]") { highs.readModel(filename); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-mip-solution", "[highs_callback]") { @@ -514,7 +514,7 @@ TEST_CASE("highs-callback-mip-solution", "[highs_callback]") { highs.startCallback(kCallbackMipSolution); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-mip-cut-pool", "[highs_callback]") { @@ -526,7 +526,7 @@ TEST_CASE("highs-callback-mip-cut-pool", "[highs_callback]") { highs.startCallback(kCallbackMipGetCutPool); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } static void runMipUserSolutionTest( @@ -566,7 +566,7 @@ static void runMipUserSolutionTest( std::max(1.0, std::fabs(objective_function_value0)); REQUIRE(objective_diff < 1e-12); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-mip-user-solution", "[highs-callback]") { @@ -637,7 +637,7 @@ TEST_CASE("highs-callback-mip-user-solution-c", "[highs-callback]") { highs.startCallback(kCallbackMipSolution); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-callback-qpasm", "[highs_callback]") { @@ -653,5 +653,5 @@ TEST_CASE("highs-callback-qpasm", "[highs_callback]") { highs.startCallback(kCallbackQpInterrupt); highs.run(); REQUIRE(highs.getInfo().qp_iteration_count == 6); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestCheckSolution.cpp b/check/TestCheckSolution.cpp index d97d49571e9..372867c028c 100644 --- a/check/TestCheckSolution.cpp +++ b/check/TestCheckSolution.cpp @@ -255,7 +255,7 @@ TEST_CASE("check-set-mip-solution", "[highs_check_solution]") { assert(other_tests); std::remove(solution_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("set-pathological-solution", "[highs_check_solution]") { @@ -283,7 +283,7 @@ TEST_CASE("set-pathological-solution", "[highs_check_solution]") { highs.run(); REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnbounded); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("check-set-lp-solution", "[highs_check_solution]") { @@ -328,7 +328,7 @@ TEST_CASE("check-set-rowwise-lp-solution", "[highs_check_solution]") { double objective2 = highs.getInfo().objective_function_value; REQUIRE(fabs(objective1 - objective2) / max(1.0, objective1) < 1e-5); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("check-set-mip-solution-extra-row", "[highs_check_solution]") { @@ -360,7 +360,7 @@ TEST_CASE("check-set-mip-solution-extra-row", "[highs_check_solution]") { if (dev_run) highs.writeSolution("", 1); std::remove(solution_file_name.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("check-set-illegal-solution", "[highs_check_solution]") { @@ -422,7 +422,7 @@ TEST_CASE("read-miplib-solution", "[highs_check_solution]") { REQUIRE(h.run() == HighsStatus::kOk); std::remove(miplib_sol_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("read-lp-file-solution", "[highs_check_solution]") { @@ -460,7 +460,7 @@ TEST_CASE("read-lp-file-solution", "[highs_check_solution]") { std::remove(model_file_name.c_str()); std::remove(solution_file_name.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("read-lp-file-basis", "[highs_check_solution]") { @@ -504,7 +504,7 @@ TEST_CASE("read-lp-file-basis", "[highs_check_solution]") { std::remove(model_file_name.c_str()); std::remove(basis_file_name.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("read-lp-file-rgn", "[highs_check_solution]") { @@ -534,7 +534,7 @@ TEST_CASE("read-lp-file-rgn", "[highs_check_solution]") { std::remove(model_file_name.c_str()); std::remove(solution_file_name.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } void runWriteReadCheckSolution(Highs& highs, const std::string& test_name, @@ -594,7 +594,7 @@ void runWriteReadCheckSolution(Highs& highs, const std::string& test_name, std::remove(solution_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void runSetLpSolution(const std::string model) { @@ -663,7 +663,7 @@ void runSetLpSolution(const std::string model) { std::remove(solution_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("miplib-sol-file", "[highs_check_solution]") { @@ -702,7 +702,7 @@ TEST_CASE("miplib-sol-file", "[highs_check_solution]") { std::remove(lp_file.c_str()); std::remove(sol_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-3317", "[highs_check_solution]") { @@ -743,5 +743,5 @@ TEST_CASE("issue-3317", "[highs_check_solution]") { std::remove(sol_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestCrossover.cpp b/check/TestCrossover.cpp index 3ba3fff9327..5d8af322018 100644 --- a/check/TestCrossover.cpp +++ b/check/TestCrossover.cpp @@ -92,7 +92,7 @@ void test_crossover(Highs& highs, HighsLp& lp) { report("After crossover", lp, solution, basis); report("From Highs", lp, highs.getSolution(), highs.getBasis()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void report(const std::string message, const HighsLp& lp, diff --git a/check/TestDualize.cpp b/check/TestDualize.cpp index f3b1aa47e15..a77fef4b96e 100644 --- a/check/TestDualize.cpp +++ b/check/TestDualize.cpp @@ -30,7 +30,7 @@ TEST_CASE("Dualize", "[highs_test_dualize]") { // instanceTest(highs, "afiro"); // instanceTest(highs, "adlittle"); instanceTest(highs, "25fv47"); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void dualizeTest(Highs& highs) { diff --git a/check/TestEkk.cpp b/check/TestEkk.cpp index 6b0bad28b9b..c14c154184b 100644 --- a/check/TestEkk.cpp +++ b/check/TestEkk.cpp @@ -32,7 +32,7 @@ void ekk_solve(Highs& highs, std::string presolve, REQUIRE(highs.resetOptions() == HighsStatus::kOk); highs.setOptionValue("output_flag", dev_run); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void ekk_distillation(Highs& highs) { @@ -89,7 +89,7 @@ TEST_CASE("Ekk", "[highs_test_ekk]") { // ekk_scipLpi3(highs); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("EkkPrimal-all", "[highs_test_ekk]") { @@ -129,5 +129,5 @@ TEST_CASE("Ekk-excessive-primal-value", "[highs_test_ekk]") { REQUIRE(highs.run() == HighsStatus::kError); REQUIRE(log.find("Excessive primal value") != std::string::npos); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestFactor.cpp b/check/TestFactor.cpp index c7b9b9af6ce..48db9cb0ce8 100644 --- a/check/TestFactor.cpp +++ b/check/TestFactor.cpp @@ -46,7 +46,7 @@ TEST_CASE("Factor-dense-tran", "[highs_test_factor]") { rhs.setup(num_row); REQUIRE(testSolveDense()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Factor-put-get-iterate", "[highs_test_factor]") { @@ -109,7 +109,7 @@ TEST_CASE("Factor-put-get-iterate", "[highs_test_factor]") { highs.changeColBounds(iCol, save_lower, save_upper); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Factor-get-set-invert", "[highs_test_factor]") { @@ -209,7 +209,7 @@ TEST_CASE("Factor-zero-col-pivot-tolerance", "[highs_test_factor]") { REQUIRE(highs.run() == HighsStatus::kOk); REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } HighsInt rowOut(const HighsInt variable_out) { diff --git a/check/TestFilereader.cpp b/check/TestFilereader.cpp index 9ccd020b7e2..3c72c480181 100644 --- a/check/TestFilereader.cpp +++ b/check/TestFilereader.cpp @@ -139,7 +139,7 @@ TEST_CASE("filereader-edge-cases", "[highs_filereader]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); REQUIRE(highs.getInfo().objective_function_value == 9); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void freeFixedModelTest(const std::string model_name) { @@ -238,7 +238,7 @@ TEST_CASE("filereader-read-mps-lp", "[highs_filereader]") { std::remove(filename_lp.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("filereader-integrality-constraints", "[highs_filereader]") { @@ -308,7 +308,7 @@ TEST_CASE("filereader-fixed-integer", "[highs_filereader]") { objective_value = highs.getInfo().objective_function_value; REQUIRE(objective_value == optimal_objective_value); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("filereader-dD2e", "[highs_filereader]") { @@ -326,7 +326,7 @@ TEST_CASE("filereader-dD2e", "[highs_filereader]") { objective_value = highs.getInfo().objective_function_value; REQUIRE(objective_value == optimal_objective_value); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } // TEST_CASE("filereader-comment", "[highs_filereader]") { @@ -425,7 +425,7 @@ TEST_CASE("write-MI-bound-model", "[highs_filereader]") { REQUIRE(required_objective_value == h.getInfo().objective_function_value); std::remove(write_model_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("mps-warnings", "[highs_filereader]") { @@ -491,7 +491,7 @@ TEST_CASE("handle-blank-space-names", "[highs_filereader]") { REQUIRE(h.writeSolution("", 1) == HighsStatus::kWarning); REQUIRE(h.writeModel("") == HighsStatus::kOk); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("read-highs-lp-file0", "[highs_filereader]") { @@ -526,7 +526,7 @@ TEST_CASE("read-highs-lp-file0", "[highs_filereader]") { std::remove(model_file_name0.c_str()); std::remove(model_file_name1.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("read-highs-lp-file1", "[highs_filereader]") { @@ -542,7 +542,7 @@ TEST_CASE("read-highs-lp-file1", "[highs_filereader]") { std::remove(model_file_name.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("lp-duplicate-variable", "[highs_filereader]") { diff --git a/check/TestHighsModel.cpp b/check/TestHighsModel.cpp index 358dfb917e4..dbdaf26f11d 100644 --- a/check/TestHighsModel.cpp +++ b/check/TestHighsModel.cpp @@ -76,7 +76,7 @@ TEST_CASE("highs-model", "[highs_model]") { status = highs.run(); REQUIRE(status == HighsStatus::kError); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("highs-integrality", "[highs_model]") { @@ -130,5 +130,5 @@ TEST_CASE("highs-integrality", "[highs_model]") { REQUIRE(highs.getColIntegrality(0, integrality) == HighsStatus::kOk); REQUIRE(integrality == HighsVarType::kInteger); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestHighsParallel.cpp b/check/TestHighsParallel.cpp index 606c22c4c99..c87c125f034 100644 --- a/check/TestHighsParallel.cpp +++ b/check/TestHighsParallel.cpp @@ -305,6 +305,19 @@ static void nestedTask() { tg.spawn([]() { loseTime(); }); } +TEST_CASE("CancelNestedTasks", "[parallel]") { + highs::parallel::initialize_scheduler(); + + for (int i = 0; i < 1000; ++i) { + highs::parallel::TaskGroup tg; + tg.spawn([]() { nestedTask(); }); + tg.spawn([]() { nestedTask(); }); + loseTime(); + } + + HighsTaskExecutor::shutdown(); +} + TEST_CASE("AvailableCoreCount", "[parallel]") { unsigned int cores = highs::parallel::available_core_count(); REQUIRE(cores > 0); @@ -354,63 +367,97 @@ TEST_CASE("AffinityReducedCoreCount", "[parallel]") { } #endif -TEST_CASE("CancelNestedTasks", "[parallel]") { - highs::parallel::initialize_scheduler(); - - for (int i = 0; i < 1000; ++i) { - highs::parallel::TaskGroup tg; - tg.spawn([]() { nestedTask(); }); - tg.spawn([]() { nestedTask(); }); - loseTime(); +// Spin until flag >= value. Returns false on timeout so a bug can't hang CI. +bool waitFor(const std::atomic& flag, int value) { + const auto start = std::chrono::steady_clock::now(); + while (flag.load() < value) { + if (std::chrono::steady_clock::now() - start > std::chrono::seconds(1)) + return false; + std::this_thread::yield(); } + return true; +} - HighsTaskExecutor::shutdown(); +long long parallelSum(HighsInt n) { + std::atomic sum{0}; + highs::parallel::for_each( + 0, n, + [&](HighsInt start, HighsInt end) { + long long local = 0; + for (HighsInt i = start; i < end; ++i) local += i; + sum += local; + }, + 16); + return sum.load(); } -TEST_CASE("ParallelCApi", "[parallel]") { - // This test exposes a failure in the C api when creating and destroying Highs - // instances on separate threads. - // One thread solves a mip, while the other loses some time before destroying - // the instance. If this causes the scheduler to be terminated (i.e., if - // Highs_destroy calls resetGlobalScheduler) while the first thread is still - // using it, the first thread may crash, hang, or terminate with wrong - // results. +struct ThreadResult { + int threads_after_init = -1; + long long sum_after_init = -1; + int threads_after_reset = -1; + long long sum_after_reset = -1; + bool synced = true; +}; + +TEST_CASE("ParallelSchedulerIsPerThread", "[parallel]") { + const int num_threads_A_first = 2; + const int num_threads_A_second = 4; + + const int num_threads_B = 3; + const HighsInt n = 1000; + const long long expected_sum = (long long)n * (n - 1) / 2; + + std::atomic init_done{0}; + std::atomic reset_done{0}; + ThreadResult a, b; + + std::thread threadA([&]() { + highs::parallel::initialize_scheduler(num_threads_A_first); + a.threads_after_init = highs::parallel::num_threads(); + a.sum_after_init = parallelSum(n); + init_done++; + a.synced = waitFor(init_done, 2); + + HighsTaskExecutor::shutdown(true); + reset_done++; + + highs::parallel::initialize_scheduler(num_threads_A_second); + a.threads_after_reset = highs::parallel::num_threads(); + a.sum_after_reset = parallelSum(n); + + HighsTaskExecutor::shutdown(true); + }); - highs::parallel::initialize_scheduler(); + std::thread threadB([&]() { + highs::parallel::initialize_scheduler(num_threads_B); + b.threads_after_init = highs::parallel::num_threads(); + b.sum_after_init = parallelSum(n); + init_done++; - std::string model = std::string(HIGHS_DIR) + "/check/instances/egout.mps"; - const double expected_obj = 568.1007; - double actual_obj; - double total = 0; + b.synced = waitFor(reset_done, 1); + b.threads_after_reset = highs::parallel::num_threads(); + b.sum_after_reset = parallelSum(n); - { - highs::parallel::TaskGroup tg; + HighsTaskExecutor::shutdown(true); + }); - tg.spawn([&]() { - void* highs = Highs_create(); - Highs_setBoolOptionValue(highs, "output_flag", dev_run); - Highs_setStringOptionValue(highs, "parallel", "on"); - Highs_readModel(highs, model.c_str()); - Highs_run(highs); - Highs_getDoubleInfoValue(highs, "objective_function_value", &actual_obj); - Highs_destroy(highs); - }); - - tg.spawn([&]() { - void* highs = Highs_create(); - for (int i = 0; i < 1e6; ++i) { - total += (double)i * i * i; - } - Highs_destroy(highs); - }); + threadA.join(); + threadB.join(); - tg.taskWait(); + REQUIRE(a.synced); + REQUIRE(b.synced); - REQUIRE(total > 1e18); - REQUIRE(std::abs(actual_obj - expected_obj) / std::abs(expected_obj) < - 1e-4); - } - // TaskGroup destructor must be called before scheduler is killed + // Both threads got their own scheduler, with the correct sizes + REQUIRE(a.threads_after_init == num_threads_A_first); + REQUIRE(b.threads_after_init == num_threads_B); + REQUIRE(a.sum_after_init == expected_sum); + REQUIRE(b.sum_after_init == expected_sum); + + // A's reset did not affect B + REQUIRE(b.threads_after_reset == num_threads_B); + REQUIRE(b.sum_after_reset == expected_sum); - HighsTaskExecutor::shutdown(true); + // A really was reset + REQUIRE(a.threads_after_reset == num_threads_A_second); + REQUIRE(a.sum_after_reset == expected_sum); } \ No newline at end of file diff --git a/check/TestHipo.cpp b/check/TestHipo.cpp index 438324d388b..e574aa6d05d 100644 --- a/check/TestHipo.cpp +++ b/check/TestHipo.cpp @@ -40,7 +40,7 @@ void runHipoTest( TEST_CASE("test-hipo-afiro", "[highs_hipo]") { Highs highs; runHipoTest(highs, "afiro.mps", -464.753); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-hipo-deterministic", "[highs_hipo]") { @@ -102,7 +102,7 @@ TEST_CASE("test-hipo-options", "[highs_hipo]") { } } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-hipo-qp", "[highs_hipo]") { @@ -110,7 +110,7 @@ TEST_CASE("test-hipo-qp", "[highs_hipo]") { runHipoTest(highs, "qptestnw.lp", -6.4500); runHipoTest(highs, "qjh.lp", -5.2500); runHipoTest(highs, "primal1.mps", -3.501296e-2); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-hipo-infeas", "[highs_hipo]") { @@ -119,7 +119,7 @@ TEST_CASE("test-hipo-infeas", "[highs_hipo]") { runHipoTest(highs, "bgetam.mps", 0, expected_status, "off"); runHipoTest(highs, "forest6.mps", 0, expected_status, "off"); runHipoTest(highs, "klein1.mps", 0, expected_status, "off"); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-hipo-freevar", "[highs_hipo]") { @@ -127,7 +127,7 @@ TEST_CASE("test-hipo-freevar", "[highs_hipo]") { Highs highs; runHipoTest(highs, "perold.mps", -9.381e3, expected_status, "on"); runHipoTest(highs, "perold.mps", -9.381e3, expected_status, "off"); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3369", "[highs_hipo]") { diff --git a/check/TestICrash.cpp b/check/TestICrash.cpp index adad15fa85b..82b28c3b3d2 100644 --- a/check/TestICrash.cpp +++ b/check/TestICrash.cpp @@ -42,5 +42,5 @@ TEST_CASE("icrash-qap04", "[highs_presolve]") { REQUIRE(info.final_residual_norm_2 >= -1e08); REQUIRE(info.final_residual_norm_2 < 1e08); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestIO.cpp b/check/TestIO.cpp index bdd13d678ad..7937ed88db9 100644 --- a/check/TestIO.cpp +++ b/check/TestIO.cpp @@ -39,7 +39,7 @@ TEST_CASE("run-callback", "[highs_io]") { highs.readModel(filename); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("log-callback", "[highs_io]") { @@ -159,5 +159,5 @@ TEST_CASE("console-file-callback-log", "[highs_io]") { std::remove(test_highs_log.c_str()); std::remove(test_user_log.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestIis.cpp b/check/TestIis.cpp index 4f4f794e42b..9ef7219c0f6 100644 --- a/check/TestIis.cpp +++ b/check/TestIis.cpp @@ -161,7 +161,7 @@ TEST_CASE("lp-incompatible-bounds", "[iis]") { for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++) REQUIRE(iis.row_status_[iRow] == kIisStatusNotInConflict); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-empty-infeasible-row", "[iis]") { @@ -233,7 +233,7 @@ TEST_CASE("lp-empty-infeasible-row", "[iis]") { } } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis-light", "[iis]") { @@ -319,7 +319,7 @@ TEST_CASE("lp-get-iis-light", "[iis]") { } highs.passModel(lp); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis", "[iis]") { @@ -398,7 +398,7 @@ TEST_CASE("lp-get-iis", "[iis]") { highs.ensureRowwise(); REQUIRE(highs_lp.a_matrix_.isRowwise()); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis-woodinfe", "[iis]") { @@ -525,7 +525,7 @@ TEST_CASE("lp-get-iis-time-limit-feasible", "[iis]") { // simplex times are all zero reportIisInfo(highs.getOptions().log_options, iis.info_); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis-time-limit-infeasible", "[iis]") { @@ -568,7 +568,7 @@ TEST_CASE("lp-get-iis-time-limit-infeasible", "[iis]") { // simplex iteration counts and min/sum/max simplex times reportIisInfo(highs.getOptions().log_options, iis.info_); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis-time-limit", "[iis]") { @@ -612,7 +612,7 @@ TEST_CASE("lp-get-iis-time-limit", "[iis]") { REQUIRE(iis.col_index_.size() == 0); REQUIRE(iis.row_index_.size() == 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis-partial", "[iis]") { @@ -645,7 +645,7 @@ TEST_CASE("lp-get-iis-partial", "[iis]") { REQUIRE(iis.col_index_.size() > 0); REQUIRE(iis.row_index_.size() > 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-get-iis-time-limit-deletion", "[iis]") { @@ -693,7 +693,7 @@ TEST_CASE("lp-get-iis-time-limit-deletion", "[iis]") { REQUIRE(iis.col_index_.size() > 0); REQUIRE(iis.row_index_.size() > 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lp-feasibility-relaxation", "[iis]") { @@ -845,7 +845,7 @@ TEST_CASE("lp-feasibility-relaxation", "[iis]") { model = "avgas"; testFeasibilityRelaxation(model, 1, 1, 1, 0); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } void testMps(std::string& model, const HighsInt iis_strategy, @@ -962,7 +962,7 @@ TEST_CASE("feasible-lp-iis", "[iis]") { REQUIRE(iis.row_status_[0] == kIisStatusNotInConflict); REQUIRE(iis.row_status_[1] == kIisStatusNotInConflict); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("write-iis_model-file", "[iis]") { @@ -1020,7 +1020,7 @@ TEST_CASE("write-iis_model-file", "[iis]") { } std::remove(test_mps.c_str()); std::remove(test_sol.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } char iis_printed_log[kIoBufferSize]; @@ -1059,7 +1059,7 @@ TEST_CASE("iis-callback", "[highs-callback]") { HighsIis iis; REQUIRE(highs.getIis(iis) == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3368", "[highs-callback]") { @@ -1091,5 +1091,5 @@ TEST_CASE("issue-3368", "[highs-callback]") { REQUIRE(h.getSolution().col_value[x] == 2.0); REQUIRE(h.getSolution().col_value[y] == 2.0); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestInfo.cpp b/check/TestInfo.cpp index b8d3e832728..7f167301b8d 100644 --- a/check/TestInfo.cpp +++ b/check/TestInfo.cpp @@ -112,5 +112,5 @@ TEST_CASE("highs-info", "[highs_info]") { } std::remove(highs_info_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestIpm.cpp b/check/TestIpm.cpp index 4a855e44160..ae530edfe80 100644 --- a/check/TestIpm.cpp +++ b/check/TestIpm.cpp @@ -25,7 +25,7 @@ TEST_CASE("test-analytic-centre", "[highs_ipm]") { HighsStatus run_status = highs.run(); REQUIRE(run_status == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-analytic-centre-infeasible", "[highs_ipm]") { @@ -50,7 +50,7 @@ TEST_CASE("test-analytic-centre-infeasible", "[highs_ipm]") { REQUIRE(run_status == HighsStatus::kOk); REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-analytic-centre-box", "[highs_ipm]") { @@ -86,7 +86,7 @@ TEST_CASE("test-analytic-centre-box", "[highs_ipm]") { REQUIRE(solution_norm < 1e-6); if (dev_run) printf("Analytic centre solution norm is %g\n", solution_norm); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-1966", "[highs_ipm]") { @@ -185,7 +185,7 @@ TEST_CASE("test-1966", "[highs_ipm]") { } highs.clearSolver(); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-2087", "[highs_ipm]") { @@ -206,7 +206,7 @@ TEST_CASE("test-2087", "[highs_ipm]") { h.run(); REQUIRE(first_ipm_iteration_count == h.getInfo().ipm_iteration_count); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-2527", "[highs_ipm]") { @@ -222,7 +222,7 @@ TEST_CASE("test-2527", "[highs_ipm]") { h.setOptionValue("presolve", kHighsOffString); REQUIRE(h.run() == HighsStatus::kOk); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-3397", "[highs_ipm]") { @@ -248,7 +248,7 @@ TEST_CASE("test-3397", "[highs_ipm]") { h.run(); REQUIRE(h.getModelStatus() == HighsModelStatus::kUnbounded); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } // LP with optimal objective -185.875 @@ -275,6 +275,6 @@ TEST_CASE("test-3397", "[highs_ipm]") { REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); REQUIRE(std::fabs(h.getInfo().objective_function_value + 185.875) < 1e-6); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } } diff --git a/check/TestIpx.cpp b/check/TestIpx.cpp index 8e704f413fa..c16e2909962 100644 --- a/check/TestIpx.cpp +++ b/check/TestIpx.cpp @@ -91,7 +91,7 @@ TEST_CASE("test-ipx", "[highs_ipx]") { (void)(info); // surpress unused variable. - // hack to reset global scheduler + // hack to reset scheduler Highs h; - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestLogging.cpp b/check/TestLogging.cpp index 7b93f76e81c..4a3e411f788 100644 --- a/check/TestLogging.cpp +++ b/check/TestLogging.cpp @@ -45,7 +45,7 @@ TEST_CASE("logging", "[highs_logging]") { if (!dev_run) std::remove(log_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("no-logging", "[highs_logging]") { @@ -65,5 +65,5 @@ TEST_CASE("no-logging", "[highs_logging]") { return_status = highs.run(); REQUIRE(return_status == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestLpModification.cpp b/check/TestLpModification.cpp index 233d50eae08..526e355e19b 100644 --- a/check/TestLpModification.cpp +++ b/check/TestLpModification.cpp @@ -67,7 +67,7 @@ TEST_CASE("LP-717-od", "[highs_data]") { HighsStatus::kOk); REQUIRE(highs.run() == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-717-full0", "[highs_data]") { @@ -178,7 +178,7 @@ TEST_CASE("LP-717-full0", "[highs_data]") { printf("After run() LP matrix has format %d\n", (int)highs_lp.a_matrix_.format_); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-717-full1", "[highs_data]") { @@ -291,7 +291,7 @@ TEST_CASE("LP-717-full1", "[highs_data]") { printf("After run() LP matrix has format %d\n", (int)highs_lp.a_matrix_.format_); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-717-full2", "[highs_data]") { @@ -419,7 +419,7 @@ TEST_CASE("LP-717-full2", "[highs_data]") { printf("After run() LP matrix has format %d\n", (int)highs_lp.a_matrix_.format_); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-modification", "[highs_data]") { @@ -1076,7 +1076,7 @@ TEST_CASE("LP-modification", "[highs_data]") { free(col0123_index); free(col0123_value); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-getcols", "[highs_data]") { @@ -1105,7 +1105,7 @@ TEST_CASE("LP-getcols", "[highs_data]") { REQUIRE(matrix_values[0] == 1.0); REQUIRE(matrix_values[1] == -1.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-getrows", "[highs_data]") { @@ -1136,7 +1136,7 @@ TEST_CASE("LP-getrows", "[highs_data]") { REQUIRE(matrix_values[0] == 1.0); REQUIRE(matrix_values[1] == -2.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-interval-changes", "[highs_data]") { @@ -1278,7 +1278,7 @@ TEST_CASE("LP-interval-changes", "[highs_data]") { REQUIRE(optimal_objective_function_value == avgas_optimal_objective_function_value); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-delete", "[highs_data]") { @@ -1437,7 +1437,7 @@ TEST_CASE("LP-delete", "[highs_data]") { std::fabs(objective_function_value - adlittle_objective_function_value) < double_equal_tolerance); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-free-row", "[highs_data]") { @@ -1470,7 +1470,7 @@ TEST_CASE("LP-free-row", "[highs_data]") { highs.run(); REQUIRE(highs.getInfo().objective_function_value == -3); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-delete-ip-var", "[highs_data]") { @@ -1508,7 +1508,7 @@ TEST_CASE("LP-delete-ip-var", "[highs_data]") { } } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void HighsStatusReport(const HighsLogOptions& log_options, std::string message, @@ -1896,7 +1896,7 @@ TEST_CASE("mod-duplicate-indices", "[highs_data]") { REQUIRE(objective0 < objective1); REQUIRE(objective0 == -7.75); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } bool equalSparseVectors(const HighsInt dim, const HighsInt num_nz0, @@ -2494,7 +2494,7 @@ TEST_CASE("hot-start-after-delete", "[highs_data]") { info.objective_function_value); // REQUIRE(info.simplex_iteration_count < ieration_count0); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("no-modification-on-error", "[highs_data]") { diff --git a/check/TestLpOrientation.cpp b/check/TestLpOrientation.cpp index fb044149d1b..6395394b8e9 100644 --- a/check/TestLpOrientation.cpp +++ b/check/TestLpOrientation.cpp @@ -117,5 +117,5 @@ TEST_CASE("LP-orientation", "[lp_orientation]") { // return_status = highs.writeModel(""); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestLpSolvers.cpp b/check/TestLpSolvers.cpp index fedf80de8cf..65f1f58bf30 100644 --- a/check/TestLpSolvers.cpp +++ b/check/TestLpSolvers.cpp @@ -25,7 +25,7 @@ void testDualObjective(const std::string model) { std::max(1.0, std::fabs(primal_objective)); REQUIRE(relative_primal_dual_gap < 1e-12); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("mip-with-lp-solver", "[highs_lp_solver]") { @@ -41,7 +41,7 @@ TEST_CASE("mip-with-lp-solver", "[highs_lp_solver]") { status = highs.run(); REQUIRE(status == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-objective-upper-bound", "[highs_lp_solver]") { @@ -169,7 +169,7 @@ TEST_CASE("dual-objective-upper-bound", "[highs_lp_solver]") { if (dev_run) printf("\nOptimal objective value error = %g\n", error); REQUIRE(error < 1e-10); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("blending-lp-ipm", "[highs_lp_solver]") { @@ -204,7 +204,7 @@ TEST_CASE("blending-lp-ipm", "[highs_lp_solver]") { } REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-objective-max", "[highs_lp_solver]") { @@ -234,7 +234,7 @@ TEST_CASE("dual-objective-max", "[highs_lp_solver]") { std::max(1.0, std::fabs(primal_objective)); REQUIRE(relative_primal_dual_gap < 1e-12); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-objective", "[highs_lp_solver]") { @@ -305,7 +305,7 @@ void testStandardForm(const HighsLp& lp) { highs.writeModel(""); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void testStandardFormModel(const std::string model) { @@ -318,7 +318,7 @@ void testStandardFormModel(const std::string model) { HighsLp lp = highs.getLp(); testStandardForm(lp); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("standard-form-mps", "[highs_lp_solver]") { @@ -363,7 +363,7 @@ TEST_CASE("standard-form-lp", "[highs_lp_solver]") { "maximizing\n"); testStandardForm(highs.getLp()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("simplex-stats", "[highs_lp_solver]") { @@ -400,7 +400,7 @@ TEST_CASE("simplex-stats", "[highs_lp_solver]") { REQUIRE(simplex_stats.row_DSE_density > 0); if (dev_run) h.reportSimplexStats(stdout); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("use_warm_start", "[highs_lp_solver]") { @@ -417,7 +417,7 @@ TEST_CASE("use_warm_start", "[highs_lp_solver]") { HighsInt iteration_count = h.getInfo().simplex_iteration_count; REQUIRE(iteration_count == required_iteration_count); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } bool fileExists(const std::string& file_name) { @@ -462,7 +462,7 @@ TEST_CASE("highs-files-lp", "[highs_lp_solver]") { std::remove(write_solution_file.c_str()); std::remove(write_basis_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("highs-files-mip", "[highs_lp_solver]") { @@ -496,7 +496,7 @@ TEST_CASE("highs-files-mip", "[highs_lp_solver]") { std::remove(write_model_file.c_str()); std::remove(write_solution_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } /* @@ -514,7 +514,7 @@ TEST_CASE("highs-debug-level", "[highs_lp_solver]") { h.run(); h.setOptionValue("highs_debug_level", 3); h.run(); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } */ TEST_CASE("solver-string", "[highs_lp_solver]") { @@ -551,7 +551,7 @@ TEST_CASE("choose-lp-solver", "[highs_lp_solver]") { h.run(); REQUIRE(h.getInfo().ipm_iteration_count > 0); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("releaseMemory", "[highs_lp_solver]") { @@ -580,7 +580,7 @@ TEST_CASE("releaseMemory", "[highs_lp_solver]") { // Verify objectives are different (different problems) REQUIRE(first_objective != second_objective); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-2300", "[highs_lp_solver]") { @@ -600,5 +600,5 @@ TEST_CASE("issue-2300", "[highs_lp_solver]") { REQUIRE(std::fabs(h.getInfo().objective_function_value - optimal_objective) < 1e-12); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestLpValidation.cpp b/check/TestLpValidation.cpp index f6ec4de3dd5..4e943d63953 100644 --- a/check/TestLpValidation.cpp +++ b/check/TestLpValidation.cpp @@ -392,7 +392,7 @@ TEST_CASE("LP-validation", "[highs_data]") { HighsStatus::kOk); REQUIRE(check_value == to_value); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-row-index-duplication", "[highs_data]") { @@ -449,7 +449,7 @@ TEST_CASE("LP-extreme-coefficient", "[highs_data]") { REQUIRE(return_status == HighsStatus::kOk); REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-duplicate-small-value", "[highs_data]") { @@ -481,7 +481,7 @@ TEST_CASE("LP-duplicate-small-value", "[highs_data]") { REQUIRE(highs.getLp().a_matrix_.numNz() == 0); lp.a_matrix_.value_ = {3, 3}; REQUIRE(highs.passModel(lp) == HighsStatus::kError); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-inf-cost", "[highs_data]") { @@ -607,7 +607,7 @@ TEST_CASE("LP-inf-cost", "[highs_data]") { // Check that x was fixed at 1, not 0.5 REQUIRE(highs.getSolution().col_value[0] == 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-change-coefficient", "[highs_data]") { @@ -654,7 +654,7 @@ TEST_CASE("LP-change-coefficient", "[highs_data]") { highs.getInfo().objective_function_value); REQUIRE(delta_objective_value < 1e-8); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-illegal-empty-start-ok", "[highs_data]") { @@ -689,7 +689,7 @@ TEST_CASE("LP-row-wise", "[highs_data]") { highs.passModel(lp); highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-infeasible-bounds", "[highs_data]") { @@ -720,5 +720,5 @@ TEST_CASE("LP-infeasible-bounds", "[highs_data]") { highs.run(); REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestMipSolver.cpp b/check/TestMipSolver.cpp index 86db2001b72..4cecaafb2f5 100644 --- a/check/TestMipSolver.cpp +++ b/check/TestMipSolver.cpp @@ -29,7 +29,7 @@ TEST_CASE("MIP-distillation", "[highs_test_mip_solver]") { if (!dev_run) highs.setOptionValue("output_flag", false); distillationMIP(highs); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } // Fails but the cases work separately in @@ -46,7 +46,7 @@ TEST_CASE("MIP-rowless-1", "[highs_test_mip_solver]") { if (!dev_run) highs.setOptionValue("output_flag", false); rowlessMIP1(highs); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-rowless-2", "[highs_test_mip_solver]") { @@ -54,7 +54,7 @@ TEST_CASE("MIP-rowless-2", "[highs_test_mip_solver]") { if (!dev_run) highs.setOptionValue("output_flag", false); rowlessMIP2(highs); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-solution-limit", "[highs_test_mip_solver]") { @@ -89,7 +89,7 @@ TEST_CASE("MIP-solution-limit", "[highs_test_mip_solver]") { highs.setOptionValue("mip_max_improving_sols", kHighsIInf); highs.clearSolver(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-integrality", "[highs_test_mip_solver]") { @@ -185,7 +185,7 @@ TEST_CASE("MIP-integrality", "[highs_test_mip_solver]") { REQUIRE(fabs(info.mip_dual_bound + 6) < double_equal_tolerance); REQUIRE(std::fabs(info.mip_gap) < 1e-12); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-clear-integrality", "[highs_test_mip_solver]") { @@ -229,7 +229,7 @@ TEST_CASE("MIP-nmck", "[highs_test_mip_solver]") { REQUIRE(info.max_primal_infeasibility == 0); REQUIRE(info.sum_primal_infeasibilities == 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-maximize", "[highs_test_mip_solver]") { @@ -311,7 +311,7 @@ TEST_CASE("MIP-maximize", "[highs_test_mip_solver]") { options.mip_abs_gap); REQUIRE(std::abs(info.mip_gap) <= options.mip_rel_gap); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-unbounded", "[highs_test_mip_solver]") { @@ -421,7 +421,7 @@ TEST_CASE("MIP-unbounded", "[highs_test_mip_solver]") { model_status = highs.getModelStatus(); REQUIRE(model_status == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-od", "[highs_test_mip_solver]") { @@ -490,7 +490,7 @@ TEST_CASE("MIP-od", "[highs_test_mip_solver]") { REQUIRE(fabs(solution.col_value[0] - required_x0_value) < double_equal_tolerance); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-infeasible-start", "[highs_test_mip_solver]") { @@ -535,7 +535,7 @@ TEST_CASE("MIP-infeasible-start", "[highs_test_mip_solver]") { highs.run(); REQUIRE(model_status == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("get-integrality", "[highs_test_mip_solver]") {} @@ -580,7 +580,7 @@ TEST_CASE("MIP-bounds", "[highs_test_mip_solver]") { REQUIRE(obj0 == obj1); std::remove(test_mps.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-get-saved-solutions", "[highs_test_mip_solver]") { @@ -609,7 +609,7 @@ TEST_CASE("MIP-get-saved-solutions", "[highs_test_mip_solver]") { highs.getSolution().col_value[iCol]); std::remove(solution_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-objective-target", "[highs_test_mip_solver]") { @@ -625,7 +625,7 @@ TEST_CASE("MIP-objective-target", "[highs_test_mip_solver]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kObjectiveTarget); REQUIRE(highs.getInfo().objective_function_value > egout_optimal_objective); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-max-offset-test", "[highs_test_mip_solver]") { @@ -655,7 +655,7 @@ TEST_CASE("MIP-max-offset-test", "[highs_test_mip_solver]") { REQUIRE(objectiveOk(max_offset_optimal_objective, -offset_optimal_objective, dev_run)); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-get-saved-solutions-presolve", "[highs_test_mip_solver]") { @@ -695,7 +695,7 @@ TEST_CASE("MIP-get-saved-solutions-presolve", "[highs_test_mip_solver]") { highs.getSolution().col_value[iCol]); std::remove(solution_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") { @@ -754,7 +754,7 @@ TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") { highs.setOptionValue("presolve", kHighsOnString); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("IP-with-fract-bounds-no-presolve", "[highs_test_mip_solver]") { @@ -791,7 +791,7 @@ TEST_CASE("IP-with-fract-bounds-no-presolve", "[highs_test_mip_solver]") { // Infeasible REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } /* @@ -878,7 +878,7 @@ void solve(Highs& highs, std::string presolve, } REQUIRE(highs.resetOptions() == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void distillationMIP(Highs& highs) { @@ -1291,7 +1291,7 @@ TEST_CASE("get-fixed-lp", "[highs_test_mip_solver]") { REQUIRE(h.getModelStatus() == HighsModelStatus::kInfeasible); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("get-presolved-mip", "[highs_test_mip_solver]") { @@ -1327,7 +1327,7 @@ TEST_CASE("get-presolved-mip", "[highs_test_mip_solver]") { h.run(); const double presolved_lp_objective_value = h.getObjectiveValue(); REQUIRE(presolved_lp_objective_value == lp_objective_value); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("get-fixed-lp-semi", "[highs_test_mip_solver]") { @@ -1363,7 +1363,7 @@ TEST_CASE("get-fixed-lp-semi", "[highs_test_mip_solver]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("row-fixed-lp", "[highs_test_mip_solver]") { @@ -1386,7 +1386,7 @@ TEST_CASE("row-fixed-lp", "[highs_test_mip_solver]") { REQUIRE(h.getInfo().objective_function_value <= mip_optimal_objective + double_equal_tolerance); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-2585", "[highs_test_mip_solver]") { @@ -1489,7 +1489,7 @@ TEST_CASE("issue-2975", "[highs_test_mip_solver]") { REQUIRE(highs.getInfo().objective_function_value == 20); REQUIRE(highs.getSolution().col_value[y] == 0.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3118", "[highs_test_mip_solver]") { @@ -1531,7 +1531,7 @@ TEST_CASE("issue-3118", "[highs_test_mip_solver]") { REQUIRE(std::abs(2 / M - highs.getInfo().objective_function_value) < 1e-9); if (dev_run) highs.writeSolution("", 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3118a", "[highs_test_mip_solver]") { @@ -1611,7 +1611,7 @@ TEST_CASE("issue-3118a", "[highs_test_mip_solver]") { HighsStatus::kOk); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-equality-clique-fixing", "[highs_test_mip_solver]") { @@ -1658,7 +1658,7 @@ TEST_CASE("MIP-equality-clique-fixing", "[highs_test_mip_solver]") { REQUIRE(domain.isFixed(4)); REQUIRE(domain.col_lower_[4] == 1.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("MIP-equality-clique-fixing-to-zero", "[highs_test_mip_solver]") { @@ -1705,7 +1705,7 @@ TEST_CASE("MIP-equality-clique-fixing-to-zero", "[highs_test_mip_solver]") { REQUIRE(domain.isFixed(4)); REQUIRE(domain.col_upper_[4] == 0.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3170", "[highs_test_mip_solver]") { @@ -1758,7 +1758,7 @@ TEST_CASE("issue-3262", "[highs_test_mip_solver]") { REQUIRE_NOTHROW(highs.run()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-2900", "[highs_test_mip_solver]") { @@ -1919,7 +1919,7 @@ TEST_CASE("dual-fix-probing-nonzero-cost", "[highs_test_mip_solver]") { REQUIRE(implications.substitutions[0].offset == 2.0); REQUIRE(implications.substitutions[0].scale == -2.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-fix-probing-tentative-global-fix", "[highs_test_mip_solver]") { @@ -1980,7 +1980,7 @@ TEST_CASE("dual-fix-probing-tentative-global-fix", "[highs_test_mip_solver]") { REQUIRE(domain.col_lower_[1] == 0.0); REQUIRE(domain.col_upper_[1] == 0.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-fix-probing-tentative-clique-complement", @@ -2052,7 +2052,7 @@ TEST_CASE("dual-fix-probing-tentative-clique-complement", REQUIRE(subst->replace.col == 0); REQUIRE(subst->replace.val == 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-fix-probing-tentative-clique-equivalence", @@ -2124,7 +2124,7 @@ TEST_CASE("dual-fix-probing-tentative-clique-equivalence", REQUIRE(subst->replace.col == 0); REQUIRE(subst->replace.val == 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-free-integer-column", "[highs_test_mip_solver]") { @@ -2393,7 +2393,7 @@ TEST_CASE("implied-equation-unbounded", "[highs_test_mip_solver]") { REQUIRE((model_status == HighsModelStatus::kUnbounded || model_status == HighsModelStatus::kUnboundedOrInfeasible)); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3389", "[highs_test_mip_solver]") { @@ -2431,7 +2431,7 @@ TEST_CASE("issue-3389", "[highs_test_mip_solver]") { REQUIRE((std::fabs(objective - 1) < 1e-8 || std::fabs(objective - (2 - 0.5 * tol)) < 1e-8)); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3389-binding-row", "[highs_test_mip_solver]") { @@ -2471,7 +2471,7 @@ TEST_CASE("issue-3389-binding-row", "[highs_test_mip_solver]") { REQUIRE((std::fabs(objective - 1) < 1e-8 || std::fabs(objective - (2 - 0.5 * tol)) < 1e-8)); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } // unstable: with 64-bit HighsInt, the efficacy check in @@ -2489,7 +2489,7 @@ TEST_CASE("issue-3389-binding-row", "[highs_test_mip_solver]") { // const HighsModelStatus require_model_status = HighsModelStatus::kOptimal; // const double optimal_objective = 9; // solve(highs, kHighsOnString, require_model_status, optimal_objective); -// highs.resetGlobalScheduler(true); +// highs.resetThreadScheduler(true); // } TEST_CASE("conflict-pool-sync-empty-conflict", "[highs_test_mip_solver]") { diff --git a/check/TestModelProperties.cpp b/check/TestModelProperties.cpp index 842df30d4a9..e7e6c40e839 100644 --- a/check/TestModelProperties.cpp +++ b/check/TestModelProperties.cpp @@ -44,7 +44,7 @@ TEST_CASE("simplest-ill-conditioning", "[highs_model_properties]") { ill_conditioning_bound) == HighsStatus::kOk); REQUIRE(highs.getIllConditioning(ill_conditioning, constraint, 1, 10) == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("simple-ill-conditioning", "[highs_model_properties]") { diff --git a/check/TestMultiObjective.cpp b/check/TestMultiObjective.cpp index 313628bfc14..3f7000e1524 100644 --- a/check/TestMultiObjective.cpp +++ b/check/TestMultiObjective.cpp @@ -194,5 +194,5 @@ TEST_CASE("multi-objective", "[util]") { } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestPdlp.cpp b/check/TestPdlp.cpp index e8b8cca726d..a0a151cbb1e 100644 --- a/check/TestPdlp.cpp +++ b/check/TestPdlp.cpp @@ -60,7 +60,7 @@ TEST_CASE("pdlp-distillation-lp", "[pdlp]") { REQUIRE(pdlp_iteration_count > 0); REQUIRE(pdlp_iteration_count == (pdlp_iteration_count_optimal / 2) - 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } #else // CUPDLP_GPU @@ -112,7 +112,7 @@ TEST_CASE("pdlp-distillation-lp", "[pdlp]") { // REQUIRE(pdlp_iteration_count > 0); // REQUIRE(pdlp_iteration_count == 79); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } #endif @@ -144,7 +144,7 @@ TEST_CASE("pdlp-3d-lp", "[pdlp]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnknown); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("pdlp-boxed-row-lp", "[pdlp]") { @@ -180,7 +180,7 @@ TEST_CASE("pdlp-boxed-row-lp", "[pdlp]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnknown); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("pdlp-infeasible-lp", "[pdlp]") { @@ -205,7 +205,7 @@ TEST_CASE("pdlp-infeasible-lp", "[pdlp]") { if (dev_run) highs.writeSolution("", 1); REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnboundedOrInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("pdlp-unbounded-lp", "[pdlp]") { @@ -235,7 +235,7 @@ TEST_CASE("pdlp-unbounded-lp", "[pdlp]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kUnbounded); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void pdlpRestart(const std::string& model) { @@ -323,5 +323,5 @@ TEST_CASE("pdlp-restart-add-row", "[pdlp]") { h.setSolution(solution); run_status = h.run(); if (dev_run) h.writeSolution("", 1); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestPdlpHi.cpp b/check/TestPdlpHi.cpp index 27d415d3de1..d38dde9248b 100644 --- a/check/TestPdlpHi.cpp +++ b/check/TestPdlpHi.cpp @@ -36,5 +36,5 @@ TEST_CASE("hi-pdlp", "[pdlp]") { REQUIRE(run_status == HighsStatus::kOk); REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestPresolve.cpp b/check/TestPresolve.cpp index eb66341a42d..ab327007fa8 100644 --- a/check/TestPresolve.cpp +++ b/check/TestPresolve.cpp @@ -88,7 +88,7 @@ TEST_CASE("postsolve-no-basis", "[highs_test_presolve]") { solution.row_dual.clear(); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-solve-postsolve-mip", "[highs_test_presolve]") { @@ -182,7 +182,7 @@ TEST_CASE("empty-row", "[highs_test_presolve]") { REQUIRE(HighsInt(solution.row_value.size()) == lp.num_row_); REQUIRE(HighsInt(basis.row_status.size()) == lp.num_row_); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void presolveSolvePostsolve(const std::string& model_file, @@ -234,7 +234,7 @@ void presolveSolvePostsolve(const std::string& model_file, REQUIRE(highs0.getInfo().simplex_iteration_count <= 0); } - highs1.resetGlobalScheduler(true); + highs1.resetThreadScheduler(true); } HighsStatus zeroCostColSing() { @@ -271,7 +271,7 @@ HighsStatus zeroCostColSing() { status = highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return status; } @@ -331,7 +331,7 @@ HighsStatus colSingDoubletonEquality() { status = highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return status; } @@ -390,7 +390,7 @@ HighsStatus colSingDoubletonInequality() { status = highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return status; } @@ -430,7 +430,7 @@ HighsStatus twoColSingDoubletonEquality() { status = highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return status; } @@ -471,7 +471,7 @@ HighsStatus twoColSingDoubletonInequality() { highs.run(); status = highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return status; } @@ -561,7 +561,7 @@ HighsStatus issue425() { status = highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return status; } @@ -638,7 +638,7 @@ TEST_CASE("write-presolved-model", "[highs_test_presolve]") { REQUIRE(highs.getInfo().simplex_iteration_count == -1); std::remove(presolved_model_file.c_str()); - highs1.resetGlobalScheduler(true); + highs1.resetThreadScheduler(true); } TEST_CASE("presolve-issue-2095", "[highs_test_presolve]") { @@ -650,7 +650,7 @@ TEST_CASE("presolve-issue-2095", "[highs_test_presolve]") { REQUIRE(highs.presolve() == HighsStatus::kOk); REQUIRE(highs.getModelPresolveStatus() == HighsPresolveStatus::kReduced); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-only-at-root", "[highs_test_presolve]") { @@ -663,7 +663,7 @@ TEST_CASE("presolve-only-at-root", "[highs_test_presolve]") { highs.readModel(model_file); REQUIRE(highs.run() == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("lifting-for-probing", "[highs_test_presolve]") { @@ -732,7 +732,7 @@ TEST_CASE("presolve-solve-postsolve-no-col-dual", "[highs_test_presolve]") { solution.col_dual.clear(); REQUIRE(highs.postsolve(solution) == HighsStatus::kError); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") { @@ -792,7 +792,7 @@ TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(h.getPresolveLog().rule[kPresolveRuleSparsify].call > 0); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("dual-bound-tightening", "[highs_test_presolve]") { @@ -853,7 +853,7 @@ TEST_CASE("presolve-rule-off", "[highs_test_presolve]") { int(presolve_num_col), int(presolve_num_row)); } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("presolve-issue-2874", "[highs_test_presolve]") { @@ -865,7 +865,7 @@ TEST_CASE("presolve-issue-2874", "[highs_test_presolve]") { REQUIRE(highs.presolve() == HighsStatus::kOk); REQUIRE(highs.getModelPresolveStatus() == HighsPresolveStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-light", "[highs_test_presolve]") { @@ -897,7 +897,7 @@ TEST_CASE("presolve-light", "[highs_test_presolve]") { highs.setOptionValue("presolve_light", kHighsOnString); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-initial-sweep-postsolve", "[highs_test_presolve]") { @@ -924,7 +924,7 @@ TEST_CASE("presolve-initial-sweep-postsolve", "[highs_test_presolve]") { REQUIRE(highs.getInfo().simplex_iteration_count == 0); if (dev_run) highs.writeSolution("", 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-initial-sweep", "[highs_test_presolve]") { @@ -974,7 +974,7 @@ TEST_CASE("presolve-initial-sweep", "[highs_test_presolve]") { } if (dev_run) highs.writeSolution("", 1); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("presolve-initial-sweep-all", "[highs_test_presolve]") { @@ -1015,7 +1015,7 @@ TEST_CASE("presolve-initial-sweep-all", "[highs_test_presolve]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); if (dev_run) highs.writeSolution("", 1); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("bound_implied", "[highs_test_presolve]") { @@ -1042,7 +1042,7 @@ TEST_CASE("issue-2962", "[highs_test_presolve]") { h.readModel(model_file); REQUIRE(h.run() == HighsStatus::kOk); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("add-to-matrix", "[highs_test_presolve]") { @@ -1103,7 +1103,7 @@ TEST_CASE("add-to-matrix", "[highs_test_presolve]") { REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); REQUIRE(h.getInfo().objective_function_value == 2.0); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("max-lp-dual-postsolve", "[highs_test_presolve]") { @@ -1162,7 +1162,7 @@ TEST_CASE("max-lp-dual-postsolve", "[highs_test_presolve]") { REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-3140", "[highs_test_presolve]") { @@ -1188,7 +1188,7 @@ TEST_CASE("issue-3140", "[highs_test_presolve]") { // // HighsPresolveStatus::kReducedToEmpty); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("dual-bound-relaxation-unbounded", "[highs_test_presolve]") { @@ -1235,7 +1235,7 @@ TEST_CASE("presolve-light-no-crossover", "[highs_test_presolve]") { REQUIRE(h.setOptionValue("solve_relaxation", true) == HighsStatus::kOk); h.run(); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-non-stop-initial-sweep", "[highs_test_presolve]") { @@ -1264,7 +1264,7 @@ TEST_CASE("test-non-stop-initial-sweep", "[highs_test_presolve]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-sparsify-primal-dual-postsolve", "[highs_test_presolve]") { @@ -1300,7 +1300,7 @@ TEST_CASE("test-sparsify-primal-dual-postsolve", "[highs_test_presolve]") { REQUIRE(status == HighsStatus::kOk); REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-duplicate-row-postsolve", "[highs_test_presolve]") { @@ -1385,7 +1385,7 @@ TEST_CASE("test-duplicate-row-postsolve", "[highs_test_presolve]") { lp.row_upper_ = {kHighsInf, 4}; } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-weakly-dominated-column-primal-dual-postsolve", @@ -1474,7 +1474,7 @@ TEST_CASE("test-weakly-dominated-column-primal-dual-postsolve", REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); if (dev_run) h.writeSolution("", 1); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-dual-fixing-primal-dual-postsolve", "[highs_test_presolve]") { @@ -1565,7 +1565,7 @@ TEST_CASE("test-dual-fixing-primal-dual-postsolve", "[highs_test_presolve]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } /* TEST_CASE("test-fuzzing", "[highs_test_presolve]") { @@ -1643,7 +1643,7 @@ HighsStatus::kOk); options.run_crossover = kHighsOnString; h.run(); h.writeSolution("", 1); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } */ @@ -1717,7 +1717,7 @@ TEST_CASE("presolve-rules-off", "[highs_test_presolve]") { } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-3342", "[highs_test_presolve]") { @@ -1824,7 +1824,7 @@ TEST_CASE("issue-3359", "[highs_test_presolve]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-3391", "[highs_test_presolve]") { @@ -1859,7 +1859,7 @@ TEST_CASE("issue-3391", "[highs_test_presolve]") { REQUIRE(h.run() == HighsStatus::kOk); REQUIRE(h.getModelStatus() == HighsModelStatus::kUnbounded); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } void debugPrimalDualPresolve(const HighsLp& lp, const bool reduces_to_empty, @@ -1950,5 +1950,5 @@ void debugPrimalDualPresolve(const HighsLp& lp, const bool reduces_to_empty, REQUIRE(h.getRunData().num_simplex_iterations_after_postsolve == 0); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestPresolveRules.cpp b/check/TestPresolveRules.cpp index 64c156cb26f..3e34d22daa1 100644 --- a/check/TestPresolveRules.cpp +++ b/check/TestPresolveRules.cpp @@ -95,7 +95,7 @@ TEST_CASE("test-col-stuffing", "[highs_test_presolve_rules]") { } lp.clear(); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-implied-bound-aggregation", "[highs_test_presolve_rules]") { @@ -349,7 +349,7 @@ kPresolveRuleInitialSweep) == HighsStatus::kOk); maximize_first = !maximize_first; } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } */ @@ -515,7 +515,7 @@ TEST_CASE("test-fourier-motzkin", "[highs_test_presolve_rules]") { solvers, 1, 6, 6); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-parallel-cols-merge-lp", "[highs_test_presolve_rules]") { @@ -554,7 +554,7 @@ TEST_CASE("test-parallel-cols-merge-lp", "[highs_test_presolve_rules]") { REQUIRE(h.getInfo().num_primal_infeasibilities == 0); REQUIRE(std::abs(h.getObjectiveValue() - 15) < 1e-8); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-parallel-cols-merge-ip", "[highs_test_presolve_rules]") { @@ -596,7 +596,7 @@ TEST_CASE("test-parallel-cols-merge-ip", "[highs_test_presolve_rules]") { REQUIRE(h.getInfo().num_primal_infeasibilities == 0); REQUIRE(std::abs(h.getObjectiveValue() - 15) < 1e-8); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-parallel-cols-merge-floor-rounding", @@ -646,7 +646,7 @@ TEST_CASE("test-parallel-cols-merge-floor-rounding", REQUIRE(h.getInfo().num_primal_infeasibilities == 0); REQUIRE(std::abs(h.getObjectiveValue()) < 1e-8); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-parallel-cols-merge-ceil-rounding", @@ -697,7 +697,7 @@ TEST_CASE("test-parallel-cols-merge-ceil-rounding", REQUIRE(h.getInfo().num_primal_infeasibilities == 0); REQUIRE(std::abs(h.getObjectiveValue() + 10.5) < 1e-8); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-clique-extract-origin", "[highs_test_presolve_rules]") { @@ -1166,7 +1166,7 @@ TEST_CASE("test-clique-no-delete-ranged-row", "[highs_test_presolve_rules]") { const std::vector& deleted = cliquetable.getDeletedRows(); REQUIRE(deleted.empty()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-clique-implied-equality", "[highs_test_presolve_rules]") { @@ -1219,7 +1219,7 @@ TEST_CASE("test-clique-implied-equality", "[highs_test_presolve_rules]") { REQUIRE(domain.isFixed(1)); REQUIRE(domain.col_lower_[1] == 1.0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-3364", "[highs_test_presolve_rules]") { @@ -1394,7 +1394,7 @@ TEST_CASE("test-aggregator-ranged-row", "[highs_test_presolve_rules]") { REQUIRE(std::abs(postsolved.col_value[1]) < 1e-8); REQUIRE(std::abs(postsolved.row_dual[0] + 1) < 1e-8); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } void solveAndCheck(const std::string& message, const HighsLp& lp, Highs& h, diff --git a/check/TestQpOracle.cpp b/check/TestQpOracle.cpp index 73477b3fd5f..08764422271 100644 --- a/check/TestQpOracle.cpp +++ b/check/TestQpOracle.cpp @@ -476,7 +476,7 @@ TEST_CASE("hessian-oracle-primal1", "[qp-oracle]") { } std::remove(write_model_filename.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } HighsHessian getHessianDiagonal4() { @@ -674,5 +674,5 @@ void testOracleSolve(const HighsModel& model) { if (k == 1) local_hessian = local_hessian.toSquare(); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestQpSolver.cpp b/check/TestQpSolver.cpp index 6223799a336..e22ba083169 100644 --- a/check/TestQpSolver.cpp +++ b/check/TestQpSolver.cpp @@ -89,7 +89,7 @@ TEST_CASE("qp-unbounded", "[qpsolver]") { highs.clearSolver(); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("qp-infeasible", "[qpsolver]") { @@ -106,7 +106,7 @@ TEST_CASE("qp-infeasible", "[qpsolver]") { REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("qpsolver", "[qpsolver]") { @@ -229,7 +229,7 @@ TEST_CASE("qpsolver", "[qpsolver]") { REQUIRE(return_status == HighsStatus::kOk); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-qod", "[qpsolver]") { @@ -382,7 +382,7 @@ TEST_CASE("test-qod", "[qpsolver]") { REQUIRE(fabs(solution.col_value[1] - required_x1) < double_equal_tolerance); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-qjh", "[qpsolver]") { @@ -509,7 +509,7 @@ TEST_CASE("test-qjh", "[qpsolver]") { } } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-min-negative-definite", "[qpsolver]") { @@ -538,7 +538,7 @@ TEST_CASE("test-min-negative-definite", "[qpsolver]") { REQUIRE(highs.run() == HighsStatus::kError); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-max-negative-definite", "[qpsolver]") { @@ -597,7 +597,7 @@ TEST_CASE("test-max-negative-definite", "[qpsolver]") { REQUIRE(fabs(solution.col_dual[0] + 1.0) < double_equal_tolerance); REQUIRE(fabs(solution.row_dual[0] + 0.5) < double_equal_tolerance); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-semi-definite0", "[qpsolver]") { @@ -645,7 +645,7 @@ TEST_CASE("test-semi-definite0", "[qpsolver]") { if (dev_run) highs.writeSolution("", kSolutionStylePretty); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-semi-definite1", "[qpsolver]") { @@ -695,7 +695,7 @@ TEST_CASE("test-semi-definite1", "[qpsolver]") { REQUIRE(fabs(solution.col_value[1]) < double_equal_tolerance); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-semi-definite2", "[qpsolver]") { @@ -745,7 +745,7 @@ TEST_CASE("test-semi-definite2", "[qpsolver]") { REQUIRE(fabs(solution.col_value[1] - 2) < double_equal_tolerance); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-qp-modification", "[qpsolver]") { @@ -860,7 +860,7 @@ TEST_CASE("test-qp-modification", "[qpsolver]") { highs.run(); if (dev_run) highs.writeSolution("", kSolutionStylePretty); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } } @@ -1146,7 +1146,7 @@ TEST_CASE("test-qp-hot-start", "[qpsolver]") { } } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("test-qp-terminations", "[qpsolver]") { @@ -1192,7 +1192,7 @@ TEST_CASE("test-qp-terminations", "[qpsolver]") { highs.setOptionValue("time_limit", kHighsInf); #endif - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("rowless-qp", "[qpsolver]") { @@ -1239,7 +1239,7 @@ TEST_CASE("rowless-qp", "[qpsolver]") { REQUIRE(dl_solution < 1e-6); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("issue-2489", "[qpsolver]") { @@ -1283,7 +1283,7 @@ TEST_CASE("issue-2489", "[qpsolver]") { REQUIRE(run_status == expected_status); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-2821", "[qpsolver]") { @@ -1336,7 +1336,7 @@ TEST_CASE("issue-2821", "[qpsolver]") { optimal_objective_value)); } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("pass-square-hessian", "[qpsolver]") { @@ -1380,7 +1380,7 @@ TEST_CASE("pass-square-hessian", "[qpsolver]") { REQUIRE(okValueDifference(info.objective_function_value, optimal_objective_value)); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } /* @@ -1405,7 +1405,7 @@ TEST_CASE("issue-2894", "[qpsolver]") { REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal); } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } */ @@ -1430,7 +1430,7 @@ TEST_CASE("issue-3322", "[qpsolver]") { REQUIRE(okValueDifference(h.getInfo().objective_function_value, required_objective_function_value)); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("issue-3045", "[qpsolver]") { @@ -1525,7 +1525,7 @@ TEST_CASE("issue-3045", "[qpsolver]") { (solver == kHipoString ? HighsStatus::kOk : HighsStatus::kError)); // } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("test-qp-atwood", "[qpsolver]") { @@ -1624,5 +1624,5 @@ TEST_CASE("test-qp-atwood", "[qpsolver]") { assert(basis_.valid); } } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/check/TestRanging.cpp b/check/TestRanging.cpp index a21dfc29ef8..d6815a7135c 100644 --- a/check/TestRanging.cpp +++ b/check/TestRanging.cpp @@ -64,7 +64,7 @@ HighsStatus quietRun(Highs& highs) { if (dev_run) highs.setOptionValue("output_flag", true); return call_status; - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void colCostColumnHeader() { @@ -554,5 +554,5 @@ void testRanging(Highs& highs) { REQUIRE(num_relative_error < 10); REQUIRE(max_relative_error < relative_error_tolerance); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestRays.cpp b/check/TestRays.cpp index 1375870855a..57d2dd30b85 100644 --- a/check/TestRays.cpp +++ b/check/TestRays.cpp @@ -306,7 +306,7 @@ void testInfeasibleMpsLp(const std::string model, // Now test with presolve on, and forcing ray calculation if possible - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void testInfeasibleMpsMip(const std::string model) { @@ -371,7 +371,7 @@ void testInfeasibleMpsMip(const std::string model) { HighsStatus::kOk); REQUIRE(!has_primal_ray); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void testUnboundedMpsLp(const std::string model, @@ -426,7 +426,7 @@ void testUnboundedMpsLp(const std::string model, REQUIRE(has_primal_ray); REQUIRE(checkPrimalRayValue(highs, primal_ray_value)); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Rays", "[highs_test_rays]") { @@ -656,7 +656,7 @@ TEST_CASE("Rays", "[highs_test_rays]") { REQUIRE(!has_primal_ray); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Rays-gas11", "[highs_test_rays]") { testUnboundedMpsLp("gas11"); } @@ -783,7 +783,7 @@ TEST_CASE("Rays-464a", "[highs_test_rays]") { highs.clearSolver(); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Rays-464b", "[highs_test_rays]") { @@ -880,7 +880,7 @@ TEST_CASE("Rays-464b", "[highs_test_rays]") { highs.clearSolver(); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Rays-infeasible-qp", "[highs_test_rays]") { @@ -930,7 +930,7 @@ TEST_CASE("Rays-infeasible-qp", "[highs_test_rays]") { REQUIRE(checkDualUnboundednessDirection(highs, dual_unboundedness_direction_value)); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("Rays-2415", "[highs_test_rays]") { diff --git a/check/TestRunData.cpp b/check/TestRunData.cpp index fa8930e3766..f1452802381 100644 --- a/check/TestRunData.cpp +++ b/check/TestRunData.cpp @@ -56,7 +56,7 @@ TEST_CASE("highs-run-data", "[highs_run_data]") { } if (!dev_run) std::remove(run_data_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } TEST_CASE("highs-run-data-presolve", "[highs_run_data]") { @@ -88,7 +88,7 @@ TEST_CASE("highs-run-data-presolve", "[highs_run_data]") { } std::remove(run_data_file.c_str()); - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } void testRunData(Highs& h, const bool irreducible, const bool reduces_to_empty, diff --git a/check/TestSemiVariables.cpp b/check/TestSemiVariables.cpp index 65f80ac6255..03a2d2b92a7 100644 --- a/check/TestSemiVariables.cpp +++ b/check/TestSemiVariables.cpp @@ -87,7 +87,7 @@ TEST_CASE("semi-variable-model", "[highs_test_semi_variables]") { REQUIRE(fabs(info.objective_function_value - optimal_objective_function_value) < double_equal_tolerance); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("semi-variable-lower-bound", "[highs_test_semi_variables]") { @@ -139,7 +139,7 @@ TEST_CASE("semi-variable-lower-bound", "[highs_test_semi_variables]") { // Check that the lower bound of the semi-variable has been restored REQUIRE(highs.getLp().col_lower_[semi_col] == semi_col_lower); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("semi-variable-upper-bound", "[highs_test_semi_variables]") { @@ -206,7 +206,7 @@ TEST_CASE("semi-variable-upper-bound", "[highs_test_semi_variables]") { if (dev_run) highs.writeSolution("", 1); REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("semi-variable-file", "[highs_test_semi_variables]") { @@ -247,7 +247,7 @@ TEST_CASE("semi-variable-file", "[highs_test_semi_variables]") { REQUIRE(fabs(info.objective_function_value - optimal_objective_function_value) < double_equal_tolerance); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("semi-variable-inconsistent-bounds", "[highs_test_semi_variables]") { @@ -280,7 +280,7 @@ TEST_CASE("semi-variable-inconsistent-bounds", "[highs_test_semi_variables]") { highs.run(); REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("semi-variable-inf-upper", "[highs_test_semi_variables]") { @@ -310,7 +310,7 @@ TEST_CASE("semi-variable-inf-upper", "[highs_test_semi_variables]") { REQUIRE(obj0 == obj1); std::remove(test_mps.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("semi-variable-fixed-write", "[highs_test_semi_variables]") { @@ -353,7 +353,7 @@ TEST_CASE("semi-variable-fixed-write", "[highs_test_semi_variables]") { REQUIRE(highs.getObjectiveValue() == 0); std::remove(test_file.c_str()); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } } @@ -397,5 +397,5 @@ TEST_CASE("3015", "[highs_test_semi_variables]") { REQUIRE(status == HighsStatus::kOk); REQUIRE(highs.getModelStatus() == HighsModelStatus::kOptimal); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestSpecialLps.cpp b/check/TestSpecialLps.cpp index 59389553f61..049486c53d7 100644 --- a/check/TestSpecialLps.cpp +++ b/check/TestSpecialLps.cpp @@ -49,7 +49,7 @@ void solve(Highs& highs, std::string presolve, std::string solver, REQUIRE(highs.resetOptions() == HighsStatus::kOk); highs.setOptionValue("output_flag", dev_run); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void distillation(Highs& highs) { @@ -617,7 +617,7 @@ void singularStartingBasis(Highs& highs) { special_lps.reportSolution(highs, dev_run); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void unconstrained(Highs& highs) { @@ -651,7 +651,7 @@ void unconstrained(Highs& highs) { REQUIRE(highs.run() == HighsStatus::kOk); REQUIRE(highs.getModelStatus() == HighsModelStatus::kInfeasible); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } void smallValue(Highs& highs) { @@ -663,7 +663,7 @@ void smallValue(Highs& highs) { HighsStatus::kWarning); REQUIRE(highs.run() == HighsStatus::kOk); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("LP-distillation", "[highs_test_special_lps]") { diff --git a/check/TestTspSolver.cpp b/check/TestTspSolver.cpp index c478243726e..d4cbbd37c8c 100644 --- a/check/TestTspSolver.cpp +++ b/check/TestTspSolver.cpp @@ -15,5 +15,5 @@ TEST_CASE("tsp-p01", "[highs_test_tsp_solver]") { highs.run(); REQUIRE(highs.getObjectiveValue() == optimal_obective_value); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } diff --git a/check/TestUserScale.cpp b/check/TestUserScale.cpp index 6a541675eb5..43c0ec9c2be 100644 --- a/check/TestUserScale.cpp +++ b/check/TestUserScale.cpp @@ -56,7 +56,7 @@ TEST_CASE("user-scale-after-run", "[highs_user_scale]") { HighsStatus::kOk); } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("chip-user-bound-scale", "[highs_user_scale]") { @@ -107,7 +107,7 @@ TEST_CASE("chip-user-bound-scale", "[highs_user_scale]") { chip_solution0 = 40; chip_solution1 = 21; } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("user-small-cost-scale", "[highs_user_scale]") { @@ -166,7 +166,7 @@ TEST_CASE("user-small-cost-scale", "[highs_user_scale]") { highs.run(); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } TEST_CASE("build-get-objective-bound-scaling", "[highs_data]") { @@ -278,7 +278,7 @@ TEST_CASE("build-get-objective-bound-scaling", "[highs_data]") { REQUIRE(suggested_objective_scale == 0); REQUIRE(suggested_bound_scale == 0); - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); } HighsLp lp0(const double cost, const double bound) { @@ -552,5 +552,5 @@ TEST_CASE("ill-scaled-model", "[highs_user_scale]") { testQp(h, large_cost, large_hessian, small_col_lower, large_bound); } - h.resetGlobalScheduler(true); + h.resetThreadScheduler(true); } diff --git a/docs/src/parallel.md b/docs/src/parallel.md index 132da716058..cc697b2f6ae 100644 --- a/docs/src/parallel.md +++ b/docs/src/parallel.md @@ -11,15 +11,12 @@ that can exploit the availability of a [GPU](@ref gpu). The value of the [threads](@ref option-threads) option determines the number of threads used by HiGHS. By default it is zero, in which case -HiGHS will use half the available threads on a machine. +HiGHS uses one thread per physical core available to your program. If it is set to one, then HiGHS will never use more than one thread. The maximum value that is advantageous is machine-dependent, but it is unlikely to be more than eight due to most computation in HiGHS being memory-bound. -Note that since parallelism is controlled by a static scheduler, -concurrent Highs instances must use the same value of `threads`. - ## Dual simplex By default, the HiGHS dual simplex solver runs in serial. However, it @@ -100,7 +97,7 @@ correct, but the options provide ways of overriding them. The extent to which parallelism is used in HiPO depends on the value of the [threads](@ref option-threads) option (see above). -## MIP +## [MIP](@id parallel-mip) If the [parallel](@ref option-parallel) option is not set to "off", the MIP solver will explore the branch-and-bound tree using multiple threads. @@ -128,3 +125,51 @@ Given the limited scope for parallelisation in HiGHS, if you have multiple independent instances to solve, then assign one instance per worker (cpu core, thread or machine) so that multiple instances are solved concurrently. + + + +## Parallel scheduler + +### What most users need to know + +HiGHS can use several threads to solve a model faster. If you call `run()` on a `Highs` instance from ordinary serial code, you do not need to do anything: + +- **Nothing to set up.** The first `run()` or `presolve()` starts the threads HiGHS needs. +- **Nothing to shut down.** The threads are reused by later solves and are stopped automatically when your program exits. Destroying a `Highs` instance does not stop them, and you do not need to do it yourself. +- **Choosing the number of threads.** By default, HiGHS uses one thread per physical core that your program is allowed to use. To use a different number, set the `threads` option before the first solve: + +```cpp + highs.setOptionValue("threads", 4); +``` + +All of this applies to the C API and to other languages as well. + +You only need the advanced section below if you run HiGHS from several threads of your own, or if you need to stop HiGHS' threads explicitly, for example before unloading a library. + +### Advanced + +#### [Scheduler scope and lifetime](@id scheduler-details) + +- An **application thread** is any thread not created by HiGHS. A **worker thread** is created by HiGHS as part of a scheduler. The **owning thread** is the application thread that created a scheduler, and it participates in the parallel work. +- The scheduler is `thread_local`. It is created lazily by the first `run()` or `presolve()` on an application thread, and is shared by all `Highs` instances used on that thread. It is never visible to other application threads. +- A scheduler of size `N` consists of the owning thread and `N - 1` workers. `N` is fixed at creation: `run()` and `presolve()` return an error if the `threads` option is nonzero and differs from `N`, so all `Highs` instances on a thread must use the same value (or the default). To change `N`, reset the scheduler first. The default is the number of physical cores in the process's CPU affinity mask. +- Running HiGHS from many application threads with the default thread count can create far more threads than you have cores and oversubscribe the machine. Set `threads` explicitly in that case. + + +#### Shutdown + +A scheduler is destroyed when its owning thread exits, or on `Highs::resetThreadScheduler(bool blocking = false)`. An explicit reset is only needed to release resources early, to change the number of worker threads, or to guarantee that the workers have terminated (for example before unloading a library). + +- `blocking = true` joins the workers before returning. +- `blocking = false` signals the workers to stop and detaches them, so some may still be running on return. Thread exit behaves like `false`. + +The call only affects the calling thread's scheduler, and is a no-op if there is none. A later `run()` or `presolve()` on that thread creates a new one. + +#### Thread safety + +`resetThreadScheduler()` must be called: + +- from the owning thread, and +- with no `run()` or `presolve()` active on it (in particular, not from a callback) and no outstanding tasks. + +It must not be called from a worker thread or from within a task. \ No newline at end of file diff --git a/docs/src/solvers.md b/docs/src/solvers.md index 0722979c2b7..247f9082462 100644 --- a/docs/src/solvers.md +++ b/docs/src/solvers.md @@ -155,10 +155,9 @@ add a test of Hessian convexity. ## MIP -MIPs are solved using a sophisticated branch-and-cut solver. This is -largely single-threaded, although a prototype multithreaded solver was -completed in February 2026, and is expected to be released by -June. Users can choose the solver for LP sub-problems as follows +MIPs are solved using a sophisticated branch-and-cut solver that exploits +multi-threading (see [here](@ref parallel-mip)). +Users can choose the solver for LP sub-problems as follows * LPs where an advanced basis is not known. This is generally the case at the root node of the branch-and-bound tree, but can occur at diff --git a/examples/call_highs_from_c_minimal.c b/examples/call_highs_from_c_minimal.c index dca30d6071c..4fada8db4a3 100644 --- a/examples/call_highs_from_c_minimal.c +++ b/examples/call_highs_from_c_minimal.c @@ -655,6 +655,6 @@ int main() { minimal_api(); minimal_api_qp(); full_api(); - Highs_resetGlobalScheduler(1); + Highs_resetThreadScheduler(1); return 0; } diff --git a/examples/call_highs_from_cpp.cpp b/examples/call_highs_from_cpp.cpp index 1cabee89142..2d0eba358af 100644 --- a/examples/call_highs_from_cpp.cpp +++ b/examples/call_highs_from_cpp.cpp @@ -172,7 +172,7 @@ int main() { cout << endl; } - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return 0; } diff --git a/examples/call_highs_hipdlp.cpp b/examples/call_highs_hipdlp.cpp index 7bc9ae85774..e2fce87897c 100644 --- a/examples/call_highs_hipdlp.cpp +++ b/examples/call_highs_hipdlp.cpp @@ -98,6 +98,6 @@ int main() { } std::cout << "PASS: HiPDLP solved the LP to optimality" << std::endl; - highs.resetGlobalScheduler(true); + highs.resetThreadScheduler(true); return EXIT_SUCCESS; } diff --git a/highs/Highs.h b/highs/Highs.h index 99c7f3f9136..b113c1e17e0 100644 --- a/highs/Highs.h +++ b/highs/Highs.h @@ -1341,7 +1341,7 @@ class Highs { std::string presolveRuleTypeToString(const HighsInt presolve_rule) const; /** - * @brief Ensures that the global scheduler is initialized, + * @brief Ensures that the scheduler is initialized, * returning HighsStatus::kError if it has already been initialized, * but the threads option is nonzero and not equal to * this->max_threads_. @@ -1349,19 +1349,20 @@ class Highs { HighsStatus initializeMultiThreading(); /** - * @brief Releases all resources held by the global scheduler instance. It is - * not thread-safe to call this function while calling run() or presolve() on - * any other Highs instance in any thread. After this function has terminated - * it is guaranteed that eventually all previously created scheduler threads - * will terminate and allocated memory will be released. After this function - * has returned the option value for the number of threads may be altered to a - * new value before the next call to run() or presolve(). If the given bool - * parameter has value true, then the function will not return until all - * memory is freed, which might be desirable when debugging heap memory but - * requires the calling thread to wait for all scheduler threads to wake-up - * which is usually not necessary. + * @brief Releases all resources held by the scheduler owned by the calling + * thread. It is thread-safe to call this function only from the thread that + * owns the scheduler (not from a worker thread) and when no run() or + * presolve() is in progress on that thread. After this function has + * terminated it is guaranteed that eventually all previously created + * scheduler threads will terminate and allocated memory will be released. + * After this function has returned the option value for the number of threads + * may be altered to a new value before the next call to run() or presolve(). + * If the given bool parameter has value true, then the function will not + * return until all memory is freed, which might be desirable when debugging + * heap memory but requires the calling thread to wait for all scheduler + * threads to wake-up which is usually not necessary. */ - static void resetGlobalScheduler(bool blocking = false); + static void resetThreadScheduler(bool blocking = false); /** * @brief If profiling is not nullptr, sets up profiling and copies @@ -1620,6 +1621,10 @@ class Highs { return HighsStatus::kError; } + // Deprecated in favour of resetThreadScheduler(). + // Cannot call deprecationMessage here. + static void resetGlobalScheduler(bool blocking = false); + // End of deprecated methods private: HighsSolution solution_; diff --git a/highs/interfaces/highs_c_api.cpp b/highs/interfaces/highs_c_api.cpp index af874762feb..1c7ab2067fc 100644 --- a/highs/interfaces/highs_c_api.cpp +++ b/highs/interfaces/highs_c_api.cpp @@ -1550,8 +1550,8 @@ HighsInt Highs_feasibilityRelaxation(void* highs, local_upper_penalty, local_rhs_penalty); } -void Highs_resetGlobalScheduler(HighsInt blocking) { - Highs::resetGlobalScheduler(blocking != 0); +void Highs_resetThreadScheduler(HighsInt blocking) { + Highs::resetThreadScheduler(blocking != 0); } const void* Highs_getCallbackDataOutItem(const HighsCallbackDataOut* data_out, @@ -1823,3 +1823,9 @@ double Highs_getHighsInfinity(const void* highs) { HighsInt Highs_getScaledModelStatus(const void* highs) { return (HighsInt)((Highs*)highs)->getModelStatus(); } + +// Deprecated in favour of resetThreadScheduler(). +// Cannot call deprecationMessage here. +void Highs_resetGlobalScheduler(HighsInt blocking) { + Highs::resetGlobalScheduler(blocking != 0); +} diff --git a/highs/interfaces/highs_c_api.h b/highs/interfaces/highs_c_api.h index 3fefad86125..104f73bfad2 100644 --- a/highs/interfaces/highs_c_api.h +++ b/highs/interfaces/highs_c_api.h @@ -356,10 +356,10 @@ void* Highs_create(void); * memory allocated for this instance. Future calls using `highs` are * not allowed. * - * Since the global scheduler's memory is shared by concurrent Highs - * instances, it cannot be freed by `Highs_destroy`. Hence, to free - * all memory used by HiGHS, `Highs_resetGlobalScheduler` must also be - * called. + * Since the scheduler is owned by the thread and not by the Highs + * instances, its memory cannot be freed by `Highs_destroy`. Hence, to free + * all memory used by HiGHS on this thread, `Highs_resetThreadScheduler` must + * also be called. * * To empty a model without invalidating `highs`, see `Highs_clearModel`. * @@ -2583,16 +2583,18 @@ HighsInt Highs_getIis(void* highs, HighsInt* iis_num_col, HighsInt* iis_num_row, HighsInt* col_bound, HighsInt* row_bound, HighsInt* col_status, HighsInt* row_status); /** - * Releases all resources held by the global scheduler instance. + * Releases all resources held by the scheduler owned by the calling thread, if + * any. * * Although the scheduler instance is created internally by a Highs - * instance, any subsequent Highs instances share the - * scheduler. Hence, calling `Highs_destroy` does not free the global - * scheduler's memory, so `Highs_resetGlobalScheduler` must also be + * instance, any subsequent Highs instances on the same thread share the + * scheduler. Hence, calling `Highs_destroy` does not free the + * scheduler's memory, so `Highs_resetThreadScheduler` must also be * called. * - * It is not thread-safe to call this function while calling `Highs_run` or one - * of the `Highs_XXXcall` methods on any other Highs instance in any thread. + * It is thread-safe to call this function only from the thread that owns the + * scheduler (not from a worker thread) and when no run() or presolve() is in + * progress on that thread. * * After this function has terminated, it is guaranteed that eventually all * previously created scheduler threads will terminate and allocated memory will @@ -2609,11 +2611,11 @@ HighsInt Highs_getIis(void* highs, HighsInt* iis_num_col, HighsInt* iis_num_row, * threads to wake-up which is usually not necessary. * * @returns No status is returned since the function call cannot fail. Calling - * this function while any Highs instance is in use on any thread is - * undefined behavior and may cause crashes, but cannot be detected and hence - * is fully in the callers responsibility. + * this function while on a worker thread or during the execution of a Highs + * function may cause crashes, but cannot be detected and hence is fully the + * caller's responsibility. */ -void Highs_resetGlobalScheduler(const HighsInt blocking); +void Highs_resetThreadScheduler(const HighsInt blocking); /** * Get a void* pointer to a callback data item @@ -2758,6 +2760,8 @@ HighsInt Highs_setOptionValue(void* highs, const char* option, HighsInt Highs_getScaledModelStatus(const void* highs); +void Highs_resetGlobalScheduler(const HighsInt blocking); + #ifdef __cplusplus } #endif diff --git a/highs/lp_data/Highs.cpp b/highs/lp_data/Highs.cpp index 8fca5b096db..7b8dcd952b1 100644 --- a/highs/lp_data/Highs.cpp +++ b/highs/lp_data/Highs.cpp @@ -1072,7 +1072,7 @@ HighsStatus Highs::presolve() { model_presolve_status_ = HighsPresolveStatus::kNotReduced; } else { const bool force_presolve = true; - // make sure global scheduler is initialized before calling presolve, since + // make sure scheduler is initialized before calling presolve, since // MIP presolve may use parallelism // return_status = this->initializeMultiThreading(); @@ -4996,16 +4996,16 @@ HighsStatus Highs::initializeMultiThreading() { highsLogDev( log_options, HighsLogType::kWarning, "Thread count (%d) exceeds physical cores (%d); consider reducing " - "threads or calling Highs::resetGlobalScheduler()\n", + "threads or calling Highs::resetThreadScheduler()\n", static_cast(this->max_threads_), static_cast(available_cores)); if (this->options_.threads != 0 && this->max_threads_ != this->options_.threads) { highsLogUser( log_options, HighsLogType::kError, - "Option 'threads' is set to %d but global scheduler has already been " + "Option 'threads' is set to %d but the scheduler has already been " "initialized to use %d threads. The previous scheduler instance can " - "be destroyed by calling Highs::resetGlobalScheduler().\n", + "be destroyed by calling Highs::resetThreadScheduler().\n", int(options_.threads), int(this->max_threads_)); return HighsStatus::kError; } @@ -5021,10 +5021,16 @@ HighsStatus Highs::initializeMultiThreading() { return HighsStatus::kOk; } -void Highs::resetGlobalScheduler(bool blocking) { +void Highs::resetThreadScheduler(bool blocking) { HighsTaskExecutor::shutdown(blocking); } +void Highs::resetGlobalScheduler(bool blocking) { + // Deprecated in favour of resetThreadScheduler(). + // Cannot call deprecationMessage here. + resetThreadScheduler(blocking); +} + void Highs::initializeSingleThreadedProfiling(HighsProfiling* profiling) { if (!profiling) return; profiling->multi_threaded = false; diff --git a/highspy/highs_bindings.cpp b/highspy/highs_bindings.cpp index 7bd55cc73fd..b74d31271ab 100644 --- a/highspy/highs_bindings.cpp +++ b/highspy/highs_bindings.cpp @@ -32,7 +32,7 @@ using dense_array_t = py::array_t; // 'getter' wrapper around std::vector to numpy array without copying data template std::function(const Base&)> make_readonly_ptr( - std::vector Base::* member) { + std::vector Base::*member) { return [member](const Base& self) -> dense_array_t { // last parameter means we keep ownership return dense_array_t((self.*member).size(), (self.*member).data(), @@ -44,7 +44,7 @@ std::function(const Base&)> make_readonly_ptr( // python) template std::function)> make_setter_ptr( - std::vector Base::* member) { + std::vector Base::*member) { return [member](Base& self, dense_array_t array) -> void { auto buf = array.request(); if (buf.ndim != 1) { @@ -1402,6 +1402,7 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) { .def("postsolve", &highs_postsolve) .def("postsolve", &highs_mipPostsolve) .def("run", &Highs::run, py::call_guard()) + .def_static("resetThreadScheduler", &Highs::resetThreadScheduler) .def_static("resetGlobalScheduler", &Highs::resetGlobalScheduler) .def( "feasibilityRelaxation", diff --git a/highspy/highspy/_core/__init__.pyi b/highspy/highspy/_core/__init__.pyi index 00b30a82e77..9eaf64374ca 100644 --- a/highspy/highspy/_core/__init__.pyi +++ b/highspy/highspy/_core/__init__.pyi @@ -1063,6 +1063,8 @@ class _Highs: def readOptions(self, file_path: str) -> HighsStatus: ... def readSolution(self, file_path: str, style: int) -> HighsStatus: ... @staticmethod + def resetThreadScheduler(blocking: bool) -> None: ... + @staticmethod def resetGlobalScheduler(blocking: bool) -> None: ... def resetOptions(self) -> HighsStatus: ... def run(self) -> HighsStatus: ... diff --git a/highspy/highspy/highs.py b/highspy/highspy/highs.py index 6b94721c078..01e05f2b825 100644 --- a/highspy/highspy/highs.py +++ b/highspy/highspy/highs.py @@ -200,7 +200,7 @@ def __solve(self) -> None: # avoid potential deadlock in Windows # can remove once HiGHS is updated to handle this internally - _Highs.resetGlobalScheduler(False) + _Highs.resetThreadScheduler(False) finally: self.__solver_stopped.release()