From 809305dbbd9a4cda2fcc01429476552f3ee2ac42 Mon Sep 17 00:00:00 2001 From: mcoury Date: Sat, 10 Dec 2022 16:43:55 -0800 Subject: [PATCH 1/6] Added taskflow support --- .gitmodules | 3 + .../preprocessing/include/dwave/presolve.hpp | 175 ++++++++++++------ extern/taskflow | 1 + setup.py | 2 + testscpp/Makefile | 14 +- 5 files changed, 137 insertions(+), 58 deletions(-) create mode 160000 extern/taskflow diff --git a/.gitmodules b/.gitmodules index 51341cc..3747830 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = testscpp/Catch2 url = https://github.com/catchorg/Catch2.git branch = v2.x +[submodule "extern/taskflow"] + path = extern/taskflow + url = https://github.com/taskflow/taskflow.git diff --git a/dwave/preprocessing/include/dwave/presolve.hpp b/dwave/preprocessing/include/dwave/presolve.hpp index d6d63c1..e0bc4ab 100644 --- a/dwave/preprocessing/include/dwave/presolve.hpp +++ b/dwave/preprocessing/include/dwave/presolve.hpp @@ -19,6 +19,8 @@ #include #include +#include "taskflow/core/taskflow.hpp" +#include "taskflow/taskflow.hpp" #include "dimod/constrained_quadratic_model.h" namespace dwave { @@ -139,7 +141,7 @@ class Presolver { model_type detach_model(); /// Load the default presolve techniques. - void load_default_presolvers(); + void load_default_presolvers(int max_rounds = 100); /// Return a const reference to the held constrained quadratic model. const model_type& model() const; @@ -148,12 +150,29 @@ class Presolver { const Postsolver& postsolver() const; private: + struct TfHelper { + tf::Executor executor; + tf::Taskflow taskflow_onetime; + tf::Taskflow taskflow_trivial; + tf::Taskflow taskflow_cleanup; + int loop_counter; + bool loop_changed; + bool model_feasible = true; + + bool operator=(const struct TfHelper& that) { + return true; + } + }; + + struct TfHelper tf_helper_; + + void load_taskflow_onetime(); + void load_taskflow_trivial(int max_rounds = 100); + void load_taskflow_cleanup(); + model_type model_; Postsolver postsolver_; - // todo: replace this with a vector of pointers or similar - bool default_techniques_; - bool detached_; void substitute_self_loops_expr(dimod::Expression& expression, @@ -281,20 +300,17 @@ class Presolver { switch (constraint.sense()) { case dimod::Sense::EQ: if (constraint.offset() != constraint.rhs()) { - // need this exact message for Python - throw std::logic_error("infeasible"); + tf_helper_.model_feasible = false; } break; case dimod::Sense::LE: if (constraint.offset() > constraint.rhs()) { - // need this exact message for Python - throw std::logic_error("infeasible"); + tf_helper_.model_feasible = false; } break; case dimod::Sense::GE: if (constraint.offset() < constraint.rhs()) { - // need this exact message for Python - throw std::logic_error("infeasible"); + tf_helper_.model_feasible = false; } break; } @@ -418,54 +434,25 @@ class Presolver { template Presolver::Presolver() - : model_(), postsolver_(), default_techniques_(false), detached_(false) {} + : model_(), postsolver_(), detached_(false) {} template Presolver::Presolver(model_type model) - : model_(std::move(model)), postsolver_(), default_techniques_(), detached_(false) {} + : model_(std::move(model)), postsolver_(), detached_(false) {} template void Presolver::apply() { if (detached_) throw std::logic_error("model has been detached, presolver is no longer valid"); - // If no techniques have been loaded, return early. - if (!default_techniques_) return; - - // One time techniques ---------------------------------------------------- - - // *-- spin-to-binary - technique_spin_to_binary(); - // *-- remove offsets - technique_remove_offsets(); - // *-- flip >= constraints - technique_flip_constraints(); - // *-- remove self-loops - technique_remove_self_loops(); - - // Trivial techniques ----------------------------------------------------- - - bool changes = true; - const index_type max_num_rounds = 100; // todo: make configurable - for (index_type num_rounds = 0; num_rounds < max_num_rounds; ++num_rounds) { - if (!changes) break; - changes = false; - - // *-- clear out 0 variables/interactions in the constraints and objective - changes |= technique_remove_zero_biases(); - // *-- todo: check for NAN - changes |= technique_check_for_nan(); - // *-- remove single variable constraints - changes |= technique_remove_single_variable_constraints(); - // *-- tighten bounds based on vartype - changes |= technique_tighten_bounds(); - // *-- remove variables that are fixed by bounds - changes |= technique_remove_fixed_variables(); - } - - // Cleanup - - // *-- remove any invalid discrete markers - technique_remove_invalid_markers(); + tf_helper_.executor.run(tf_helper_.taskflow_onetime).wait(); + tf_helper_.executor.run(tf_helper_.taskflow_trivial).wait(); + if(tf_helper_.model_feasible) { + tf_helper_.executor.run(tf_helper_.taskflow_cleanup).wait(); + } + else { + // need this exact message for Python + throw std::logic_error("infeasible"); + } } template @@ -482,8 +469,92 @@ Presolver::detach_model() { } template -void Presolver::load_default_presolvers() { - default_techniques_ = true; +void Presolver::load_default_presolvers(int max_rounds) { + load_taskflow_onetime(); + load_taskflow_trivial(); + load_taskflow_cleanup(); +} + +template +void Presolver::load_taskflow_onetime() { + auto [a, b, c, d] = tf_helper_.taskflow_onetime.emplace( + [&]() { technique_spin_to_binary(); }, + [&]() { technique_remove_offsets(); }, + [&]() { technique_flip_constraints(); }, + [&]() { technique_remove_self_loops(); } + ); + + a.name("spin_to_binary"); + b.name("remove_offsets"); + c.name("flip_constraints"); + d.name("remove_self_loops"); + + a.precede(b); + b.precede(c); + c.precede(d); +} + +template +void Presolver::load_taskflow_trivial(int max_rounds) { + auto alpha = tf_helper_.taskflow_trivial.emplace( + [&]() { + tf_helper_.loop_changed = false; + tf_helper_.loop_counter = 0; + } + ); + auto [a, b, c, d, e] = tf_helper_.taskflow_trivial.emplace( + [&]() { tf_helper_.loop_changed |= technique_remove_zero_biases(); }, + [&]() { tf_helper_.loop_changed |= technique_check_for_nan(); }, + [&]() { tf_helper_.loop_changed |= technique_remove_single_variable_constraints(); }, + [&]() + { + if(tf_helper_.model_feasible) { + tf_helper_.loop_changed |= technique_tighten_bounds(); + } + }, + [&]() + { + if(tf_helper_.model_feasible) { + tf_helper_.loop_changed |= technique_remove_fixed_variables(); + } + } + ); + auto omega = tf_helper_.taskflow_trivial.emplace( + [&]() { + if(tf_helper_.model_feasible + && tf_helper_.loop_changed + && ++tf_helper_.loop_counter < max_rounds + ) { + tf_helper_.loop_changed = false; + return 0; // This will take us back to (a) + } + return 1; // This will cause us to exit + } + ); + + alpha.name("initialize"); + a.name("remove_zero_biases"); + b.name("check_for_nan"); + c.name("remove_single_variable_constraints"); + d.name("tighten_bounds"); + e.name("remove_fixed_variables"); + omega.name("conditional"); + + alpha.precede(a); + a.precede(b); + b.precede(c); + c.precede(d); + d.precede(e); + e.precede(omega); + omega.precede(a); // loops back to (a) iff omega returns 0; o/w this will exit the taskflow +} + +template +void Presolver::load_taskflow_cleanup() { + auto a = tf_helper_.taskflow_cleanup.emplace( + [&]() { technique_remove_invalid_markers(); } + ); + a.name("remove_invalid_markers"); } template diff --git a/extern/taskflow b/extern/taskflow new file mode 160000 index 0000000..6633a09 --- /dev/null +++ b/extern/taskflow @@ -0,0 +1 @@ +Subproject commit 6633a0919065f27b6697a8fb51ed36fbda8d384a diff --git a/setup.py b/setup.py index 4ea96c4..1daf620 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ 'msvc': ['/std:c++17', '/EHsc'], 'unix': [ '-std=c++17', + '-pthread' ], } @@ -62,6 +63,7 @@ def build_extensions(self): include_dirs=[ numpy.get_include(), dimod.get_include(), + 'extern/taskflow' ], install_requires=[ 'numpy>=1.20.0,<2.0.0', # keep synced with circle-ci, pyproject.toml diff --git a/testscpp/Makefile b/testscpp/Makefile index a565fe2..0c4a184 100644 --- a/testscpp/Makefile +++ b/testscpp/Makefile @@ -1,11 +1,12 @@ -ROOT := ../ +ROOT := .. SRC := $(ROOT)/dwave/preprocessing/ CATCH2 := $(ROOT)/testscpp/Catch2/single_include/ DIMOD := $(shell python -c 'import dimod; print(dimod.get_include())') -INCLUDES := -I $(SRC)/include/ -I $(DIMOD) -I $(CATCH2) +TASKFLOW := $(ROOT)/extern/taskflow/ +INCLUDES := -I $(SRC)/include/ -I $(DIMOD) -I $(CATCH2) -I $(TASKFLOW) FLAGS := -std=c++17 -Wall -Wno-unknown-pragmas -Wno-sign-compare -Wno-deprecated-declarations -fcompare-debug-second -O3 -all: catch2 test_main test_main_parallel tests tests_parallel +all: update test_main test_main_parallel tests tests_parallel tests: test_main.out ./test_main @@ -14,13 +15,14 @@ tests_parallel: test_main_parallel.out ./test_main_parallel test_main: test_main.cpp - g++ $(FLAGS) -c test_main.cpp - g++ $(FLAGS) test_main.o tests/*.cpp -o test_main $(INCLUDES) + g++ $(FLAGS) -pthread -c test_main.cpp + g++ $(FLAGS) -pthread test_main.o tests/*.cpp -o test_main $(INCLUDES) + test_main_parallel: test_main.cpp g++ $(FLAGS) -fopenmp -c test_main.cpp -o test_main_parallel.o g++ $(FLAGS) -fopenmp test_main_parallel.o tests/*.cpp -o test_main_parallel $(INCLUDES) -catch2: +update: git submodule init git submodule update From d602f3d5bb1deffbc13cd6de0a1bd2ee5c01a7d2 Mon Sep 17 00:00:00 2001 From: mcoury Date: Fri, 16 Dec 2022 11:27:47 -0800 Subject: [PATCH 2/6] Adding ability to load specific presolvers --- .../preprocessing/include/dwave/presolve.hpp | 87 +++++++++++++------ 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/dwave/preprocessing/include/dwave/presolve.hpp b/dwave/preprocessing/include/dwave/presolve.hpp index e0bc4ab..fe11eb4 100644 --- a/dwave/preprocessing/include/dwave/presolve.hpp +++ b/dwave/preprocessing/include/dwave/presolve.hpp @@ -15,6 +15,7 @@ #pragma once #include +#include #include #include #include @@ -116,6 +117,23 @@ void Postsolver::substitute_variable(ind transforms_.back().offset = offset; } +class PresolverTechniques { +public: + const static uint64_t SPIN_TO_BINARY = 1 << 0; + const static uint64_t REMOVE_OFFSETS = 1 << 1; + const static uint64_t FLIP_CONSTRAINTS = 1 << 2; + const static uint64_t REMOVE_SELF_LOOPS = 1 << 3; + const static uint64_t REMOVE_INVALID_MARKERS = 1 << 4; + const static uint64_t CHECK_FOR_NAN = 1 << 5; + const static uint64_t REMOVE_SINGLE_VAR_CONSTRAINTS = 1 << 6; + const static uint64_t REMOVE_ZERO_BIASES = 1 << 7; + const static uint64_t TIGHTEN_BOUNDS = 1 << 8; + const static uint64_t REMOVE_FIXED_VARS = 1 << 9; + + const static uint64_t ALL = -1; + const static uint64_t NONE = 0; +}; + template class Presolver { public: @@ -141,7 +159,10 @@ class Presolver { model_type detach_model(); /// Load the default presolve techniques. - void load_default_presolvers(int max_rounds = 100); + void load_default_presolvers(uint32_t max_rounds = 100) { + load_presolvers(PresolverTechniques::ALL, max_rounds); + } + void load_presolvers(uint64_t presolvers = PresolverTechniques::ALL, uint32_t max_rounds = 100); /// Return a const reference to the held constrained quadratic model. const model_type& model() const; @@ -155,7 +176,7 @@ class Presolver { tf::Taskflow taskflow_onetime; tf::Taskflow taskflow_trivial; tf::Taskflow taskflow_cleanup; - int loop_counter; + uint32_t loop_counter; bool loop_changed; bool model_feasible = true; @@ -173,6 +194,8 @@ class Presolver { model_type model_; Postsolver postsolver_; + uint64_t presolvers_ = PresolverTechniques::NONE; + bool detached_; void substitute_self_loops_expr(dimod::Expression& expression, @@ -212,7 +235,7 @@ class Presolver { } } void technique_remove_offsets() { - for (size_type c = 0; c < model_.num_constraints(); ++c) { + for (size_type c = 0; c < model_.num_constraints(); ++c) { auto& constraint = model_.constraint_ref(c); if (constraint.offset()) { constraint.set_rhs(constraint.rhs() - constraint.offset()); @@ -221,7 +244,7 @@ class Presolver { } } void technique_flip_constraints() { - for (size_type c = 0; c < model_.num_constraints(); ++c) { + for (size_type c = 0; c < model_.num_constraints(); ++c) { auto& constraint = model_.constraint_ref(c); if (constraint.sense() == dimod::Sense::GE) { constraint.scale(-1); @@ -229,7 +252,7 @@ class Presolver { } } void technique_remove_self_loops() { - std::unordered_map mapping; + std::unordered_map mapping; substitute_self_loops_expr(model_.objective, mapping); @@ -244,7 +267,7 @@ class Presolver { } } void technique_remove_invalid_markers() { - std::vector discrete; + std::vector discrete; for (size_type c = 0; c < model_.num_constraints(); ++c) { auto& constraint = model_.constraint_ref(c); @@ -289,7 +312,7 @@ class Presolver { return false; } bool technique_remove_single_variable_constraints() { - bool ret = false; + bool ret = false; size_type c = 0; while (c < model_.num_constraints()) { auto& constraint = model_.constraint_ref(c); @@ -356,7 +379,7 @@ class Presolver { return ret; } bool technique_remove_zero_biases() { - bool ret = false; + bool ret = false; ret |= remove_zero_biases(model_.objective); for (size_t c = 0; c < model_.num_constraints(); ++c) { @@ -366,7 +389,7 @@ class Presolver { return ret; } bool technique_tighten_bounds() { - bool ret = false; + bool ret = false; bias_type lb; bias_type ub; for (size_type v = 0; v < model_.num_variables(); ++v) { @@ -392,7 +415,7 @@ class Presolver { return ret; } bool technique_remove_fixed_variables() { - bool ret = false; + bool ret = false; size_type v = 0; while (v < model_.num_variables()) { if (model_.lower_bound(v) == model_.upper_bound(v)) { @@ -443,6 +466,7 @@ Presolver::Presolver(model_type model) template void Presolver::apply() { if (detached_) throw std::logic_error("model has been detached, presolver is no longer valid"); + if (presolvers_ == PresolverTechniques::NONE) return; tf_helper_.executor.run(tf_helper_.taskflow_onetime).wait(); tf_helper_.executor.run(tf_helper_.taskflow_trivial).wait(); @@ -469,29 +493,42 @@ Presolver::detach_model() { } template -void Presolver::load_default_presolvers(int max_rounds) { +void Presolver::load_presolvers(uint64_t presolvers, uint32_t max_rounds) { + presolvers_ = presolvers; + load_taskflow_onetime(); - load_taskflow_trivial(); + load_taskflow_trivial(max_rounds); load_taskflow_cleanup(); } template void Presolver::load_taskflow_onetime() { - auto [a, b, c, d] = tf_helper_.taskflow_onetime.emplace( - [&]() { technique_spin_to_binary(); }, - [&]() { technique_remove_offsets(); }, - [&]() { technique_flip_constraints(); }, - [&]() { technique_remove_self_loops(); } - ); + std::vector tasks; - a.name("spin_to_binary"); - b.name("remove_offsets"); - c.name("flip_constraints"); - d.name("remove_self_loops"); + if(presolvers_ | PresolverTechniques::SPIN_TO_BINARY) { + auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_spin_to_binary(); } ); + task.name("spin_to_binary"); + tasks.emplace_back(task); + } + if(presolvers_ | PresolverTechniques::REMOVE_OFFSETS) { + auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_remove_offsets(); } ); + task.name("remove_offsets"); + tasks.emplace_back(task); + } + if(presolvers_ | PresolverTechniques::FLIP_CONSTRAINTS) { + auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_flip_constraints(); } ); + task.name("flip_constraints"); + tasks.emplace_back(task); + } + if(presolvers_ | PresolverTechniques::REMOVE_SELF_LOOPS) { + auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_remove_self_loops(); } ); + task.name("remove_self_loops"); + tasks.emplace_back(task); + } - a.precede(b); - b.precede(c); - c.precede(d); + for(auto i = 0; i < tasks.size() - 1; i++) { + tasks[i].precede(tasks[i+1]); + } } template From 929c62e5988b146a2a714f7e44c56a1022ddebac Mon Sep 17 00:00:00 2001 From: mcoury Date: Fri, 16 Dec 2022 12:08:40 -0800 Subject: [PATCH 3/6] Looped tasks can now be selected individually --- .../preprocessing/include/dwave/presolve.hpp | 199 ++++++++++-------- 1 file changed, 110 insertions(+), 89 deletions(-) diff --git a/dwave/preprocessing/include/dwave/presolve.hpp b/dwave/preprocessing/include/dwave/presolve.hpp index fe11eb4..561217f 100644 --- a/dwave/preprocessing/include/dwave/presolve.hpp +++ b/dwave/preprocessing/include/dwave/presolve.hpp @@ -20,9 +20,9 @@ #include #include +#include "dimod/constrained_quadratic_model.h" #include "taskflow/core/taskflow.hpp" #include "taskflow/taskflow.hpp" -#include "dimod/constrained_quadratic_model.h" namespace dwave { namespace presolve { @@ -118,17 +118,17 @@ void Postsolver::substitute_variable(ind } class PresolverTechniques { -public: - const static uint64_t SPIN_TO_BINARY = 1 << 0; - const static uint64_t REMOVE_OFFSETS = 1 << 1; - const static uint64_t FLIP_CONSTRAINTS = 1 << 2; - const static uint64_t REMOVE_SELF_LOOPS = 1 << 3; - const static uint64_t REMOVE_INVALID_MARKERS = 1 << 4; - const static uint64_t CHECK_FOR_NAN = 1 << 5; - const static uint64_t REMOVE_SINGLE_VAR_CONSTRAINTS = 1 << 6; - const static uint64_t REMOVE_ZERO_BIASES = 1 << 7; - const static uint64_t TIGHTEN_BOUNDS = 1 << 8; - const static uint64_t REMOVE_FIXED_VARS = 1 << 9; + public: + const static uint64_t SPIN_TO_BINARY = 1 << 0; + const static uint64_t REMOVE_OFFSETS = 1 << 1; + const static uint64_t FLIP_CONSTRAINTS = 1 << 2; + const static uint64_t REMOVE_SELF_LOOPS = 1 << 3; + const static uint64_t REMOVE_INVALID_MARKERS = 1 << 4; + const static uint64_t CHECK_FOR_NAN = 1 << 5; + const static uint64_t REMOVE_SINGLE_VAR_CONSTRAINTS = 1 << 6; + const static uint64_t REMOVE_ZERO_BIASES = 1 << 7; + const static uint64_t TIGHTEN_BOUNDS = 1 << 8; + const static uint64_t REMOVE_FIXED_VARS = 1 << 9; const static uint64_t ALL = -1; const static uint64_t NONE = 0; @@ -180,13 +180,11 @@ class Presolver { bool loop_changed; bool model_feasible = true; - bool operator=(const struct TfHelper& that) { - return true; - } + bool operator=(const struct TfHelper& that) { return true; } }; struct TfHelper tf_helper_; - + void load_taskflow_onetime(); void load_taskflow_trivial(int max_rounds = 100); void load_taskflow_cleanup(); @@ -235,7 +233,7 @@ class Presolver { } } void technique_remove_offsets() { - for (size_type c = 0; c < model_.num_constraints(); ++c) { + for (size_type c = 0; c < model_.num_constraints(); ++c) { auto& constraint = model_.constraint_ref(c); if (constraint.offset()) { constraint.set_rhs(constraint.rhs() - constraint.offset()); @@ -244,7 +242,7 @@ class Presolver { } } void technique_flip_constraints() { - for (size_type c = 0; c < model_.num_constraints(); ++c) { + for (size_type c = 0; c < model_.num_constraints(); ++c) { auto& constraint = model_.constraint_ref(c); if (constraint.sense() == dimod::Sense::GE) { constraint.scale(-1); @@ -252,7 +250,7 @@ class Presolver { } } void technique_remove_self_loops() { - std::unordered_map mapping; + std::unordered_map mapping; substitute_self_loops_expr(model_.objective, mapping); @@ -267,7 +265,7 @@ class Presolver { } } void technique_remove_invalid_markers() { - std::vector discrete; + std::vector discrete; for (size_type c = 0; c < model_.num_constraints(); ++c) { auto& constraint = model_.constraint_ref(c); @@ -312,7 +310,11 @@ class Presolver { return false; } bool technique_remove_single_variable_constraints() { - bool ret = false; + if (!tf_helper_.model_feasible) { + return false; + } + + bool ret = false; size_type c = 0; while (c < model_.num_constraints()) { auto& constraint = model_.constraint_ref(c); @@ -379,17 +381,23 @@ class Presolver { return ret; } bool technique_remove_zero_biases() { - bool ret = false; + if (!tf_helper_.model_feasible) { + return false; + } + bool ret = false; ret |= remove_zero_biases(model_.objective); for (size_t c = 0; c < model_.num_constraints(); ++c) { ret |= remove_zero_biases(model_.constraint_ref(c)); } - return ret; } bool technique_tighten_bounds() { - bool ret = false; + if (!tf_helper_.model_feasible) { + return false; + } + + bool ret = false; bias_type lb; bias_type ub; for (size_type v = 0; v < model_.num_variables(); ++v) { @@ -415,7 +423,11 @@ class Presolver { return ret; } bool technique_remove_fixed_variables() { - bool ret = false; + if (!tf_helper_.model_feasible) { + return false; + } + + bool ret = false; size_type v = 0; while (v < model_.num_variables()) { if (model_.lower_bound(v) == model_.upper_bound(v)) { @@ -470,10 +482,9 @@ void Presolver::apply() { tf_helper_.executor.run(tf_helper_.taskflow_onetime).wait(); tf_helper_.executor.run(tf_helper_.taskflow_trivial).wait(); - if(tf_helper_.model_feasible) { + if (tf_helper_.model_feasible) { tf_helper_.executor.run(tf_helper_.taskflow_cleanup).wait(); - } - else { + } else { // need this exact message for Python throw std::logic_error("infeasible"); } @@ -493,7 +504,8 @@ Presolver::detach_model() { } template -void Presolver::load_presolvers(uint64_t presolvers, uint32_t max_rounds) { +void Presolver::load_presolvers(uint64_t presolvers, + uint32_t max_rounds) { presolvers_ = presolvers; load_taskflow_onetime(); @@ -505,92 +517,101 @@ template void Presolver::load_taskflow_onetime() { std::vector tasks; - if(presolvers_ | PresolverTechniques::SPIN_TO_BINARY) { - auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_spin_to_binary(); } ); + if (presolvers_ | PresolverTechniques::SPIN_TO_BINARY) { + auto task = tf_helper_.taskflow_onetime.emplace([&]() { technique_spin_to_binary(); }); task.name("spin_to_binary"); tasks.emplace_back(task); } - if(presolvers_ | PresolverTechniques::REMOVE_OFFSETS) { - auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_remove_offsets(); } ); + if (presolvers_ | PresolverTechniques::REMOVE_OFFSETS) { + auto task = tf_helper_.taskflow_onetime.emplace([&]() { technique_remove_offsets(); }); task.name("remove_offsets"); tasks.emplace_back(task); } - if(presolvers_ | PresolverTechniques::FLIP_CONSTRAINTS) { - auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_flip_constraints(); } ); + if (presolvers_ | PresolverTechniques::FLIP_CONSTRAINTS) { + auto task = tf_helper_.taskflow_onetime.emplace([&]() { technique_flip_constraints(); }); task.name("flip_constraints"); tasks.emplace_back(task); } - if(presolvers_ | PresolverTechniques::REMOVE_SELF_LOOPS) { - auto task = tf_helper_.taskflow_onetime.emplace( [&]() { technique_remove_self_loops(); } ); + if (presolvers_ | PresolverTechniques::REMOVE_SELF_LOOPS) { + auto task = tf_helper_.taskflow_onetime.emplace([&]() { technique_remove_self_loops(); }); task.name("remove_self_loops"); tasks.emplace_back(task); } - for(auto i = 0; i < tasks.size() - 1; i++) { - tasks[i].precede(tasks[i+1]); + for (auto i = 0; i < tasks.size() - 1; i++) { + tasks[i].precede(tasks[i + 1]); } } template void Presolver::load_taskflow_trivial(int max_rounds) { - auto alpha = tf_helper_.taskflow_trivial.emplace( - [&]() { + auto alpha = tf_helper_.taskflow_trivial.emplace([&]() { + tf_helper_.loop_changed = false; + tf_helper_.loop_counter = 0; + }); + alpha.name("initialize"); + + auto omega = tf_helper_.taskflow_trivial.emplace([&]() { + if (tf_helper_.model_feasible && tf_helper_.loop_changed && + ++tf_helper_.loop_counter < max_rounds) { tf_helper_.loop_changed = false; - tf_helper_.loop_counter = 0; + return 0; // This will loop back to the first task in tasks, below } - ); - auto [a, b, c, d, e] = tf_helper_.taskflow_trivial.emplace( - [&]() { tf_helper_.loop_changed |= technique_remove_zero_biases(); }, - [&]() { tf_helper_.loop_changed |= technique_check_for_nan(); }, - [&]() { tf_helper_.loop_changed |= technique_remove_single_variable_constraints(); }, - [&]() - { - if(tf_helper_.model_feasible) { - tf_helper_.loop_changed |= technique_tighten_bounds(); - } - }, - [&]() - { - if(tf_helper_.model_feasible) { - tf_helper_.loop_changed |= technique_remove_fixed_variables(); - } - } - ); - auto omega = tf_helper_.taskflow_trivial.emplace( - [&]() { - if(tf_helper_.model_feasible - && tf_helper_.loop_changed - && ++tf_helper_.loop_counter < max_rounds - ) { - tf_helper_.loop_changed = false; - return 0; // This will take us back to (a) - } - return 1; // This will cause us to exit - } - ); - - alpha.name("initialize"); - a.name("remove_zero_biases"); - b.name("check_for_nan"); - c.name("remove_single_variable_constraints"); - d.name("tighten_bounds"); - e.name("remove_fixed_variables"); + return 1; // This will exit the loop + }); omega.name("conditional"); - alpha.precede(a); - a.precede(b); - b.precede(c); - c.precede(d); - d.precede(e); - e.precede(omega); - omega.precede(a); // loops back to (a) iff omega returns 0; o/w this will exit the taskflow + std::vector tasks; + if (presolvers_ | PresolverTechniques::REMOVE_ZERO_BIASES) { + auto task = tf_helper_.taskflow_trivial.emplace( + [&]() { tf_helper_.loop_changed |= technique_remove_zero_biases(); }); + task.name("remove_zero_biases"); + tasks.emplace_back(task); + } + if (presolvers_ | PresolverTechniques::CHECK_FOR_NAN) { + auto task = tf_helper_.taskflow_trivial.emplace( + [&]() { tf_helper_.loop_changed |= technique_check_for_nan(); }); + task.name("check_for_nan"); + tasks.emplace_back(task); + } + if (presolvers_ | PresolverTechniques::REMOVE_SINGLE_VAR_CONSTRAINTS) { + auto task = tf_helper_.taskflow_trivial.emplace([&]() { + tf_helper_.loop_changed |= technique_remove_single_variable_constraints(); + }); + task.name("remove_single_variable_constraints"); + tasks.emplace_back(task); + } + if (presolvers_ | PresolverTechniques::TIGHTEN_BOUNDS) { + auto task = tf_helper_.taskflow_trivial.emplace( + [&]() { tf_helper_.loop_changed |= technique_tighten_bounds(); }); + task.name("tighten_bounds"); + tasks.emplace_back(task); + } + if (presolvers_ | PresolverTechniques::REMOVE_FIXED_VARS) { + auto task = tf_helper_.taskflow_trivial.emplace( + [&]() { tf_helper_.loop_changed |= technique_remove_fixed_variables(); }); + task.name("remove_fixed_variables"); + tasks.emplace_back(task); + } + + if (tasks.size() > 0) { + alpha.precede(tasks[0]); + + for (auto i = 0; i < tasks.size() - 1; i++) { + tasks[i].precede(tasks[i + 1]); + } + + tasks[tasks.size() - 1].precede(omega); + omega.precede(tasks[0]); + } + else { + alpha.precede(omega); + } } template void Presolver::load_taskflow_cleanup() { - auto a = tf_helper_.taskflow_cleanup.emplace( - [&]() { technique_remove_invalid_markers(); } - ); + auto a = tf_helper_.taskflow_cleanup.emplace([&]() { technique_remove_invalid_markers(); }); a.name("remove_invalid_markers"); } From 5217040133737160bf4e3fb59f4adc371e356ca8 Mon Sep 17 00:00:00 2001 From: mcoury Date: Fri, 16 Dec 2022 12:11:38 -0800 Subject: [PATCH 4/6] Cleanup tasks can now be selected individually --- dwave/preprocessing/include/dwave/presolve.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dwave/preprocessing/include/dwave/presolve.hpp b/dwave/preprocessing/include/dwave/presolve.hpp index 561217f..f36a5d2 100644 --- a/dwave/preprocessing/include/dwave/presolve.hpp +++ b/dwave/preprocessing/include/dwave/presolve.hpp @@ -611,8 +611,11 @@ void Presolver::load_taskflow_trivial(in template void Presolver::load_taskflow_cleanup() { - auto a = tf_helper_.taskflow_cleanup.emplace([&]() { technique_remove_invalid_markers(); }); - a.name("remove_invalid_markers"); + if (presolvers_ | PresolverTechniques::REMOVE_INVALID_MARKERS) { + auto task = tf_helper_.taskflow_cleanup.emplace( + [&]() { technique_remove_invalid_markers(); }); + task.name("remove_invalid_markers"); + } } template From f964bb6d7999366de59eac18890f828adf0c1d38 Mon Sep 17 00:00:00 2001 From: mcoury Date: Fri, 16 Dec 2022 12:14:32 -0800 Subject: [PATCH 5/6] Clanging presolve.hpp --- dwave/preprocessing/include/dwave/presolve.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dwave/preprocessing/include/dwave/presolve.hpp b/dwave/preprocessing/include/dwave/presolve.hpp index f36a5d2..ed0fb52 100644 --- a/dwave/preprocessing/include/dwave/presolve.hpp +++ b/dwave/preprocessing/include/dwave/presolve.hpp @@ -603,8 +603,7 @@ void Presolver::load_taskflow_trivial(in tasks[tasks.size() - 1].precede(omega); omega.precede(tasks[0]); - } - else { + } else { alpha.precede(omega); } } @@ -612,8 +611,8 @@ void Presolver::load_taskflow_trivial(in template void Presolver::load_taskflow_cleanup() { if (presolvers_ | PresolverTechniques::REMOVE_INVALID_MARKERS) { - auto task = tf_helper_.taskflow_cleanup.emplace( - [&]() { technique_remove_invalid_markers(); }); + auto task = + tf_helper_.taskflow_cleanup.emplace([&]() { technique_remove_invalid_markers(); }); task.name("remove_invalid_markers"); } } From baf11672e839ec5bb04e5a905c12d848809e4c55 Mon Sep 17 00:00:00 2001 From: mcoury Date: Mon, 19 Dec 2022 09:38:53 -0800 Subject: [PATCH 6/6] Setting taskflow to v3.4.0 (f1490ffc286eba418107c4aa9b8913e8ca76443c) --- extern/taskflow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/taskflow b/extern/taskflow index 6633a09..f1490ff 160000 --- a/extern/taskflow +++ b/extern/taskflow @@ -1 +1 @@ -Subproject commit 6633a0919065f27b6697a8fb51ed36fbda8d384a +Subproject commit f1490ffc286eba418107c4aa9b8913e8ca76443c