From ae0c1532a815ac5592b815e91c80901014626b28 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 09:38:15 +0100 Subject: [PATCH 01/12] allow for more than one variable branching decision --- dev/main.cpp | 2 +- lib/CMakeLists.txt | 6 ++-- .../branch-and-bound/BranchAndBound.h | 4 +-- .../factories/BracnhingWithPriority.h | 4 +-- .../{NodeVarInfo.h => DefaultNodeInfo.h} | 30 +++++++++---------- ...{NodeVarUpdator.h => DefaultNodeUpdator.h} | 30 +++++++++---------- .../watchers/ExportBranchAndBoundTreeToCSV.h | 10 +++---- .../idol/optimizers/callbacks/IntegerMaster.h | 10 +++---- .../{NodeVarInfo.cpp => DefaultNodeInfo.cpp} | 22 +++++++------- .../watchers/BranchAndBoundTree.cpp | 19 ++++++------ .../optimizers/callbacks/IntegerMaster.cpp | 6 ++-- tests/branch-and-bound/knapsack.cpp | 12 ++++---- tests/branch-and-bound/location.cpp | 12 ++++---- tests/branch-and-price/assignment.cpp | 8 ++--- 14 files changed, 86 insertions(+), 89 deletions(-) rename lib/include/idol/optimizers/branch-and-bound/nodes/{NodeVarInfo.h => DefaultNodeInfo.h} (61%) rename lib/include/idol/optimizers/branch-and-bound/nodes/{NodeVarUpdator.h => DefaultNodeUpdator.h} (77%) rename lib/src/optimizers/branch-and-bound/nodes/{NodeVarInfo.cpp => DefaultNodeInfo.cpp} (59%) diff --git a/dev/main.cpp b/dev/main.cpp index 83c723764..3bf49688a 100644 --- a/dev/main.cpp +++ b/dev/main.cpp @@ -1,6 +1,6 @@ #include "idol/modeling.h" #include "idol/problems/generalized-assignment-problem/GAP_Instance.h" -#include "idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h" +#include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" #include "idol/optimizers/branch-and-bound/BranchAndBound.h" #include "idol/optimizers/branch-and-bound/branching-rules/factories/MostInfeasible.h" #include "idol/optimizers/branch-and-bound/node-selection-rules/factories/BestBound.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b1f72e689..93a447a3a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -154,9 +154,9 @@ add_library(idol STATIC src/optimizers/branch-and-bound/branching-rules/factories/StrongBranchingPhase.cpp include/idol/optimizers/branch-and-bound/branching-rules/factories/PseudoCost.h include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h - include/idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h - include/idol/optimizers/branch-and-bound/nodes/NodeVarUpdator.h - src/optimizers/branch-and-bound/nodes/NodeVarInfo.cpp + include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h + include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h + src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp include/idol/optimizers/wrappers/HiGHS/HiGHS.h src/optimizers/wrappers/HiGHS/HiGHS.cpp src/optimizers/wrappers/HiGHS/Optimizers_HiGHS.cpp diff --git a/lib/include/idol/optimizers/branch-and-bound/BranchAndBound.h b/lib/include/idol/optimizers/branch-and-bound/BranchAndBound.h index 8f875dc06..b7cbebb6d 100644 --- a/lib/include/idol/optimizers/branch-and-bound/BranchAndBound.h +++ b/lib/include/idol/optimizers/branch-and-bound/BranchAndBound.h @@ -13,7 +13,7 @@ #include "idol/optimizers/branch-and-bound/callbacks/CallbackAsBranchAndBoundCallback.h" #include "idol/optimizers/callbacks/CallbackFactory.h" #include "idol/optimizers/branch-and-bound/cutting-planes/CuttingPlaneGenerator.h" -#include "idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h" +#include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" namespace idol { template @@ -24,7 +24,7 @@ namespace idol { * @tparam NodeT the class used to store nodes information. * It is strongly advised to inherit from NodeVarInfo in order to create your own node type. */ -template +template class idol::BranchAndBound : public OptimizerFactoryWithDefaultParameters> { std::unique_ptr m_relaxation_optimizer_factory; std::unique_ptr> m_branching_rule_factory; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/BracnhingWithPriority.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/BracnhingWithPriority.h index ae7e254e7..c320ea046 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/BracnhingWithPriority.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/BracnhingWithPriority.h @@ -10,13 +10,13 @@ #include "idol/optimizers/branch-and-bound/branching-rules/impls/BranchingWithPriority.h" namespace idol { - class NodeVarInfo; + class DefaultNodeInfo; template class BranchingWithPriority; } -template +template class idol::BranchingWithPriority : public BranchingRuleFactory { std::list>> m_branching_rules; public: diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h similarity index 61% rename from lib/include/idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h rename to lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h index 12d46e5c6..bc7d4a913 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h @@ -2,14 +2,14 @@ // Created by henri on 18.10.23. // -#ifndef IDOL_NODEVARINFO_H -#define IDOL_NODEVARINFO_H +#ifndef IDOL_DEFAULTNODEINFO_H +#define IDOL_DEFAULTNODEINFO_H #include "idol/modeling/models/Model.h" -#include "NodeVarUpdator.h" +#include "DefaultNodeUpdator.h" namespace idol { - class NodeVarInfo; + class DefaultNodeInfo; namespace Optimizers { template class BranchAndBound; @@ -17,18 +17,18 @@ namespace idol { } -class idol::NodeVarInfo { +class idol::DefaultNodeInfo { public: - struct BranchingDecision { + struct VarBranchingDecision { Var variable; CtrType type; double bound; - BranchingDecision(Var t_variable, CtrType t_type, double t_bound) : variable(std::move(t_variable)), type(t_type), bound(t_bound) {} + VarBranchingDecision(Var t_variable, CtrType t_type, double t_bound) : variable(std::move(t_variable)), type(t_type), bound(t_bound) {} }; - NodeVarInfo() = default; + DefaultNodeInfo() = default; - virtual ~NodeVarInfo() = default; + virtual ~DefaultNodeInfo() = default; [[nodiscard]] SolutionStatus status() const { return m_primal_solution.status(); } @@ -46,21 +46,19 @@ class idol::NodeVarInfo { virtual void save(const Model& t_original_formulation, const Model& t_model); - [[nodiscard]] virtual NodeVarInfo* create_child() const; + [[nodiscard]] virtual DefaultNodeInfo* create_child() const; void set_local_lower_bound(const Var& t_var, double t_lb); void set_local_upper_bound(const Var& t_var, double t_ub); - bool has_branching_decision() const { return m_branching_decision.has_value(); } + [[nodiscard]] auto variable_branching_decisions() const { return ConstIteratorForward(m_branching_decision); } - [[nodiscard]] const BranchingDecision& branching_decision() const { return m_branching_decision.value(); } - - static NodeVarUpdator* create_updator(Model& t_relaxation); + static DefaultNodeUpdator* create_updator(Model& t_relaxation); private: Solution::Primal m_primal_solution; std::optional m_sum_of_infeasibilities; - std::optional m_branching_decision; + std::list m_branching_decision; }; -#endif //IDOL_NODEVARINFO_H +#endif //IDOL_DEFAULTNODEINFO_H diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeVarUpdator.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h similarity index 77% rename from lib/include/idol/optimizers/branch-and-bound/nodes/NodeVarUpdator.h rename to lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h index 1b796f9d1..ada9e6bb4 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeVarUpdator.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h @@ -2,8 +2,8 @@ // Created by henri on 18.10.23. // -#ifndef IDOL_NODEVARUPDATOR_H -#define IDOL_NODEVARUPDATOR_H +#ifndef IDOL_DEFAULTNODEUPDATOR_H +#define IDOL_DEFAULTNODEUPDATOR_H #include "NodeUpdator.h" #include "idol/modeling/models/Model.h" @@ -11,11 +11,11 @@ namespace idol { template - class NodeVarUpdator; + class DefaultNodeUpdator; } template -class idol::NodeVarUpdator : public NodeUpdator { +class idol::DefaultNodeUpdator : public NodeUpdator { Model& m_relaxation; @@ -27,13 +27,13 @@ class idol::NodeVarUpdator : public NodeUpdator { Map &t_changed_lower_bounds, Map &t_changed_upper_bounds); - void apply_local_update(const typename NodeVarInfoT::BranchingDecision& t_branching_decision, + void apply_local_update(const typename NodeVarInfoT::VarBranchingDecision& t_branching_decision, Map &t_current_changed_bounds, Map &t_changed_bounds); public: - explicit NodeVarUpdator(Model& t_relaxation); + explicit DefaultNodeUpdator(Model& t_relaxation); - NodeVarUpdator(const NodeVarUpdator&) = delete; + DefaultNodeUpdator(const DefaultNodeUpdator&) = delete; void apply_local_updates(const Node &t_node) override; @@ -46,12 +46,12 @@ class idol::NodeVarUpdator : public NodeUpdator { }; template -idol::NodeVarUpdator::NodeVarUpdator(idol::Model &t_relaxation) : m_relaxation(t_relaxation) { +idol::DefaultNodeUpdator::DefaultNodeUpdator(idol::Model &t_relaxation) : m_relaxation(t_relaxation) { } template -void idol::NodeVarUpdator::clear_local_updates() { +void idol::DefaultNodeUpdator::clear_local_updates() { for (const auto& [var, lb] : m_changed_lower_bounds) { m_relaxation.set_var_lb(var, lb); @@ -66,7 +66,7 @@ void idol::NodeVarUpdator::clear_local_updates() { } template -void idol::NodeVarUpdator::apply_local_updates(const Node &t_node) { +void idol::DefaultNodeUpdator::apply_local_updates(const Node &t_node) { Map changed_lower_bounds; Map changed_upper_bounds; @@ -79,7 +79,7 @@ void idol::NodeVarUpdator::apply_local_updates(const Node -void idol::NodeVarUpdator::apply_local_updates(const idol::Node &t_node, +void idol::DefaultNodeUpdator::apply_local_updates(const idol::Node &t_node, Map &t_changed_lower_bounds, Map &t_changed_upper_bounds ) { @@ -88,9 +88,7 @@ void idol::NodeVarUpdator::apply_local_updates(const idol::Node::apply_local_updates(const idol::Node -void idol::NodeVarUpdator::apply_local_update(const typename NodeVarInfoT::BranchingDecision &t_branching_decision, +void idol::DefaultNodeUpdator::apply_local_update(const typename NodeVarInfoT::VarBranchingDecision &t_branching_decision, idol::Map &t_current_changed_bounds, idol::Map &t_changed_bounds) { @@ -144,4 +142,4 @@ void idol::NodeVarUpdator::apply_local_update(const typename NodeV } -#endif //IDOL_NODEVARUPDATOR_H +#endif //IDOL_DEFAULTNODEUPDATOR_H diff --git a/lib/include/idol/optimizers/branch-and-bound/watchers/ExportBranchAndBoundTreeToCSV.h b/lib/include/idol/optimizers/branch-and-bound/watchers/ExportBranchAndBoundTreeToCSV.h index 38d778582..bae8c3570 100644 --- a/lib/include/idol/optimizers/branch-and-bound/watchers/ExportBranchAndBoundTreeToCSV.h +++ b/lib/include/idol/optimizers/branch-and-bound/watchers/ExportBranchAndBoundTreeToCSV.h @@ -6,7 +6,7 @@ #define IDOL_EXPORTBRANCHANDBOUNDTREETOCSV_H -#include "idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h" +#include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" #include "idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h" #include "idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h" @@ -14,16 +14,16 @@ namespace idol::Utils { class ExportBranchAndBoundTreeToCSV; } -class idol::Utils::ExportBranchAndBoundTreeToCSV : public BranchAndBoundCallbackFactory { +class idol::Utils::ExportBranchAndBoundTreeToCSV : public BranchAndBoundCallbackFactory { const std::string m_filename; public: explicit ExportBranchAndBoundTreeToCSV(std::string t_filename); - BranchAndBoundCallback *operator()() override; + BranchAndBoundCallback *operator()() override; - BranchAndBoundCallbackFactory *clone() const override; + BranchAndBoundCallbackFactory *clone() const override; - class Strategy : public BranchAndBoundCallback { + class Strategy : public BranchAndBoundCallback { const std::string m_filename; protected: void operator()(CallbackEvent t_event) override; diff --git a/lib/include/idol/optimizers/callbacks/IntegerMaster.h b/lib/include/idol/optimizers/callbacks/IntegerMaster.h index c2c8548f9..6960d4f59 100644 --- a/lib/include/idol/optimizers/callbacks/IntegerMaster.h +++ b/lib/include/idol/optimizers/callbacks/IntegerMaster.h @@ -5,7 +5,7 @@ #ifndef IDOL_INTEGERMASTER_H #define IDOL_INTEGERMASTER_H -#include "idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h" +#include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" #include "idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h" #include "idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h" @@ -13,7 +13,7 @@ namespace idol::Heuristics { class IntegerMaster; } -class idol::Heuristics::IntegerMaster : public BranchAndBoundCallbackFactory { +class idol::Heuristics::IntegerMaster : public BranchAndBoundCallbackFactory { std::unique_ptr m_optimizer_factory; std::optional m_integer_columns; @@ -32,7 +32,7 @@ class idol::Heuristics::IntegerMaster : public BranchAndBoundCallbackFactory { + class Strategy : public BranchAndBoundCallback { std::unique_ptr m_optimizer_factory; bool m_integer_columns = true; double m_time_limit = std::numeric_limits::max(); @@ -58,9 +58,9 @@ class idol::Heuristics::IntegerMaster : public BranchAndBoundCallbackFactory *operator()() override; + BranchAndBoundCallback *operator()() override; - [[nodiscard]] BranchAndBoundCallbackFactory *clone() const override; + [[nodiscard]] BranchAndBoundCallbackFactory *clone() const override; IntegerMaster& with_optimizer(const OptimizerFactory& t_optimizer); diff --git a/lib/src/optimizers/branch-and-bound/nodes/NodeVarInfo.cpp b/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp similarity index 59% rename from lib/src/optimizers/branch-and-bound/nodes/NodeVarInfo.cpp rename to lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp index 6e92369a2..be9bc7d23 100644 --- a/lib/src/optimizers/branch-and-bound/nodes/NodeVarInfo.cpp +++ b/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp @@ -1,22 +1,22 @@ // // Created by henri on 18.10.23. // -#include "idol/optimizers/branch-and-bound/nodes/NodeVarInfo.h" +#include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" #include "idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h" -void idol::NodeVarInfo::set_local_upper_bound(const idol::Var &t_var, double t_ub) { - m_branching_decision = std::make_optional(t_var, LessOrEqual, t_ub); +void idol::DefaultNodeInfo::set_local_upper_bound(const idol::Var &t_var, double t_ub) { + m_branching_decision.emplace_back(t_var, LessOrEqual, t_ub); } -void idol::NodeVarInfo::set_local_lower_bound(const idol::Var &t_var, double t_lb) { - m_branching_decision = std::make_optional(t_var, GreaterOrEqual, t_lb); +void idol::DefaultNodeInfo::set_local_lower_bound(const idol::Var &t_var, double t_lb) { + m_branching_decision.emplace_back(t_var, GreaterOrEqual, t_lb); } -idol::NodeVarUpdator * -idol::NodeVarInfo::create_updator(idol::Model &t_relaxation) { - return new NodeVarUpdator(t_relaxation); +idol::DefaultNodeUpdator * +idol::DefaultNodeInfo::create_updator(idol::Model &t_relaxation) { + return new DefaultNodeUpdator(t_relaxation); } -void idol::NodeVarInfo::save(const idol::Model &t_original_formulation, +void idol::DefaultNodeInfo::save(const idol::Model &t_original_formulation, const idol::Model &t_model) { const auto status = t_model.get_status(); @@ -54,6 +54,6 @@ void idol::NodeVarInfo::save(const idol::Model &t_original_formulation, } -idol::NodeVarInfo *idol::NodeVarInfo::create_child() const { - return new NodeVarInfo(); +idol::DefaultNodeInfo *idol::DefaultNodeInfo::create_child() const { + return new DefaultNodeInfo(); } diff --git a/lib/src/optimizers/branch-and-bound/watchers/BranchAndBoundTree.cpp b/lib/src/optimizers/branch-and-bound/watchers/BranchAndBoundTree.cpp index 6f5120ea3..8ec460376 100644 --- a/lib/src/optimizers/branch-and-bound/watchers/BranchAndBoundTree.cpp +++ b/lib/src/optimizers/branch-and-bound/watchers/BranchAndBoundTree.cpp @@ -8,11 +8,11 @@ idol::Utils::ExportBranchAndBoundTreeToCSV::ExportBranchAndBoundTreeToCSV(std::s } -idol::BranchAndBoundCallbackFactory *idol::Utils::ExportBranchAndBoundTreeToCSV::clone() const { +idol::BranchAndBoundCallbackFactory *idol::Utils::ExportBranchAndBoundTreeToCSV::clone() const { return new ExportBranchAndBoundTreeToCSV(*this); } -idol::BranchAndBoundCallback *idol::Utils::ExportBranchAndBoundTreeToCSV::operator()() { +idol::BranchAndBoundCallback *idol::Utils::ExportBranchAndBoundTreeToCSV::operator()() { return new Strategy(m_filename); } @@ -59,14 +59,15 @@ void idol::Utils::ExportBranchAndBoundTreeToCSV::Strategy::operator()(idol::Call } else { parent_id = node.parent().id(); - const auto& branching_decision = node.info().branching_decision(); - branch_label << branching_decision.variable.name() << " "; - if (branching_decision.type == LessOrEqual) { - branch_label << "≤"; - } else { - branch_label << "≥"; + for (const auto& branching_decision : node.info().variable_branching_decisions()) { + branch_label << branching_decision.variable.name() << " "; + if (branching_decision.type == LessOrEqual) { + branch_label << "≤"; + } else { + branch_label << "≥"; + } + branch_label << " " << branching_decision.bound; } - branch_label << " " << branching_decision.bound; } if (status == Optimal || status == Feasible) { diff --git a/lib/src/optimizers/callbacks/IntegerMaster.cpp b/lib/src/optimizers/callbacks/IntegerMaster.cpp index 1109a6003..82b9225a5 100644 --- a/lib/src/optimizers/callbacks/IntegerMaster.cpp +++ b/lib/src/optimizers/callbacks/IntegerMaster.cpp @@ -13,7 +13,7 @@ idol::Heuristics::IntegerMaster::IntegerMaster(const IntegerMaster& t_src) m_frequency(t_src.m_frequency) {} -idol::BranchAndBoundCallback *idol::Heuristics::IntegerMaster::operator()() { +idol::BranchAndBoundCallback *idol::Heuristics::IntegerMaster::operator()() { if (!m_optimizer_factory) { throw Exception("No solver was given to solve the integer master problem, please call IntegerMaster.rst::with_optimizer to configure."); @@ -55,7 +55,7 @@ idol::Heuristics::IntegerMaster &idol::Heuristics::IntegerMaster::with_optimizer return *this; } -idol::BranchAndBoundCallbackFactory *idol::Heuristics::IntegerMaster::clone() const { +idol::BranchAndBoundCallbackFactory *idol::Heuristics::IntegerMaster::clone() const { return new IntegerMaster(*this); } @@ -157,7 +157,7 @@ void idol::Heuristics::IntegerMaster::Strategy::operator()(CallbackEvent t_event } } - auto* info = new NodeVarInfo(); + auto* info = new DefaultNodeInfo(); info->set_primal_solution(std::move(solution)); submit_heuristic_solution(info); diff --git a/tests/branch-and-bound/knapsack.cpp b/tests/branch-and-bound/knapsack.cpp index 55cd53826..50b7014bc 100644 --- a/tests/branch-and-bound/knapsack.cpp +++ b/tests/branch-and-bound/knapsack.cpp @@ -32,11 +32,11 @@ using node_selection_rules = std::tuple< >; using branching_rules = std::tuple< - MostInfeasible::Strategy, - LeastInfeasible::Strategy, - UniformlyRandom::Strategy, - StrongBranching::Strategy, - PseudoCost::Strategy + MostInfeasible::Strategy, + LeastInfeasible::Strategy, + UniformlyRandom::Strategy, + StrongBranching::Strategy, + PseudoCost::Strategy >; using test_parameters = cartesian_product; @@ -70,7 +70,7 @@ TEMPLATE_LIST_TEST_CASE("Solve Knapsack Problem instances with different node se model.add(c); model.set_obj_expr(idol_Sum(j, Range(n_items), -instance.profit(j) * x[j])); - model.use(BranchAndBound() + model.use(BranchAndBound() .with_node_optimizer(OPTIMIZER::ContinuousRelaxation()) .with_branching_rule(BranchingRuleT()) .with_node_selection_rule(NodeSelectionRuleT()) diff --git a/tests/branch-and-bound/location.cpp b/tests/branch-and-bound/location.cpp index bd120430a..c6577f68d 100644 --- a/tests/branch-and-bound/location.cpp +++ b/tests/branch-and-bound/location.cpp @@ -32,11 +32,11 @@ using node_selection_rules = std::tuple< >; using branching_rules = std::tuple< - MostInfeasible::Strategy, - LeastInfeasible::Strategy, - UniformlyRandom::Strategy, - StrongBranching::Strategy, - PseudoCost::Strategy + MostInfeasible::Strategy, + LeastInfeasible::Strategy, + UniformlyRandom::Strategy, + StrongBranching::Strategy, + PseudoCost::Strategy >; using test_parameters = cartesian_product; @@ -92,7 +92,7 @@ TEMPLATE_LIST_TEST_CASE("Solve Facility Location Problem instances with differen // Set backend options model.use( - BranchAndBound() + BranchAndBound() .with_node_optimizer(OPTIMIZER::ContinuousRelaxation()) .with_branching_rule(BranchingRuleT()) .with_node_selection_rule(NodeSelectionRuleT()) diff --git a/tests/branch-and-price/assignment.cpp b/tests/branch-and-price/assignment.cpp index c731922f6..b6a1e38c9 100644 --- a/tests/branch-and-price/assignment.cpp +++ b/tests/branch-and-price/assignment.cpp @@ -109,7 +109,7 @@ TEST_CASE("Solve Generalized Assignment Problem instances with different branch- .with_default_sub_problem_spec( DantzigWolfe::SubProblem() .add_optimizer( - BranchAndBound() + BranchAndBound() .with_node_optimizer(OPTIMIZER::ContinuousRelaxation()) .with_branching_rule(MostInfeasible()) .with_node_selection_rule(BestBound()) @@ -129,14 +129,14 @@ TEST_CASE("Solve Generalized Assignment Problem instances with different branch- .with_default_sub_problem_spec( DantzigWolfe::SubProblem() .add_optimizer( - BranchAndBound() + BranchAndBound() .with_node_optimizer( DantzigWolfeDecomposition(nested_decomposition2) .with_master_optimizer(OPTIMIZER::ContinuousRelaxation()) .with_default_sub_problem_spec( DantzigWolfe::SubProblem() .add_optimizer( - BranchAndBound() + BranchAndBound() .with_node_optimizer( OPTIMIZER::ContinuousRelaxation()) .with_branching_rule(MostInfeasible()) @@ -170,7 +170,7 @@ TEST_CASE("Solve Generalized Assignment Problem instances with different branch- }; model.use( - BranchAndBound() + BranchAndBound() .with_node_optimizer(*relaxation_solvers[solver_index].second) .with_branching_rule(MostInfeasible()) .with_node_selection_rule(BestBound()) From 43bc2816f3c9ac95e28136de6736778b6cfed00b Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 10:12:48 +0100 Subject: [PATCH 02/12] remove unused file --- .../impls/VariableBranchingRule.h | 125 ------------------ 1 file changed, 125 deletions(-) delete mode 100644 lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranchingRule.h diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranchingRule.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranchingRule.h deleted file mode 100644 index 137a20348..000000000 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranchingRule.h +++ /dev/null @@ -1,125 +0,0 @@ -// -// Created by henri on 21/03/23. -// - -#ifndef IDOL_VARIABLEBRANCHINGRULE_H -#define IDOL_VARIABLEBRANCHINGRULE_H - -#include "BranchingRule.h" -#include "idol/optimizers/Logger.h" -#include "idol/modeling/variables/Var.h" - -#include -#include - -namespace idol { - template - class BranchAndBound; - - template - class VariableBranchingRule; -} -template -class idol::VariableBranchingRule : public BranchingRule { - std::optional> m_variable_selected_for_branching; -protected: - void reset_variable_selected_for_branching(); - - void set_variable_selected_for_branching(const Var& t_var, double t_score); - - [[nodiscard]] bool has_variable_selected_for_branching() const; - - [[nodiscard]] const std::pair& variable_selected_for_branching() const; - - template - void select_variable_for_branching(IteratorT t_begin, - IteratorT t_end, - const std::function& t_scoring_function - ); - - std::list create_child_nodes_by_bound( - const Node& t_node, - const Var& t_variable_selected_for_branching, - double t_value, - double t_lb, - double t_ub - ); -public: - explicit VariableBranchingRule(const Model& t_model) : BranchingRule(t_model) {} -}; - -template -std::list idol::VariableBranchingRule::create_child_nodes_by_bound(const Node &t_node, - const Var &t_variable_selected_for_branching, - double t_value, - double t_lb, - double t_ub) { - auto* n1 = t_node.info().create_child(); - n1->set_local_lower_bound(t_variable_selected_for_branching, t_lb); - - auto* n2 = t_node.info().create_child(); - n2->set_local_upper_bound(t_variable_selected_for_branching, t_ub); - - idol_Log(Trace, - "Node " << t_node.id() << " has 2 child nodes with " - << t_variable_selected_for_branching << " >= " << t_lb - << " and " - << t_variable_selected_for_branching << " <= " << t_ub - << " (current value of " << t_variable_selected_for_branching << " is " << t_value << ")."; - ); - - return { n1, n2 }; -} - -template -template -void idol::VariableBranchingRule::select_variable_for_branching(IteratorT t_begin, IteratorT t_end, - const std::function &t_scoring_function) { - - IteratorT selected_variable = t_end; - - double maximum_score = -Inf; - - for ( ; t_begin != t_end ; ++t_begin ) { - - // std::cout << *t_begin << " -> " << t_scoring_function(*t_begin) << std::endl; - - if (double variable_score = t_scoring_function(*t_begin) ; variable_score > maximum_score) { - - maximum_score = variable_score; - selected_variable = t_begin; - - } - - } - - if (selected_variable != t_end) { - //std::cout << "selected " << *selected_variable << std::endl; - set_variable_selected_for_branching(*selected_variable, maximum_score); - } - - -} - -template -const std::pair &idol::VariableBranchingRule::variable_selected_for_branching() const { - return m_variable_selected_for_branching.value(); -} - -template -bool idol::VariableBranchingRule::has_variable_selected_for_branching() const { - return m_variable_selected_for_branching.has_value(); -} - -template -void idol::VariableBranchingRule::set_variable_selected_for_branching(const Var &t_var, double t_score) { - m_variable_selected_for_branching = { t_var, t_score }; -} - -template -void idol::VariableBranchingRule::reset_variable_selected_for_branching() { - m_variable_selected_for_branching = {}; -} - -#endif //IDOL_VARIABLEBRANCHINGRULE_H From d01af8e192427c4e0e38c827e196f5cf1ad58612 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 10:13:34 +0100 Subject: [PATCH 03/12] add branching on constraints to DefaultNodeInfo, DefaultNodeInfo replaces VarInfoNode --- lib/CMakeLists.txt | 1 - .../Optimizers_BranchAndBound.h | 4 +- .../branching-rules/impls/VariableBranching.h | 4 +- .../branch-and-bound/nodes/DefaultNodeInfo.h | 17 +++-- .../nodes/DefaultNodeUpdator.h | 72 +++++++++++++------ .../branch-and-bound/nodes/NodeUpdator.h | 4 +- .../nodes/DefaultNodeInfo.cpp | 15 ++-- 7 files changed, 77 insertions(+), 40 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 93a447a3a..d08f37d33 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -97,7 +97,6 @@ add_library(idol STATIC include/idol/optimizers/branch-and-bound/nodes/Node.h include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h include/idol/optimizers/branch-and-bound/node-selection-rules/impls/DepthFirst.h - include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranchingRule.h include/idol/optimizers/branch-and-bound/node-selection-rules/factories/BreadthFirst.h include/idol/optimizers/branch-and-bound/node-selection-rules/factories/WorstBound.h include/idol/optimizers/branch-and-bound/node-selection-rules/factories/BestBound.h diff --git a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h index 13be29aba..ad60b6fea 100644 --- a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h +++ b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h @@ -381,7 +381,7 @@ void idol::Optimizers::BranchAndBound::hook_optimize() { set_best_bound(get_best_obj()); } - m_node_updator->clear_local_updates(); + m_node_updator->clear(); if (get_status() == Fail) { return; @@ -475,7 +475,7 @@ void idol::Optimizers::BranchAndBound::solve(TreeNode& t_node) con idol_Log(Trace, "Preparing to solve node " << t_node.id() << "."); - m_node_updator->apply_local_updates(t_node); + m_node_updator->prepare(t_node); m_relaxation->optimizer().set_param_best_bound_stop(std::min(get_best_obj(), get_param_best_bound_stop())); m_relaxation->optimizer().set_param_time_limit(get_remaining_time()); diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h index ddb670da9..3bf96bd09 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h @@ -44,10 +44,10 @@ class idol::BranchingRules::VariableBranching : public BranchingRuleset_local_lower_bound(t_var, lb); + n1->add_branching_variable(t_var, GreaterOrEqual, lb); auto* n2 = t_node.info().create_child(); - n2->set_local_upper_bound(t_var, ub); + n2->add_branching_variable(t_var, LessOrEqual, ub); idol_Log(Trace, "Node " << t_node.id() << " has 2 child nodes with " diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h index bc7d4a913..3c4150fe8 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h @@ -26,6 +26,12 @@ class idol::DefaultNodeInfo { VarBranchingDecision(Var t_variable, CtrType t_type, double t_bound) : variable(std::move(t_variable)), type(t_type), bound(t_bound) {} }; + struct CtrBranchingDecision { + Ctr constraint; + TempCtr temporary_constraint; + CtrBranchingDecision(const Ctr& t_constraint, TempCtr&& t_temporary_constraint) : constraint(t_constraint), temporary_constraint(t_temporary_constraint) {} + }; + DefaultNodeInfo() = default; virtual ~DefaultNodeInfo() = default; @@ -48,17 +54,20 @@ class idol::DefaultNodeInfo { [[nodiscard]] virtual DefaultNodeInfo* create_child() const; - void set_local_lower_bound(const Var& t_var, double t_lb); + void add_branching_variable(const Var& t_var, CtrType t_type, double t_bound); + + void add_branching_constraint(const Ctr &t_ctr, TempCtr t_temporary_constraint); - void set_local_upper_bound(const Var& t_var, double t_ub); + [[nodiscard]] auto variable_branching_decisions() const { return ConstIteratorForward(m_variable_branching_decisions); } - [[nodiscard]] auto variable_branching_decisions() const { return ConstIteratorForward(m_branching_decision); } + [[nodiscard]] auto constraint_branching_decisions() const { return ConstIteratorForward(m_constraint_branching_decisions); } static DefaultNodeUpdator* create_updator(Model& t_relaxation); private: Solution::Primal m_primal_solution; std::optional m_sum_of_infeasibilities; - std::list m_branching_decision; + std::list m_variable_branching_decisions; + std::list m_constraint_branching_decisions; }; #endif //IDOL_DEFAULTNODEINFO_H diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h index ada9e6bb4..e9b547f32 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h @@ -21,23 +21,26 @@ class idol::DefaultNodeUpdator : public NodeUpdator { Map m_changed_lower_bounds; // variable -> old_bound Map m_changed_upper_bounds; + std::list m_added_constraints; - void apply_local_updates(const Node& t_node, - Map &t_changed_lower_bounds, - Map &t_changed_upper_bounds); + void apply_variable_branching_decisions(const Node &t_node, + Map &t_changed_lower_bounds, + Map &t_changed_upper_bounds); - void apply_local_update(const typename NodeVarInfoT::VarBranchingDecision& t_branching_decision, - Map &t_current_changed_bounds, - Map &t_changed_bounds); + void apply_variable_branching_decision(const typename NodeVarInfoT::VarBranchingDecision& t_branching_decision, + Map &t_current_changed_bounds, + Map &t_changed_bounds); + + void apply_constraint_branching_decisions(const Node &t_node); public: explicit DefaultNodeUpdator(Model& t_relaxation); DefaultNodeUpdator(const DefaultNodeUpdator&) = delete; - void apply_local_updates(const Node &t_node) override; + void prepare(const Node &t_node) override; - void clear_local_updates() override; + void clear() override; protected: Model& relaxation() { return m_relaxation; } @@ -45,44 +48,69 @@ class idol::DefaultNodeUpdator : public NodeUpdator { const Model& relaxation() const { return m_relaxation; } }; +template +void +idol::DefaultNodeUpdator::apply_constraint_branching_decisions(const idol::Node &t_node) { + + if (t_node.level() == 0) { + return; + } + + apply_constraint_branching_decisions(t_node.parent()); + + for (const auto& [ctr, temporary_constraint] : t_node.info().constraint_branching_decisions()) { + m_relaxation.add(ctr, temporary_constraint); + m_added_constraints.emplace_back(ctr); + } + +} + template idol::DefaultNodeUpdator::DefaultNodeUpdator(idol::Model &t_relaxation) : m_relaxation(t_relaxation) { } template -void idol::DefaultNodeUpdator::clear_local_updates() { +void idol::DefaultNodeUpdator::clear() { + // Clear LB for (const auto& [var, lb] : m_changed_lower_bounds) { m_relaxation.set_var_lb(var, lb); } m_changed_lower_bounds.clear(); + // Clear UB for (const auto& [var, ub] : m_changed_upper_bounds) { m_relaxation.set_var_ub(var, ub); } m_changed_upper_bounds.clear(); + // Clear Ctr + for (const auto& ctr : m_added_constraints) { + m_relaxation.remove(ctr); + } + m_added_constraints.clear(); } template -void idol::DefaultNodeUpdator::apply_local_updates(const Node &t_node) { +void idol::DefaultNodeUpdator::prepare(const Node &t_node) { Map changed_lower_bounds; Map changed_upper_bounds; - apply_local_updates(t_node, changed_lower_bounds, changed_upper_bounds); - clear_local_updates(); + apply_variable_branching_decisions(t_node, changed_lower_bounds, changed_upper_bounds); + clear(); m_changed_lower_bounds = changed_lower_bounds; m_changed_upper_bounds = changed_upper_bounds; + apply_constraint_branching_decisions(t_node); } template -void idol::DefaultNodeUpdator::apply_local_updates(const idol::Node &t_node, - Map &t_changed_lower_bounds, - Map &t_changed_upper_bounds - ) { +void idol::DefaultNodeUpdator::apply_variable_branching_decisions(const idol::Node &t_node, + Map &t_changed_lower_bounds, + Map &t_changed_upper_bounds + ) { if (t_node.level() == 0) { return; @@ -92,10 +120,10 @@ void idol::DefaultNodeUpdator::apply_local_updates(const idol::Nod switch (branching_decision.type) { case LessOrEqual: - apply_local_update(branching_decision, m_changed_upper_bounds, t_changed_upper_bounds); + apply_variable_branching_decision(branching_decision, m_changed_upper_bounds, t_changed_upper_bounds); break; case GreaterOrEqual: - apply_local_update(branching_decision, m_changed_lower_bounds, t_changed_lower_bounds); + apply_variable_branching_decision(branching_decision, m_changed_lower_bounds, t_changed_lower_bounds); break; default: throw Exception("Branching on equality constraints is not implemented."); @@ -103,14 +131,14 @@ void idol::DefaultNodeUpdator::apply_local_updates(const idol::Nod } - apply_local_updates(t_node.parent(), t_changed_lower_bounds, t_changed_upper_bounds); + apply_variable_branching_decisions(t_node.parent(), t_changed_lower_bounds, t_changed_upper_bounds); } template -void idol::DefaultNodeUpdator::apply_local_update(const typename NodeVarInfoT::VarBranchingDecision &t_branching_decision, - idol::Map &t_current_changed_bounds, - idol::Map &t_changed_bounds) { +void idol::DefaultNodeUpdator::apply_variable_branching_decision(const typename NodeVarInfoT::VarBranchingDecision &t_branching_decision, + idol::Map &t_current_changed_bounds, + idol::Map &t_changed_bounds) { const auto& var = t_branching_decision.variable; diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h b/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h index efdbf82a9..e1d046136 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h @@ -20,9 +20,9 @@ class idol::NodeUpdator { public: virtual ~NodeUpdator() = default; - virtual void apply_local_updates(const Node& t_node) = 0; + virtual void prepare(const Node& t_node) = 0; - virtual void clear_local_updates() = 0; + virtual void clear() = 0; }; #endif //IDOL_NODEUPDATOR_H diff --git a/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp b/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp index be9bc7d23..bbed4d549 100644 --- a/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp +++ b/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp @@ -4,18 +4,19 @@ #include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" #include "idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h" -void idol::DefaultNodeInfo::set_local_upper_bound(const idol::Var &t_var, double t_ub) { - m_branching_decision.emplace_back(t_var, LessOrEqual, t_ub); -} - -void idol::DefaultNodeInfo::set_local_lower_bound(const idol::Var &t_var, double t_lb) { - m_branching_decision.emplace_back(t_var, GreaterOrEqual, t_lb); -} idol::DefaultNodeUpdator * idol::DefaultNodeInfo::create_updator(idol::Model &t_relaxation) { return new DefaultNodeUpdator(t_relaxation); } +void idol::DefaultNodeInfo::add_branching_constraint(const Ctr &t_ctr, TempCtr t_temporary_constraint) { + m_constraint_branching_decisions.emplace_back(t_ctr, std::move(t_temporary_constraint)); +} + +void idol::DefaultNodeInfo::add_branching_variable(const idol::Var &t_var, idol::CtrType t_type, double t_bound) { + m_variable_branching_decisions.emplace_back(t_var, t_type, t_bound); +} + void idol::DefaultNodeInfo::save(const idol::Model &t_original_formulation, const idol::Model &t_model) { From 8cb428ff7923d0761b2793916e6f976f80bd0253 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 10:18:12 +0100 Subject: [PATCH 04/12] fixes PseudoCost branching to account for multiple branching decisions --- .../branching-rules/impls/PseudoCost.h | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h index aba851a7f..596a40dd7 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h @@ -67,35 +67,40 @@ void idol::BranchingRules::PseudoCost::on_node_solved(const idol:: return; } - // Current node - const auto& branching_decision = t_node.info().branching_decision(); - const auto& var = branching_decision.variable; - const double bound = branching_decision.bound; - const bool is_upper_bound = branching_decision.type == LessOrEqual; - const double node_objective_value = t_node.info().objective_value(); - // Parent const auto& parent = t_node.parent(); const auto& parent_solution = parent.info().primal_solution(); const double parent_objective_value = parent_solution.objective_value(); - const double parent_var_primal_value = parent_solution.get(var); - const double objective_gain_per_unit_change = (node_objective_value - parent_objective_value) / std::abs(bound - parent_var_primal_value); + // Current node + for (const auto &branching_decision : t_node.info().variable_branching_decisions()) { - PseudoCostData data; + const auto &var = branching_decision.variable; + const double bound = branching_decision.bound; + const bool is_upper_bound = branching_decision.type == LessOrEqual; + const double node_objective_value = t_node.info().objective_value(); - if (is_upper_bound) { - data.n_entries_for_upper_bounds = 1; - data.objective_gains_by_upper_boundings = objective_gain_per_unit_change; - } else { - data.n_entries_for_lower_bounds = 1; - data.objective_gains_by_lower_boundings = objective_gain_per_unit_change; - } + const double parent_var_primal_value = parent_solution.get(var); + + const double objective_gain_per_unit_change = + (node_objective_value - parent_objective_value) / std::abs(bound - parent_var_primal_value); - auto [it, success] = m_pseudo_cost_data.emplace(var, data); + PseudoCostData data; + + if (is_upper_bound) { + data.n_entries_for_upper_bounds = 1; + data.objective_gains_by_upper_boundings = objective_gain_per_unit_change; + } else { + data.n_entries_for_lower_bounds = 1; + data.objective_gains_by_lower_boundings = objective_gain_per_unit_change; + } + + auto [it, success] = m_pseudo_cost_data.emplace(var, data); + + if (!success) { + it->second += data; + } - if (!success) { - it->second += data; } } From e0e09dc288278555bb242ce915d8d432099be77e Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 10:52:47 +0100 Subject: [PATCH 05/12] move away VarBranchingDecision and CtrBranchingDecision --- lib/CMakeLists.txt | 1 + .../Optimizers_BranchAndBound.h | 204 +++++++++--------- .../branching-rules/factories/Diver.h | 14 +- .../factories/FirstInfeasibleFound.h | 14 +- .../factories/LeastInfeasible.h | 14 +- .../factories/MostInfeasible.h | 14 +- .../branching-rules/factories/PseudoCost.h | 14 +- .../factories/StrongBranching.h | 28 +-- .../factories/UniformlyRandom.h | 16 +- .../branching-rules/impls/BranchingRule.h | 20 +- .../branching-rules/impls/Diver.h | 10 +- .../impls/FirstInfeasibleFound.h | 24 +-- .../branching-rules/impls/LeastInfeasible.h | 24 +-- .../branching-rules/impls/MostInfeasible.h | 24 +-- .../branching-rules/impls/PseudoCost.h | 72 +++---- .../branching-rules/impls/StrongBranching.h | 64 +++--- .../branching-rules/impls/UniformlyRandom.h | 24 +-- .../branching-rules/impls/VariableBranching.h | 17 +- .../AbstractBranchAndBoundCallbackI.h | 16 +- .../callbacks/BranchAndBoundCallback.h | 136 ++++++------ .../callbacks/BranchAndBoundCallbackFactory.h | 10 +- .../CallbackAsBranchAndBoundCallback.h | 18 +- .../nodes/BranchingDecision.h | 31 +++ .../branch-and-bound/nodes/DefaultNodeInfo.h | 14 +- .../nodes/DefaultNodeUpdator.h | 34 +-- .../optimizers/branch-and-bound/nodes/Node.h | 28 +-- .../nodes/DefaultNodeInfo.cpp | 1 + 27 files changed, 454 insertions(+), 432 deletions(-) create mode 100644 lib/include/idol/optimizers/branch-and-bound/nodes/BranchingDecision.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d08f37d33..fe0eb899c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -196,6 +196,7 @@ add_library(idol STATIC include/idol/optimizers/branch-and-bound/branching-rules/factories/BracnhingWithPriority.h src/optimizers/logs.cpp include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingWithPriority.h + include/idol/optimizers/branch-and-bound/nodes/BranchingDecision.h ) find_package(OpenMP REQUIRED) diff --git a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h index ad60b6fea..f15eb9120 100644 --- a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h +++ b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h @@ -22,7 +22,7 @@ #include namespace idol::Optimizers { - template class BranchAndBound; + template class BranchAndBound; } template @@ -33,7 +33,7 @@ class idol::Optimizers::BranchAndBound : public Algorithm { std::unique_ptr m_relaxation_optimizer_factory; std::unique_ptr m_relaxation; - std::unique_ptr<::idol::NodeUpdator> m_node_updator; + std::unique_ptr> m_node_updator; std::unique_ptr> m_branching_rule; std::unique_ptr> m_node_selection_rule; @@ -143,109 +143,109 @@ class idol::Optimizers::BranchAndBound : public Algorithm { const TreeNode& incumbent() const { return m_incumbent.value(); } }; -template -void idol::Optimizers::BranchAndBound::add_cutting_plane_generator(const CuttingPlaneGenerator &t_cutting_plane_generator) { +template +void idol::Optimizers::BranchAndBound::add_cutting_plane_generator(const CuttingPlaneGenerator &t_cutting_plane_generator) { m_callback->add_cutting_plane_generator(t_cutting_plane_generator); } -template -void idol::Optimizers::BranchAndBound::set_solution_index(unsigned int t_index) { +template +void idol::Optimizers::BranchAndBound::set_solution_index(unsigned int t_index) { if (t_index != 0) { throw Exception("Solution index out of bounds."); } } -template -unsigned int idol::Optimizers::BranchAndBound::get_solution_index() const { +template +unsigned int idol::Optimizers::BranchAndBound::get_solution_index() const { return 0; } -template -unsigned int idol::Optimizers::BranchAndBound::get_n_solutions() const { +template +unsigned int idol::Optimizers::BranchAndBound::get_n_solutions() const { return !!m_incumbent; } -template -void idol::Optimizers::BranchAndBound::update_obj() { +template +void idol::Optimizers::BranchAndBound::update_obj() { m_relaxation->set_obj_expr(parent().get_obj_expr()); } -template -void idol::Optimizers::BranchAndBound::update_rhs() { +template +void idol::Optimizers::BranchAndBound::update_rhs() { m_relaxation->set_rhs_expr(parent().get_rhs_expr()); } -template -void idol::Optimizers::BranchAndBound::update_obj_constant() { +template +void idol::Optimizers::BranchAndBound::update_obj_constant() { m_relaxation->set_obj_const(parent().get_obj_expr().constant()); } -template -void idol::Optimizers::BranchAndBound::update_mat_coeff(const Ctr &t_ctr, const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::update_mat_coeff(const Ctr &t_ctr, const Var &t_var) { m_relaxation->set_mat_coeff(t_ctr, t_var, parent().get_mat_coeff(t_ctr, t_var)); } -template -void idol::Optimizers::BranchAndBound::update_ctr_type(const Ctr &t_ctr) { +template +void idol::Optimizers::BranchAndBound::update_ctr_type(const Ctr &t_ctr) { m_relaxation->set_ctr_type(t_ctr, parent().get_ctr_type(t_ctr)); } -template -void idol::Optimizers::BranchAndBound::update_ctr_rhs(const Ctr &t_ctr) { +template +void idol::Optimizers::BranchAndBound::update_ctr_rhs(const Ctr &t_ctr) { m_relaxation->set_ctr_rhs(t_ctr, parent().get_ctr_row(t_ctr).rhs()); } -template -void idol::Optimizers::BranchAndBound::update_var_type(const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::update_var_type(const Var &t_var) { m_relaxation->set_var_type(t_var, parent().get_var_type(t_var)); } -template -void idol::Optimizers::BranchAndBound::update_var_lb(const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::update_var_lb(const Var &t_var) { m_relaxation->set_var_lb(t_var, parent().get_var_lb(t_var)); } -template -void idol::Optimizers::BranchAndBound::update_var_ub(const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::update_var_ub(const Var &t_var) { m_relaxation->set_var_ub(t_var, parent().get_var_ub(t_var)); } -template -void idol::Optimizers::BranchAndBound::update_var_obj(const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::update_var_obj(const Var &t_var) { m_relaxation->set_var_obj(t_var, parent().get_var_column(t_var).obj()); } -template -void idol::Optimizers::BranchAndBound::update_obj_sense() { +template +void idol::Optimizers::BranchAndBound::update_obj_sense() { throw Exception("Not implemented"); } -template -double idol::Optimizers::BranchAndBound::get_ctr_farkas(const Ctr &t_ctr) const { +template +double idol::Optimizers::BranchAndBound::get_ctr_farkas(const Ctr &t_ctr) const { if (m_n_solved_nodes > 1) { throw Exception("Accessing Farkas certificate for MIP is not available after the root node."); } return m_relaxation->get_ctr_farkas(t_ctr); } -template -double idol::Optimizers::BranchAndBound::get_ctr_dual(const Ctr &t_ctr) const { +template +double idol::Optimizers::BranchAndBound::get_ctr_dual(const Ctr &t_ctr) const { if (m_n_solved_nodes > 1) { throw Exception("Accessing dual values for MIP is not available after the root node."); } return m_relaxation->get_ctr_dual(t_ctr); } -template -double idol::Optimizers::BranchAndBound::get_var_ray(const Var &t_var) const { +template +double idol::Optimizers::BranchAndBound::get_var_ray(const Var &t_var) const { if (m_n_solved_nodes > 1) { throw Exception("Ray not available."); } return m_relaxation->get_var_ray(t_var); } -template -double idol::Optimizers::BranchAndBound::get_var_primal(const Var &t_var) const { +template +double idol::Optimizers::BranchAndBound::get_var_primal(const Var &t_var) const { if (!m_incumbent.has_value()){ throw Exception("Trying to access primal values while no incumbent was found."); @@ -254,8 +254,8 @@ double idol::Optimizers::BranchAndBound::get_var_primal(const Var return m_incumbent->info().primal_solution().get(t_var); } -template -void idol::Optimizers::BranchAndBound::submit_lower_bound(double t_lower_bound) { +template +void idol::Optimizers::BranchAndBound::submit_lower_bound(double t_lower_bound) { if (t_lower_bound > get_best_obj()) { set_status(Fail); set_reason(NotSpecified); @@ -268,10 +268,10 @@ void idol::Optimizers::BranchAndBound::submit_lower_bound(double t } } -template -void idol::Optimizers::BranchAndBound::submit_heuristic_solution(NodeVarInfoT* t_info) { +template +void idol::Optimizers::BranchAndBound::submit_heuristic_solution(NodeInfoT* t_info) { - auto t_node = Node::create_detached_node(t_info); + auto t_node = Node::create_detached_node(t_info); if (t_node.info().objective_value() < get_best_obj()) { @@ -289,9 +289,9 @@ void idol::Optimizers::BranchAndBound::submit_heuristic_solution(N } -template +template idol::SideEffectRegistry -idol::Optimizers::BranchAndBound::call_callbacks(CallbackEvent t_event, const Optimizers::BranchAndBound::TreeNode &t_node) { +idol::Optimizers::BranchAndBound::call_callbacks(CallbackEvent t_event, const Optimizers::BranchAndBound::TreeNode &t_node) { if (!m_callback) { return {}; @@ -300,23 +300,23 @@ idol::Optimizers::BranchAndBound::call_callbacks(CallbackEvent t_e return m_callback->operator()(this, t_event, t_node, m_relaxation.get()); } -template -void idol::Optimizers::BranchAndBound::add_callback(BranchAndBoundCallback *t_cb) { +template +void idol::Optimizers::BranchAndBound::add_callback(BranchAndBoundCallback *t_cb) { m_callback->add_callback(t_cb); } -template -void idol::Optimizers::BranchAndBound::build() { +template +void idol::Optimizers::BranchAndBound::build() { m_branching_rule->m_log_level = log_level(); m_branching_rule->m_log_color = log_color(); } -template -idol::Optimizers::BranchAndBound::BranchAndBound(const Model &t_model, +template +idol::Optimizers::BranchAndBound::BranchAndBound(const Model &t_model, const OptimizerFactory& t_node_optimizer, - const BranchingRuleFactory& t_branching_rule_factory, - const NodeSelectionRuleFactory& t_node_selection_rule_factory, - AbstractBranchAndBoundCallbackI* t_callback) + const BranchingRuleFactory& t_branching_rule_factory, + const NodeSelectionRuleFactory& t_node_selection_rule_factory, + AbstractBranchAndBoundCallbackI* t_callback) : Algorithm(t_model), m_relaxation_optimizer_factory(t_node_optimizer.clone()), m_branching_rule(t_branching_rule_factory(*this)), @@ -327,30 +327,30 @@ idol::Optimizers::BranchAndBound::BranchAndBound(const Model &t_mo } -template -void idol::Optimizers::BranchAndBound::create_relaxations() { +template +void idol::Optimizers::BranchAndBound::create_relaxations() { const auto &original_model = parent(); m_relaxation.reset(original_model.clone()); m_relaxation->use(*m_relaxation_optimizer_factory); - m_node_updator.reset(NodeVarInfoT::create_updator(*m_relaxation)); + m_node_updator.reset(dynamic_cast*>(NodeInfoT::create_updator(*m_relaxation))); } -template -idol::Node idol::Optimizers::BranchAndBound::create_root_node() { +template +idol::Node idol::Optimizers::BranchAndBound::create_root_node() { - auto root_node = Node::create_root_node(); + auto root_node = Node::create_root_node(); assert(root_node.id() == 0); ++m_n_created_nodes; return root_node; } -template -void idol::Optimizers::BranchAndBound::hook_before_optimize() { +template +void idol::Optimizers::BranchAndBound::hook_before_optimize() { Algorithm::hook_before_optimize(); // Reset solution @@ -368,8 +368,8 @@ void idol::Optimizers::BranchAndBound::hook_before_optimize() { m_callback->initialize(parent()); } -template -void idol::Optimizers::BranchAndBound::hook_optimize() { +template +void idol::Optimizers::BranchAndBound::hook_optimize() { auto root_node = create_root_node(); @@ -411,8 +411,8 @@ void idol::Optimizers::BranchAndBound::hook_optimize() { } -template -void idol::Optimizers::BranchAndBound::explore(TreeNode &t_node, +template +void idol::Optimizers::BranchAndBound::explore(TreeNode &t_node, SetOfActiveNodes & t_active_nodes, unsigned int t_step) { @@ -470,8 +470,8 @@ void idol::Optimizers::BranchAndBound::explore(TreeNode &t_node, } -template -void idol::Optimizers::BranchAndBound::solve(TreeNode& t_node) const { +template +void idol::Optimizers::BranchAndBound::solve(TreeNode& t_node) const { idol_Log(Trace, "Preparing to solve node " << t_node.id() << "."); @@ -492,8 +492,8 @@ void idol::Optimizers::BranchAndBound::solve(TreeNode& t_node) con } -template -void idol::Optimizers::BranchAndBound::analyze(const BranchAndBound::TreeNode &t_node, bool* t_explore_children_flag, bool* t_reoptimize_flag) { +template +void idol::Optimizers::BranchAndBound::analyze(const BranchAndBound::TreeNode &t_node, bool* t_explore_children_flag, bool* t_reoptimize_flag) { *t_explore_children_flag = false; *t_reoptimize_flag = false; @@ -610,8 +610,8 @@ void idol::Optimizers::BranchAndBound::analyze(const BranchAndBoun } -template -void idol::Optimizers::BranchAndBound::log_node(LogLevel t_msg_level, const BranchAndBound::TreeNode &t_node) { +template +void idol::Optimizers::BranchAndBound::log_node(LogLevel t_msg_level, const BranchAndBound::TreeNode &t_node) { const double objective_value = t_node.info().has_objective_value() ? t_node.info().objective_value() : Inf; const unsigned int id = t_node.id(); @@ -639,15 +639,15 @@ void idol::Optimizers::BranchAndBound::log_node(LogLevel t_msg_lev } -template -void idol::Optimizers::BranchAndBound::set_as_incumbent(const BranchAndBound::TreeNode &t_node) { +template +void idol::Optimizers::BranchAndBound::set_as_incumbent(const BranchAndBound::TreeNode &t_node) { m_incumbent = t_node; set_best_obj(t_node.info().objective_value()); set_status(Feasible); } -template -void idol::Optimizers::BranchAndBound::update_lower_bound(const BranchAndBound::SetOfActiveNodes &t_active_nodes) { +template +void idol::Optimizers::BranchAndBound::update_lower_bound(const BranchAndBound::SetOfActiveNodes &t_active_nodes) { if (t_active_nodes.empty()) { return; } @@ -660,8 +660,8 @@ void idol::Optimizers::BranchAndBound::update_lower_bound(const Br } -template -void idol::Optimizers::BranchAndBound::prune_nodes_by_bound(BranchAndBound::SetOfActiveNodes& t_active_nodes) { +template +void idol::Optimizers::BranchAndBound::prune_nodes_by_bound(BranchAndBound::SetOfActiveNodes& t_active_nodes) { const double upper_bound = get_best_obj(); @@ -684,26 +684,26 @@ void idol::Optimizers::BranchAndBound::prune_nodes_by_bound(Branch } -template -idol::Node -idol::Optimizers::BranchAndBound::select_node_for_branching(BranchAndBound::SetOfActiveNodes &t_active_nodes) { +template +idol::Node +idol::Optimizers::BranchAndBound::select_node_for_branching(BranchAndBound::SetOfActiveNodes &t_active_nodes) { auto iterator = m_node_selection_rule->operator()(t_active_nodes); auto result = *iterator; t_active_nodes.erase(iterator); return result; } -template -void idol::Optimizers::BranchAndBound::write(const std::string &t_name) { +template +void idol::Optimizers::BranchAndBound::write(const std::string &t_name) { m_relaxation->write(t_name); } -template -std::vector> idol::Optimizers::BranchAndBound::create_child_nodes(const BranchAndBound::TreeNode &t_node) { +template +std::vector> idol::Optimizers::BranchAndBound::create_child_nodes(const BranchAndBound::TreeNode &t_node) { auto children_info = m_branching_rule->create_child_nodes(t_node); - std::vector> result; + std::vector> result; result.reserve(children_info.size()); for (auto* ptr_to_info : children_info) { @@ -715,42 +715,42 @@ std::vector> idol::Optimizers::BranchAndBound -bool idol::Optimizers::BranchAndBound::gap_is_closed() const { +template +bool idol::Optimizers::BranchAndBound::gap_is_closed() const { return get_relative_gap() <= get_tol_mip_relative_gap() || get_absolute_gap() <= get_tol_mip_absolute_gap(); } -template -void idol::Optimizers::BranchAndBound::backtrack(BranchAndBound::SetOfActiveNodes &t_actives_nodes, +template +void idol::Optimizers::BranchAndBound::backtrack(BranchAndBound::SetOfActiveNodes &t_actives_nodes, SetOfActiveNodes &t_sub_active_nodes) { t_actives_nodes.merge(std::move(t_sub_active_nodes)); } -template -void idol::Optimizers::BranchAndBound::update() { +template +void idol::Optimizers::BranchAndBound::update() { m_relaxation->update(); } -template -void idol::Optimizers::BranchAndBound::remove(const Ctr &t_ctr) { +template +void idol::Optimizers::BranchAndBound::remove(const Ctr &t_ctr) { m_relaxation->remove(t_ctr); } -template -void idol::Optimizers::BranchAndBound::remove(const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::remove(const Var &t_var) { m_relaxation->remove(t_var); } -template -void idol::Optimizers::BranchAndBound::add(const Ctr &t_ctr) { +template +void idol::Optimizers::BranchAndBound::add(const Ctr &t_ctr) { m_relaxation->add(t_ctr); } -template -void idol::Optimizers::BranchAndBound::add(const Var &t_var) { +template +void idol::Optimizers::BranchAndBound::add(const Var &t_var) { m_relaxation->add(t_var); } diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/Diver.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/Diver.h index fee6a80bc..2395a15a2 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/Diver.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/Diver.h @@ -20,19 +20,19 @@ class idol::Diver : public idol::VariableBranching { template Diver(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { public: Strategy() = default; - explicit Strategy(const Diver& t_parent) : VariableBranching::Strategy(t_parent) {} + explicit Strategy(const Diver& t_parent) : VariableBranching::Strategy(t_parent) {} - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { - return new BranchingRules::Diver(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { + return new BranchingRules::Diver(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/FirstInfeasibleFound.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/FirstInfeasibleFound.h index 5c9ecfc6e..50ff44cb3 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/FirstInfeasibleFound.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/FirstInfeasibleFound.h @@ -19,19 +19,19 @@ class idol::FirstInfeasibleFound : public idol::VariableBranching { template FirstInfeasibleFound(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { public: Strategy() = default; - explicit Strategy(const FirstInfeasibleFound& t_parent) : VariableBranching::Strategy(t_parent) {} + explicit Strategy(const FirstInfeasibleFound& t_parent) : VariableBranching::Strategy(t_parent) {} - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { - return new BranchingRules::FirstInfeasibleFound(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { + return new BranchingRules::FirstInfeasibleFound(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/LeastInfeasible.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/LeastInfeasible.h index 544b31e53..e44ca1710 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/LeastInfeasible.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/LeastInfeasible.h @@ -19,19 +19,19 @@ class idol::LeastInfeasible : public idol::VariableBranching { template LeastInfeasible(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { public: Strategy() = default; - explicit Strategy(const LeastInfeasible& t_parent) : VariableBranching::Strategy(t_parent) {} + explicit Strategy(const LeastInfeasible& t_parent) : VariableBranching::Strategy(t_parent) {} - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { - return new BranchingRules::LeastInfeasible(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { + return new BranchingRules::LeastInfeasible(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/MostInfeasible.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/MostInfeasible.h index f1b233461..4333d2fc4 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/MostInfeasible.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/MostInfeasible.h @@ -19,19 +19,19 @@ class idol::MostInfeasible : public idol::VariableBranching { template MostInfeasible(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { public: Strategy() = default; - explicit Strategy(const MostInfeasible& t_parent) : VariableBranching::Strategy(t_parent) {} + explicit Strategy(const MostInfeasible& t_parent) : VariableBranching::Strategy(t_parent) {} - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { - return new BranchingRules::MostInfeasible(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { + return new BranchingRules::MostInfeasible(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/PseudoCost.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/PseudoCost.h index 1c6a1aa6c..54f3f19d0 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/PseudoCost.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/PseudoCost.h @@ -19,19 +19,19 @@ class idol::PseudoCost : public idol::VariableBranching { template PseudoCost(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { public: Strategy() = default; - explicit Strategy(const PseudoCost& t_parent) : VariableBranching::Strategy(t_parent) {} + explicit Strategy(const PseudoCost& t_parent) : VariableBranching::Strategy(t_parent) {} - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { - return new BranchingRules::PseudoCost(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { + return new BranchingRules::PseudoCost(t_parent, idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent())); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/StrongBranching.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/StrongBranching.h index a05e9b3e7..f70e85b34 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/StrongBranching.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/StrongBranching.h @@ -21,8 +21,8 @@ class idol::StrongBranching : public idol::VariableBranching { template StrongBranching(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { std::optional m_max_n_variables; std::unique_ptr m_node_scoring_function; std::list m_phases; @@ -30,22 +30,22 @@ class idol::StrongBranching : public idol::VariableBranching { public: Strategy() = default; - Strategy(const Strategy& t_src); + Strategy(const Strategy& t_src); explicit Strategy(const StrongBranching& t_parent); - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { - return new BranchingRules::StrongBranching( + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { + return new BranchingRules::StrongBranching( t_parent, - idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent()), + idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent()), m_max_n_variables.has_value() ? m_max_n_variables.value() : 100, m_node_scoring_function ? m_node_scoring_function->clone() : new NodeScoreFunctions::Product(), m_phases ); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; @@ -61,18 +61,18 @@ class idol::StrongBranching : public idol::VariableBranching { std::list m_phases; }; -template -idol::StrongBranching::Strategy::Strategy(const idol::StrongBranching &t_parent) - : VariableBranching::Strategy(t_parent), +template +idol::StrongBranching::Strategy::Strategy(const idol::StrongBranching &t_parent) + : VariableBranching::Strategy(t_parent), m_max_n_variables(t_parent.m_max_n_variables), m_node_scoring_function(t_parent.m_node_scoring_function ? t_parent.m_node_scoring_function->clone() : nullptr), m_phases(t_parent.m_phases) { } -template -idol::StrongBranching::Strategy::Strategy(const Strategy& t_src) - : VariableBranching::Strategy(t_src), +template +idol::StrongBranching::Strategy::Strategy(const Strategy& t_src) + : VariableBranching::Strategy(t_src), m_max_n_variables(t_src.m_max_n_variables), m_node_scoring_function(t_src.m_node_scoring_function ? t_src.m_node_scoring_function->clone() : nullptr), m_phases(t_src.m_phases) diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/UniformlyRandom.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/UniformlyRandom.h index 79d527c60..d2c1d01b5 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/UniformlyRandom.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/factories/UniformlyRandom.h @@ -20,27 +20,27 @@ class idol::UniformlyRandom : public idol::VariableBranching { template UniformlyRandom(IteratorT t_begin, IteratorT t_end) : idol::VariableBranching(t_begin, t_end) {} - template - class Strategy : public VariableBranching::Strategy { + template + class Strategy : public VariableBranching::Strategy { std::optional m_seed; public: Strategy() = default; explicit Strategy(const UniformlyRandom& t_parent) - : VariableBranching::Strategy(t_parent), + : VariableBranching::Strategy(t_parent), m_seed(t_parent.m_seed) {} - BranchingRules::VariableBranching * - operator()(const Optimizers::BranchAndBound &t_parent) const override { + BranchingRules::VariableBranching * + operator()(const Optimizers::BranchAndBound &t_parent) const override { unsigned int seed = m_seed.has_value() ? m_seed.value() : (std::random_device())(); - return new BranchingRules::UniformlyRandom(t_parent, - idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent()), + return new BranchingRules::UniformlyRandom(t_parent, + idol::VariableBranching::Strategy::create_branching_candidates(t_parent.parent()), seed); } - VariableBranching::Strategy *clone() const override { + VariableBranching::Strategy *clone() const override { return new Strategy(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h index 1834583b4..0b1439f28 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h @@ -11,18 +11,18 @@ namespace idol { namespace Optimizers { - template + template class BranchAndBound; } - template + template class BranchingRule; } -template +template class idol::BranchingRule { - friend class Optimizers::BranchAndBound; - const Optimizers::BranchAndBound& m_parent; + friend class Optimizers::BranchAndBound; + const Optimizers::BranchAndBound& m_parent; LogLevel m_log_level = Warn; Color m_log_color = Default; protected: @@ -30,18 +30,18 @@ class idol::BranchingRule { [[nodiscard]] Color log_color() const { return m_log_color; } [[nodiscard]] std::string name() const { return "branching-rule"; } public: - explicit BranchingRule(const Optimizers::BranchAndBound& t_parent) : m_parent(t_parent) {} + explicit BranchingRule(const Optimizers::BranchAndBound& t_parent) : m_parent(t_parent) {} virtual ~BranchingRule() = default; - [[nodiscard]] const Optimizers::BranchAndBound& parent() const { return m_parent; } + [[nodiscard]] const Optimizers::BranchAndBound& parent() const { return m_parent; } [[nodiscard]] const Model& model() const { return m_parent.parent(); } - [[nodiscard]] virtual bool is_valid(const Node& t_node) const = 0; + [[nodiscard]] virtual bool is_valid(const Node& t_node) const = 0; - [[nodiscard]] virtual std::list create_child_nodes(const Node& t_node) = 0; + [[nodiscard]] virtual std::list create_child_nodes(const Node& t_node) = 0; - virtual void on_node_solved(const Node& t_node) {} + virtual void on_node_solved(const Node& t_node) {} virtual void on_nodes_have_been_created() {} diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/Diver.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/Diver.h index cb6e81bd7..2876f21b3 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/Diver.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/Diver.h @@ -15,21 +15,21 @@ namespace idol::BranchingRules { template class idol::BranchingRules::Diver : public BranchingRuleT { public: - using NodeVarInfoT = typename BranchingRuleT::NodeInfoT; + using NodeInfoT = typename BranchingRuleT::NodeInfoT; - explicit Diver(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); + explicit Diver(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); - std::list create_child_nodes(const Node &t_node) override; + std::list create_child_nodes(const Node &t_node) override; }; template -idol::BranchingRules::Diver::Diver(const idol::Optimizers::BranchAndBound &t_parent, +idol::BranchingRules::Diver::Diver(const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) : BranchingRuleT(t_parent, std::move(t_branching_candidates)) {} template std::list -idol::BranchingRules::Diver::create_child_nodes(const idol::Node &t_node) { +idol::BranchingRules::Diver::create_child_nodes(const idol::Node &t_node) { auto children = BranchingRuleT::create_child_nodes(t_node); diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/FirstInfeasibleFound.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/FirstInfeasibleFound.h index e4a57b9d7..166a5c3c3 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/FirstInfeasibleFound.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/FirstInfeasibleFound.h @@ -8,26 +8,26 @@ #include "VariableBranching.h" namespace idol::BranchingRules { - template class FirstInfeasibleFound; + template class FirstInfeasibleFound; } -template -class idol::BranchingRules::FirstInfeasibleFound : public VariableBranching { +template +class idol::BranchingRules::FirstInfeasibleFound : public VariableBranching { protected: - std::list> scoring_function(const std::list &t_var, const Node &t_node) override; + std::list> scoring_function(const std::list &t_var, const Node &t_node) override; public: - explicit FirstInfeasibleFound(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); + explicit FirstInfeasibleFound(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); }; -template -idol::BranchingRules::FirstInfeasibleFound::FirstInfeasibleFound( - const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) - : VariableBranching(t_parent, std::move(t_branching_candidates)) {} +template +idol::BranchingRules::FirstInfeasibleFound::FirstInfeasibleFound( + const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) + : VariableBranching(t_parent, std::move(t_branching_candidates)) {} -template +template std::list> -idol::BranchingRules::FirstInfeasibleFound::scoring_function(const std::list &t_variables, - const Node &t_node) { +idol::BranchingRules::FirstInfeasibleFound::scoring_function(const std::list &t_variables, + const Node &t_node) { return { { t_variables.front(), -1. } }; } diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/LeastInfeasible.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/LeastInfeasible.h index ce0a75087..be6922239 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/LeastInfeasible.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/LeastInfeasible.h @@ -9,26 +9,26 @@ #include "VariableBranching.h" namespace idol::BranchingRules { - template class LeastInfeasible; + template class LeastInfeasible; } -template -class idol::BranchingRules::LeastInfeasible : public VariableBranching { +template +class idol::BranchingRules::LeastInfeasible : public VariableBranching { protected: - std::list> scoring_function(const std::list &t_var, const Node &t_node) override; + std::list> scoring_function(const std::list &t_var, const Node &t_node) override; public: - explicit LeastInfeasible(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); + explicit LeastInfeasible(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); }; -template -idol::BranchingRules::LeastInfeasible::LeastInfeasible( - const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) - : VariableBranching(t_parent, std::move(t_branching_candidates)) {} +template +idol::BranchingRules::LeastInfeasible::LeastInfeasible( + const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) + : VariableBranching(t_parent, std::move(t_branching_candidates)) {} -template +template std::list> -idol::BranchingRules::LeastInfeasible::scoring_function(const std::list &t_variables, - const Node &t_node) { +idol::BranchingRules::LeastInfeasible::scoring_function(const std::list &t_variables, + const Node &t_node) { const auto& primal_solution = t_node.info().primal_solution(); diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/MostInfeasible.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/MostInfeasible.h index c7257c3b9..e9dfd2ba2 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/MostInfeasible.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/MostInfeasible.h @@ -9,26 +9,26 @@ #include "VariableBranching.h" namespace idol::BranchingRules { - template class MostInfeasible; + template class MostInfeasible; } -template -class idol::BranchingRules::MostInfeasible : public VariableBranching { +template +class idol::BranchingRules::MostInfeasible : public VariableBranching { protected: - std::list> scoring_function(const std::list &t_var, const Node &t_node) override; + std::list> scoring_function(const std::list &t_var, const Node &t_node) override; public: - explicit MostInfeasible(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); + explicit MostInfeasible(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); }; -template -idol::BranchingRules::MostInfeasible::MostInfeasible( - const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) - : VariableBranching(t_parent, std::move(t_branching_candidates)) {} +template +idol::BranchingRules::MostInfeasible::MostInfeasible( + const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) + : VariableBranching(t_parent, std::move(t_branching_candidates)) {} -template +template std::list> -idol::BranchingRules::MostInfeasible::scoring_function(const std::list &t_variables, - const Node &t_node) { +idol::BranchingRules::MostInfeasible::scoring_function(const std::list &t_variables, + const Node &t_node) { const auto& primal_solution = t_node.info().primal_solution(); diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h index 596a40dd7..989837db2 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/PseudoCost.h @@ -10,11 +10,11 @@ #include "NodeScoreFunction.h" namespace idol::BranchingRules { - template class PseudoCost; + template class PseudoCost; } -template -class idol::BranchingRules::PseudoCost : public VariableBranching { +template +class idol::BranchingRules::PseudoCost : public VariableBranching { std::unique_ptr m_scoring_function; @@ -29,26 +29,26 @@ class idol::BranchingRules::PseudoCost : public VariableBranching Map m_pseudo_cost_data; - double compute_upper_bounding_score(const Var& t_var, const PseudoCostData& t_data, const Node& t_node) const; + double compute_upper_bounding_score(const Var& t_var, const PseudoCostData& t_data, const Node& t_node) const; - double compute_lower_bounding_score(const Var& t_var, const PseudoCostData& t_data, const Node& t_node) const; + double compute_lower_bounding_score(const Var& t_var, const PseudoCostData& t_data, const Node& t_node) const; - double compute_average_lower_bounding_score(const Node& t_node) const; + double compute_average_lower_bounding_score(const Node& t_node) const; - double compute_average_upper_bounding_score(const Node& t_node) const; + double compute_average_upper_bounding_score(const Node& t_node) const; public: - explicit PseudoCost(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); + explicit PseudoCost(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates); - std::list> scoring_function(const std::list &t_var, const Node &t_node) override; + std::list> scoring_function(const std::list &t_var, const Node &t_node) override; - void on_node_solved(const Node &t_node) override; + void on_node_solved(const Node &t_node) override; }; -template -typename idol::BranchingRules::PseudoCost::PseudoCostData & -idol::BranchingRules::PseudoCost::PseudoCostData::operator+=( - const idol::BranchingRules::PseudoCost::PseudoCostData &t_rhs) { +template +typename idol::BranchingRules::PseudoCost::PseudoCostData & +idol::BranchingRules::PseudoCost::PseudoCostData::operator+=( + const idol::BranchingRules::PseudoCost::PseudoCostData &t_rhs) { objective_gains_by_upper_boundings += t_rhs.objective_gains_by_upper_boundings; objective_gains_by_lower_boundings += t_rhs.objective_gains_by_lower_boundings; @@ -58,10 +58,10 @@ idol::BranchingRules::PseudoCost::PseudoCostData::operator+=( return *this; } -template -void idol::BranchingRules::PseudoCost::on_node_solved(const idol::Node &t_node) { +template +void idol::BranchingRules::PseudoCost::on_node_solved(const idol::Node &t_node) { - BranchingRule::on_node_solved(t_node); + BranchingRule::on_node_solved(t_node); if (t_node.level() == 0) { return; @@ -105,17 +105,17 @@ void idol::BranchingRules::PseudoCost::on_node_solved(const idol:: } -template -idol::BranchingRules::PseudoCost::PseudoCost( - const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) - : VariableBranching(t_parent, std::move(t_branching_candidates)), +template +idol::BranchingRules::PseudoCost::PseudoCost( + const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates) + : VariableBranching(t_parent, std::move(t_branching_candidates)), m_scoring_function(new NodeScoreFunctions::Linear()) {} -template +template std::list> -idol::BranchingRules::PseudoCost::scoring_function(const std::list &t_variables, - const Node &t_node) { +idol::BranchingRules::PseudoCost::scoring_function(const std::list &t_variables, + const Node &t_node) { std::list> result; @@ -139,10 +139,10 @@ idol::BranchingRules::PseudoCost::scoring_function(const std::list return result; } -template -double idol::BranchingRules::PseudoCost::compute_lower_bounding_score(const idol::Var &t_var, +template +double idol::BranchingRules::PseudoCost::compute_lower_bounding_score(const idol::Var &t_var, const PseudoCostData& t_data, - const idol::Node &t_node) const { + const idol::Node &t_node) const { const double sum_of_objective_gains_per_unit_change = t_data.objective_gains_by_lower_boundings; const unsigned int n_entries = t_data.n_entries_for_lower_bounds; @@ -151,10 +151,10 @@ double idol::BranchingRules::PseudoCost::compute_lower_bounding_sc return (std::ceil(var_value) - var_value) * sum_of_objective_gains_per_unit_change / (double) n_entries; } -template -double idol::BranchingRules::PseudoCost::compute_upper_bounding_score(const idol::Var &t_var, +template +double idol::BranchingRules::PseudoCost::compute_upper_bounding_score(const idol::Var &t_var, const PseudoCostData& t_data, - const idol::Node &t_node) const { + const idol::Node &t_node) const { const double sum_of_objective_gains_per_unit_change = t_data.objective_gains_by_upper_boundings; const unsigned int n_entries = t_data.n_entries_for_upper_bounds; @@ -163,9 +163,9 @@ double idol::BranchingRules::PseudoCost::compute_upper_bounding_sc return (var_value - std::floor(var_value)) * sum_of_objective_gains_per_unit_change / (double) n_entries; } -template -double idol::BranchingRules::PseudoCost::compute_average_upper_bounding_score( - const idol::Node &t_node) const { +template +double idol::BranchingRules::PseudoCost::compute_average_upper_bounding_score( + const idol::Node &t_node) const { double sum = 0.; unsigned int n = 0; @@ -187,9 +187,9 @@ double idol::BranchingRules::PseudoCost::compute_average_upper_bou return sum / (double) n; } -template -double idol::BranchingRules::PseudoCost::compute_average_lower_bounding_score( - const idol::Node &t_node) const { +template +double idol::BranchingRules::PseudoCost::compute_average_lower_bounding_score( + const idol::Node &t_node) const { double sum = 0.; unsigned int n = 0; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/StrongBranching.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/StrongBranching.h index 732f4e860..7327bc186 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/StrongBranching.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/StrongBranching.h @@ -12,44 +12,44 @@ #include "idol/optimizers/branch-and-bound/branching-rules/factories/StrongBranching.h" namespace idol::BranchingRules { - template class StrongBranching; + template class StrongBranching; } -template -class idol::BranchingRules::StrongBranching : public VariableBranching { - std::unique_ptr> m_inner_variable_branching_rule; +template +class idol::BranchingRules::StrongBranching : public VariableBranching { + std::unique_ptr> m_inner_variable_branching_rule; std::unique_ptr m_score_function; std::list m_phases; std::vector> sort_variables_by_score(const std::list>& t_scores); - std::list> make_nodes(const std::list& t_node_infos, const Node& t_parent_node); + std::list> make_nodes(const std::list& t_node_infos, const Node& t_parent_node); - StrongBranchingPhase& current_phase(const Node& t_node); + StrongBranchingPhase& current_phase(const Node& t_node); - void solve_nodes(StrongBranchingPhase& t_phase, std::list>& t_nodes); + void solve_nodes(StrongBranchingPhase& t_phase, std::list>& t_nodes); - double compute_score(double t_parent_objective, std::list>& t_nodes); + double compute_score(double t_parent_objective, std::list>& t_nodes); public: - explicit StrongBranching(const Optimizers::BranchAndBound& t_parent, + explicit StrongBranching(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates, unsigned int t_max_n_variables, NodeScoreFunction* t_score_function, const std::list& t_phases); - std::list> scoring_function(const std::list &t_var, const Node &t_node) override; + std::list> scoring_function(const std::list &t_var, const Node &t_node) override; }; -template -idol::BranchingRules::StrongBranching::StrongBranching( - const idol::Optimizers::BranchAndBound &t_parent, +template +idol::BranchingRules::StrongBranching::StrongBranching( + const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates, unsigned int t_max_n_variables, NodeScoreFunction* t_score_function, const std::list& t_phases ) - : VariableBranching(t_parent, std::move(t_branching_candidates)), - m_inner_variable_branching_rule(new BranchingRules::MostInfeasible(t_parent, {})), + : VariableBranching(t_parent, std::move(t_branching_candidates)), + m_inner_variable_branching_rule(new BranchingRules::MostInfeasible(t_parent, {})), m_score_function(t_score_function->clone()), m_phases(t_phases) { @@ -62,10 +62,10 @@ idol::BranchingRules::StrongBranching::StrongBranching( ); } -template +template std::list> -idol::BranchingRules::StrongBranching::scoring_function(const std::list &t_variables, - const Node &t_node) { +idol::BranchingRules::StrongBranching::scoring_function(const std::list &t_variables, + const Node &t_node) { std::list> result; @@ -92,8 +92,8 @@ idol::BranchingRules::StrongBranching::scoring_function(const std: return result; } -template -std::vector> idol::BranchingRules::StrongBranching::sort_variables_by_score( +template +std::vector> idol::BranchingRules::StrongBranching::sort_variables_by_score( const std::list> &t_scores) { std::vector> result; @@ -107,12 +107,12 @@ std::vector> idol::BranchingRules::StrongBranching< return result; } -template -std::list> -idol::BranchingRules::StrongBranching::make_nodes(const std::list& t_node_infos, - const Node& t_parent_node) { +template +std::list> +idol::BranchingRules::StrongBranching::make_nodes(const std::list& t_node_infos, + const Node& t_parent_node) { - std::list> result; + std::list> result; const unsigned int id = t_parent_node.id(); @@ -123,8 +123,8 @@ idol::BranchingRules::StrongBranching::make_nodes(const std::list< return result; } -template -void idol::BranchingRules::StrongBranching::solve_nodes(StrongBranchingPhase& t_phase, std::list>& t_nodes) { +template +void idol::BranchingRules::StrongBranching::solve_nodes(StrongBranchingPhase& t_phase, std::list>& t_nodes) { auto& branch_and_bound = this->parent(); auto& optimizer = const_cast(branch_and_bound.relaxation().optimizer()); @@ -140,9 +140,9 @@ void idol::BranchingRules::StrongBranching::solve_nodes(StrongBran } -template -double idol::BranchingRules::StrongBranching::compute_score(double t_parent_objective, - std::list>& t_nodes) { +template +double idol::BranchingRules::StrongBranching::compute_score(double t_parent_objective, + std::list>& t_nodes) { if (t_nodes.size() != 2) { throw Exception("Strong branching expected two nodes, got " + std::to_string(t_nodes.size()) + "."); @@ -160,9 +160,9 @@ double idol::BranchingRules::StrongBranching::compute_score(double } -template +template idol::StrongBranchingPhase & -idol::BranchingRules::StrongBranching::current_phase(const idol::Node &t_node) { +idol::BranchingRules::StrongBranching::current_phase(const idol::Node &t_node) { const unsigned int level = t_node.level(); diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/UniformlyRandom.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/UniformlyRandom.h index 92a7918c3..a52bc87e7 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/UniformlyRandom.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/UniformlyRandom.h @@ -9,33 +9,33 @@ #include "VariableBranching.h" namespace idol::BranchingRules { - template class UniformlyRandom; + template class UniformlyRandom; } -template -class idol::BranchingRules::UniformlyRandom : public VariableBranching { +template +class idol::BranchingRules::UniformlyRandom : public VariableBranching { unsigned int m_seed; protected: - std::list> scoring_function(const std::list &t_var, const Node &t_node) override; + std::list> scoring_function(const std::list &t_var, const Node &t_node) override; public: - explicit UniformlyRandom(const Optimizers::BranchAndBound& t_parent, + explicit UniformlyRandom(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates, unsigned int t_seed); }; -template -idol::BranchingRules::UniformlyRandom::UniformlyRandom( - const idol::Optimizers::BranchAndBound &t_parent, +template +idol::BranchingRules::UniformlyRandom::UniformlyRandom( + const idol::Optimizers::BranchAndBound &t_parent, std::list t_branching_candidates, unsigned int t_seed ) - : VariableBranching(t_parent, std::move(t_branching_candidates)), + : VariableBranching(t_parent, std::move(t_branching_candidates)), m_seed(t_seed) {} -template +template std::list> -idol::BranchingRules::UniformlyRandom::scoring_function(const std::list &t_variables, - const Node &t_node) { +idol::BranchingRules::UniformlyRandom::scoring_function(const std::list &t_variables, + const Node &t_node) { std::mt19937 engine(m_seed); std::uniform_real_distribution distribution(0, 1); diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h index 3bf96bd09..769ecb038 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h @@ -15,13 +15,12 @@ namespace idol::BranchingRules { class VariableBranching; } -template -class idol::BranchingRules::VariableBranching : public BranchingRule { +template +class idol::BranchingRules::VariableBranching : public BranchingRule { std::list m_branching_candidates; public: - using NodeInfoT = NodeVarInfoT; - virtual bool is_valid(const Node &t_node) const { + virtual bool is_valid(const Node &t_node) const { const auto& primal_solution = t_node.info().primal_solution(); @@ -34,9 +33,9 @@ class idol::BranchingRules::VariableBranching : public BranchingRule> scoring_function(const std::list& t_variables, const Node &t_node) = 0; + virtual std::list> scoring_function(const std::list& t_variables, const Node &t_node) = 0; - virtual std::list create_child_nodes_for_selected_variable(const Node &t_node, const Var& t_var) { + virtual std::list create_child_nodes_for_selected_variable(const Node &t_node, const Var& t_var) { const auto& primal_solution = t_node.info().primal_solution(); const double value = primal_solution.get(t_var); @@ -60,7 +59,7 @@ class idol::BranchingRules::VariableBranching : public BranchingRule create_child_nodes(const Node &t_node) { + virtual std::list create_child_nodes(const Node &t_node) { const auto& primal_solution = t_node.info().primal_solution(); @@ -82,8 +81,8 @@ class idol::BranchingRules::VariableBranching : public BranchingRule& branching_candidates() const { return m_branching_candidates; } - VariableBranching(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates) - : BranchingRule(t_parent), + VariableBranching(const Optimizers::BranchAndBound& t_parent, std::list t_branching_candidates) + : BranchingRule(t_parent), m_branching_candidates(std::move(t_branching_candidates)) { } diff --git a/lib/include/idol/optimizers/branch-and-bound/callbacks/AbstractBranchAndBoundCallbackI.h b/lib/include/idol/optimizers/branch-and-bound/callbacks/AbstractBranchAndBoundCallbackI.h index 34801d669..c2d7483f1 100644 --- a/lib/include/idol/optimizers/branch-and-bound/callbacks/AbstractBranchAndBoundCallbackI.h +++ b/lib/include/idol/optimizers/branch-and-bound/callbacks/AbstractBranchAndBoundCallbackI.h @@ -10,19 +10,19 @@ namespace idol { - template + template class BranchAndBoundCallback; class CuttingPlaneGenerator; namespace Optimizers { - template + template class BranchAndBound; } struct SideEffectRegistry; - template + template class AbstractBranchAndBoundCallbackI; } @@ -32,24 +32,24 @@ struct idol::SideEffectRegistry { unsigned int n_added_user_cuts = 0; }; -template +template class idol::AbstractBranchAndBoundCallbackI { public: virtual ~AbstractBranchAndBoundCallbackI() = default; protected: virtual SideEffectRegistry operator()( - Optimizers::BranchAndBound* t_parent, + Optimizers::BranchAndBound* t_parent, CallbackEvent t_event, - const Node& t_current_node, + const Node& t_current_node, Model* t_relaxation) = 0; - virtual void add_callback(BranchAndBoundCallback* t_cb) = 0; + virtual void add_callback(BranchAndBoundCallback* t_cb) = 0; virtual void add_cutting_plane_generator(const CuttingPlaneGenerator& t_cutting_plane_generator) = 0; virtual void initialize(const Model& t_model) = 0; - friend class Optimizers::BranchAndBound; + friend class Optimizers::BranchAndBound; }; #endif //IDOL_ABSTRACTBRANCHANDBOUNDCALLBACKI_H diff --git a/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h b/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h index 7275a3d48..82eb93c9e 100644 --- a/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h +++ b/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallback.h @@ -14,26 +14,26 @@ namespace idol { - template + template class BranchAndBoundCallback; - template + template class BranchAndBoundCallbackI; } /** - * @tparam NodeVarInfoT the class used to store each branch-and-tree node's information + * @tparam NodeInfoT the class used to store each branch-and-tree node's information */ -template -class idol::BranchAndBoundCallbackI : public AbstractBranchAndBoundCallbackI { - friend class BranchAndBoundCallback; +template +class idol::BranchAndBoundCallbackI : public AbstractBranchAndBoundCallbackI { + friend class BranchAndBoundCallback; - std::list>> m_callbacks; - std::list>> m_cutting_plane_callbacks; + std::list>> m_callbacks; + std::list>> m_cutting_plane_callbacks; std::list> m_cutting_plane_generators; - Optimizers::BranchAndBound* m_parent = nullptr; - std::optional> m_node; + Optimizers::BranchAndBound* m_parent = nullptr; + std::optional> m_node; Model* m_relaxation = nullptr; SideEffectRegistry* m_registry = nullptr; @@ -44,7 +44,7 @@ class idol::BranchAndBoundCallbackI : public AbstractBranchAndBoundCallbackI& node() const; + [[nodiscard]] const Node& node() const; [[nodiscard]] const Model& relaxation() const; @@ -54,57 +54,57 @@ class idol::BranchAndBoundCallbackI : public AbstractBranchAndBoundCallbackI *t_parent, + Optimizers::BranchAndBound *t_parent, CallbackEvent t_event, - const Node& t_current_node, + const Node& t_current_node, Model *t_relaxation) override; - void add_callback(BranchAndBoundCallback *t_cb) override; + void add_callback(BranchAndBoundCallback *t_cb) override; void add_cutting_plane_generator(const CuttingPlaneGenerator &t_cutting_plane_generator) override; void initialize(const Model& t_model) override; }; -template -const idol::SideEffectRegistry &idol::BranchAndBoundCallbackI::side_effect_registry() const { +template +const idol::SideEffectRegistry &idol::BranchAndBoundCallbackI::side_effect_registry() const { if (!m_registry) { throw Exception("No side effect registry was found"); } return *m_registry; } -template -const idol::Timer &idol::BranchAndBoundCallbackI::time() const { +template +const idol::Timer &idol::BranchAndBoundCallbackI::time() const { return m_parent->time(); } -template -void idol::BranchAndBoundCallbackI::initialize(const Model& t_model) { +template +void idol::BranchAndBoundCallbackI::initialize(const Model& t_model) { m_cutting_plane_callbacks.clear(); for (auto& generator : m_cutting_plane_generators) { generator->operator()(t_model); for (auto& cb : generator->generated_callbacks()) { - m_cutting_plane_callbacks.emplace_back(CallbackAsBranchAndBoundCallback(*cb).operator()()); + m_cutting_plane_callbacks.emplace_back(CallbackAsBranchAndBoundCallback(*cb).operator()()); } generator->clear_generated_callbacks(); } } -template -void idol::BranchAndBoundCallbackI::add_cutting_plane_generator(const CuttingPlaneGenerator &t_cutting_plane_generator) { +template +void idol::BranchAndBoundCallbackI::add_cutting_plane_generator(const CuttingPlaneGenerator &t_cutting_plane_generator) { m_cutting_plane_generators.emplace_back(t_cutting_plane_generator.clone()); } -template +template class idol::BranchAndBoundCallback { public: virtual ~BranchAndBoundCallback() = default; @@ -131,7 +131,7 @@ class idol::BranchAndBoundCallback { * Returns the node which is currently explored * @return the node which is currently explored */ - [[nodiscard]] const Node& node() const; + [[nodiscard]] const Node& node() const; /** * Returns the current node's model being solved @@ -152,7 +152,7 @@ class idol::BranchAndBoundCallback { * if and only if it is valid and improves the current best objective. * @param t_info a node information storing the solution */ - void submit_heuristic_solution(NodeVarInfoT* t_info); + void submit_heuristic_solution(NodeInfoT* t_info); /** * Submits a new proven bound. @@ -170,78 +170,78 @@ class idol::BranchAndBoundCallback { [[nodiscard]] const Timer& time() const; private: - BranchAndBoundCallbackI* m_interface = nullptr; + BranchAndBoundCallbackI* m_interface = nullptr; void throw_if_no_interface() const; - friend class BranchAndBoundCallbackI; + friend class BranchAndBoundCallbackI; }; -template -const idol::SideEffectRegistry &idol::BranchAndBoundCallback::side_effect_registry() const { +template +const idol::SideEffectRegistry &idol::BranchAndBoundCallback::side_effect_registry() const { throw_if_no_interface(); return m_interface->side_effect_registry(); } -template -const idol::Timer &idol::BranchAndBoundCallback::time() const { +template +const idol::Timer &idol::BranchAndBoundCallback::time() const { throw_if_no_interface(); return m_interface->time(); } -template -void idol::BranchAndBoundCallback::submit_bound(double t_bound) { +template +void idol::BranchAndBoundCallback::submit_bound(double t_bound) { throw_if_no_interface(); m_interface->submit_bound(t_bound); } -template -void idol::BranchAndBoundCallback::submit_heuristic_solution(NodeVarInfoT *t_info) { +template +void idol::BranchAndBoundCallback::submit_heuristic_solution(NodeInfoT *t_info) { throw_if_no_interface(); m_interface->submit_heuristic_solution(t_info); } -template -const idol::Model &idol::BranchAndBoundCallback::original_model() const { +template +const idol::Model &idol::BranchAndBoundCallback::original_model() const { throw_if_no_interface(); return m_interface->original_model(); } -template -const idol::Model &idol::BranchAndBoundCallback::relaxation() const { +template +const idol::Model &idol::BranchAndBoundCallback::relaxation() const { throw_if_no_interface(); return m_interface->relaxation(); } -template -const idol::Node &idol::BranchAndBoundCallback::node() const { +template +const idol::Node &idol::BranchAndBoundCallback::node() const { throw_if_no_interface(); return m_interface->node(); } -template -void idol::BranchAndBoundCallback::add_lazy_cut(const TempCtr &t_cut) { +template +void idol::BranchAndBoundCallback::add_lazy_cut(const TempCtr &t_cut) { throw_if_no_interface(); m_interface->add_lazy_cut(t_cut); } -template -void idol::BranchAndBoundCallback::add_user_cut(const TempCtr &t_cut) { +template +void idol::BranchAndBoundCallback::add_user_cut(const TempCtr &t_cut) { throw_if_no_interface(); m_interface->add_user_cut(t_cut); } -template -void idol::BranchAndBoundCallback::throw_if_no_interface() const { +template +void idol::BranchAndBoundCallback::throw_if_no_interface() const { if (!m_interface) { throw Exception("No interface was found."); } } -template +template idol::SideEffectRegistry -idol::BranchAndBoundCallbackI::operator()(Optimizers::BranchAndBound *t_parent, CallbackEvent t_event, - const Node &t_current_node, Model *t_relaxation) { +idol::BranchAndBoundCallbackI::operator()(Optimizers::BranchAndBound *t_parent, CallbackEvent t_event, + const Node &t_current_node, Model *t_relaxation) { SideEffectRegistry result; m_parent = t_parent; @@ -273,59 +273,59 @@ idol::BranchAndBoundCallbackI::operator()(Optimizers::BranchAndBou return result; } -template -void idol::BranchAndBoundCallbackI::add_callback(BranchAndBoundCallback *t_cb) { +template +void idol::BranchAndBoundCallbackI::add_callback(BranchAndBoundCallback *t_cb) { m_callbacks.emplace_back(t_cb); } -template -void idol::BranchAndBoundCallbackI::submit_bound(double t_bound) { +template +void idol::BranchAndBoundCallbackI::submit_bound(double t_bound) { if (!m_parent) { throw Exception("submit_bound is not accessible in this context."); } m_parent->submit_lower_bound(t_bound); } -template -void idol::BranchAndBoundCallbackI::submit_heuristic_solution(NodeVarInfoT *t_info) { +template +void idol::BranchAndBoundCallbackI::submit_heuristic_solution(NodeInfoT *t_info) { if (!m_parent) { throw Exception("submit_heuristic_solution is not accessible in this context."); } m_parent->submit_heuristic_solution(t_info); } -template -const idol::Model &idol::BranchAndBoundCallbackI::original_model() const { +template +const idol::Model &idol::BranchAndBoundCallbackI::original_model() const { if (!m_parent) { throw Exception("original_model is not accessible in this context."); } return m_parent->parent(); } -template -const idol::Model &idol::BranchAndBoundCallbackI::relaxation() const { +template +const idol::Model &idol::BranchAndBoundCallbackI::relaxation() const { if (!m_relaxation) { throw Exception("relaxation is not accessible in this context."); } return *m_relaxation; } -template -const idol::Node &idol::BranchAndBoundCallbackI::node() const { +template +const idol::Node &idol::BranchAndBoundCallbackI::node() const { if (!m_node) { throw Exception("node is not accessible in this context."); } return *m_node; } -template -void idol::BranchAndBoundCallbackI::add_lazy_cut(const TempCtr &t_cut) { +template +void idol::BranchAndBoundCallbackI::add_lazy_cut(const TempCtr &t_cut) { m_relaxation->add_ctr(t_cut); ++m_registry->n_added_lazy_cuts; } -template -void idol::BranchAndBoundCallbackI::add_user_cut(const TempCtr &t_cut) { +template +void idol::BranchAndBoundCallbackI::add_user_cut(const TempCtr &t_cut) { m_relaxation->add_ctr(t_cut); ++m_registry->n_added_user_cuts; } diff --git a/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h b/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h index 71a44494e..10356722c 100644 --- a/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h +++ b/lib/include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h @@ -6,21 +6,21 @@ #define IDOL_BRANCHANDBOUNDCALLBACKFACTORY_H namespace idol { - template + template class BranchAndBoundCallback; - template + template class BranchAndBoundCallbackFactory; } -template +template class idol::BranchAndBoundCallbackFactory { public: virtual ~BranchAndBoundCallbackFactory() = default; - virtual BranchAndBoundCallback* operator()() = 0; + virtual BranchAndBoundCallback* operator()() = 0; - virtual BranchAndBoundCallbackFactory* clone() const = 0; + virtual BranchAndBoundCallbackFactory* clone() const = 0; }; #endif //IDOL_BRANCHANDBOUNDCALLBACKFACTORY_H diff --git a/lib/include/idol/optimizers/branch-and-bound/callbacks/CallbackAsBranchAndBoundCallback.h b/lib/include/idol/optimizers/branch-and-bound/callbacks/CallbackAsBranchAndBoundCallback.h index 1a7607f47..21f84fca6 100644 --- a/lib/include/idol/optimizers/branch-and-bound/callbacks/CallbackAsBranchAndBoundCallback.h +++ b/lib/include/idol/optimizers/branch-and-bound/callbacks/CallbackAsBranchAndBoundCallback.h @@ -11,21 +11,21 @@ #include "BranchAndBoundCallback.h" namespace idol { - template + template class CallbackAsBranchAndBoundCallback; } -template -class idol::CallbackAsBranchAndBoundCallback : public BranchAndBoundCallbackFactory { +template +class idol::CallbackAsBranchAndBoundCallback : public BranchAndBoundCallbackFactory { std::unique_ptr m_callback_factory; CallbackAsBranchAndBoundCallback(const CallbackAsBranchAndBoundCallback& t_src) : m_callback_factory(t_src.m_callback_factory->clone()) {} public: - class Strategy : public BranchAndBoundCallback { + class Strategy : public BranchAndBoundCallback { - friend class ::idol::CallbackAsBranchAndBoundCallback; + friend class ::idol::CallbackAsBranchAndBoundCallback; class Interface : public CallbackI { friend class Strategy; @@ -47,7 +47,7 @@ class idol::CallbackAsBranchAndBoundCallback : public BranchAndBoundCallbackFact } void submit_heuristic_solution(Solution::Primal t_solution) override { - auto* info = new NodeVarInfoT(); + auto* info = new NodeInfoT(); info->set_primal_solution(std::move(t_solution)); m_parent.submit_heuristic_solution(info); } @@ -75,13 +75,13 @@ class idol::CallbackAsBranchAndBoundCallback : public BranchAndBoundCallbackFact explicit CallbackAsBranchAndBoundCallback(const CallbackFactory& t_factory) : m_callback_factory(t_factory.clone()) {} - BranchAndBoundCallback *operator()() override { + BranchAndBoundCallback *operator()() override { auto* cb = m_callback_factory->operator()(); return new Strategy(cb); } - BranchAndBoundCallbackFactory *clone() const override { - return new CallbackAsBranchAndBoundCallback(*this); + BranchAndBoundCallbackFactory *clone() const override { + return new CallbackAsBranchAndBoundCallback(*this); } }; diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/BranchingDecision.h b/lib/include/idol/optimizers/branch-and-bound/nodes/BranchingDecision.h new file mode 100644 index 000000000..4668ede61 --- /dev/null +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/BranchingDecision.h @@ -0,0 +1,31 @@ +// +// Created by henri on 15.11.23. +// + +#ifndef IDOL_BRANCHINGDECISION_H +#define IDOL_BRANCHINGDECISION_H + +#include + +#include "idol/modeling/variables/Var.h" +#include "idol/modeling/constraints/Ctr.h" +#include "idol/modeling/constraints/TempCtr.h" + +namespace idol { + + struct VarBranchingDecision { + Var variable; + CtrType type; + double bound; + VarBranchingDecision(Var t_variable, CtrType t_type, double t_bound) : variable(std::move(t_variable)), type(t_type), bound(t_bound) {} + }; + + struct CtrBranchingDecision { + Ctr constraint; + TempCtr temporary_constraint; + CtrBranchingDecision(Ctr t_constraint, TempCtr&& t_temporary_constraint) : constraint(std::move(t_constraint)), temporary_constraint(t_temporary_constraint) {} + }; + +} + +#endif //IDOL_BRANCHINGDECISION_H diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h index 3c4150fe8..76dc11b1e 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h @@ -7,6 +7,7 @@ #include "idol/modeling/models/Model.h" #include "DefaultNodeUpdator.h" +#include "BranchingDecision.h" namespace idol { class DefaultNodeInfo; @@ -19,19 +20,6 @@ namespace idol { class idol::DefaultNodeInfo { public: - struct VarBranchingDecision { - Var variable; - CtrType type; - double bound; - VarBranchingDecision(Var t_variable, CtrType t_type, double t_bound) : variable(std::move(t_variable)), type(t_type), bound(t_bound) {} - }; - - struct CtrBranchingDecision { - Ctr constraint; - TempCtr temporary_constraint; - CtrBranchingDecision(const Ctr& t_constraint, TempCtr&& t_temporary_constraint) : constraint(t_constraint), temporary_constraint(t_temporary_constraint) {} - }; - DefaultNodeInfo() = default; virtual ~DefaultNodeInfo() = default; diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h index e9b547f32..74962a1c3 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h @@ -8,14 +8,16 @@ #include "NodeUpdator.h" #include "idol/modeling/models/Model.h" #include "Node.h" +#include "DefaultNodeInfo.h" +#include "BranchingDecision.h" namespace idol { template class DefaultNodeUpdator; } -template -class idol::DefaultNodeUpdator : public NodeUpdator { +template +class idol::DefaultNodeUpdator : public NodeUpdator { Model& m_relaxation; @@ -24,21 +26,21 @@ class idol::DefaultNodeUpdator : public NodeUpdator { std::list m_added_constraints; - void apply_variable_branching_decisions(const Node &t_node, + void apply_variable_branching_decisions(const Node &t_node, Map &t_changed_lower_bounds, Map &t_changed_upper_bounds); - void apply_variable_branching_decision(const typename NodeVarInfoT::VarBranchingDecision& t_branching_decision, + void apply_variable_branching_decision(const VarBranchingDecision& t_branching_decision, Map &t_current_changed_bounds, Map &t_changed_bounds); - void apply_constraint_branching_decisions(const Node &t_node); + void apply_constraint_branching_decisions(const Node &t_node); public: explicit DefaultNodeUpdator(Model& t_relaxation); DefaultNodeUpdator(const DefaultNodeUpdator&) = delete; - void prepare(const Node &t_node) override; + void prepare(const Node &t_node) override; void clear() override; @@ -48,9 +50,9 @@ class idol::DefaultNodeUpdator : public NodeUpdator { const Model& relaxation() const { return m_relaxation; } }; -template +template void -idol::DefaultNodeUpdator::apply_constraint_branching_decisions(const idol::Node &t_node) { +idol::DefaultNodeUpdator::apply_constraint_branching_decisions(const idol::Node &t_node) { if (t_node.level() == 0) { return; @@ -70,8 +72,8 @@ idol::DefaultNodeUpdator::DefaultNodeUpdator(idol::Model &t_relaxation) : } -template -void idol::DefaultNodeUpdator::clear() { +template +void idol::DefaultNodeUpdator::clear() { // Clear LB for (const auto& [var, lb] : m_changed_lower_bounds) { @@ -92,8 +94,8 @@ void idol::DefaultNodeUpdator::clear() { m_added_constraints.clear(); } -template -void idol::DefaultNodeUpdator::prepare(const Node &t_node) { +template +void idol::DefaultNodeUpdator::prepare(const Node &t_node) { Map changed_lower_bounds; Map changed_upper_bounds; @@ -106,8 +108,8 @@ void idol::DefaultNodeUpdator::prepare(const Node &t apply_constraint_branching_decisions(t_node); } -template -void idol::DefaultNodeUpdator::apply_variable_branching_decisions(const idol::Node &t_node, +template +void idol::DefaultNodeUpdator::apply_variable_branching_decisions(const idol::Node &t_node, Map &t_changed_lower_bounds, Map &t_changed_upper_bounds ) { @@ -135,8 +137,8 @@ void idol::DefaultNodeUpdator::apply_variable_branching_decisions( } -template -void idol::DefaultNodeUpdator::apply_variable_branching_decision(const typename NodeVarInfoT::VarBranchingDecision &t_branching_decision, +template +void idol::DefaultNodeUpdator::apply_variable_branching_decision(const VarBranchingDecision &t_branching_decision, idol::Map &t_current_changed_bounds, idol::Map &t_changed_bounds) { diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/Node.h b/lib/include/idol/optimizers/branch-and-bound/nodes/Node.h index 2b62f83f5..7eb365f12 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/Node.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/Node.h @@ -9,27 +9,27 @@ namespace idol { class Model; - template class Node; + template class Node; } -template +template class idol::Node { - static_assert(std::is_default_constructible_v); + static_assert(std::is_default_constructible_v); unsigned int m_id; unsigned int m_level; - std::shared_ptr m_info; - std::shared_ptr> m_parent; + std::shared_ptr m_info; + std::shared_ptr> m_parent; - Node() : m_level(0), m_id(0), m_info(std::make_shared()) {} - Node(NodeVarInfoT* t_ptr_to_info, unsigned int t_id) : m_id(t_id), m_level(0), m_info(t_ptr_to_info) {} + Node() : m_level(0), m_id(0), m_info(std::make_shared()) {} + Node(NodeInfoT* t_ptr_to_info, unsigned int t_id) : m_id(t_id), m_level(0), m_info(t_ptr_to_info) {} public: - Node(NodeVarInfoT* t_ptr_to_info, unsigned int t_id, const Node& t_parent) + Node(NodeInfoT* t_ptr_to_info, unsigned int t_id, const Node& t_parent) : m_id(t_id), m_level(t_parent.level() + 1), m_info(t_ptr_to_info), - m_parent(std::make_shared>(t_parent)) + m_parent(std::make_shared>(t_parent)) {} Node(const Node&) = default; @@ -38,19 +38,19 @@ class idol::Node { Node& operator=(const Node&) = default; Node& operator=(Node&&) noexcept = default; - static Node create_root_node() { return Node(); } + static Node create_root_node() { return Node(); } - static Node create_detached_node(NodeVarInfoT* t_ptr_to_info) { return Node(t_ptr_to_info, -1); } + static Node create_detached_node(NodeInfoT* t_ptr_to_info) { return Node(t_ptr_to_info, -1); } [[nodiscard]] unsigned int id() const { return m_id; } [[nodiscard]] unsigned int level() const { return m_level; } - [[nodiscard]] const NodeVarInfoT& info() const { return *m_info; } + [[nodiscard]] const NodeInfoT& info() const { return *m_info; } - NodeVarInfoT& info() { return *m_info; } + NodeInfoT& info() { return *m_info; } - [[nodiscard]] const Node& parent() const { return *m_parent; } + [[nodiscard]] const Node& parent() const { return *m_parent; } }; #endif //IDOL_NODE_H diff --git a/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp b/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp index bbed4d549..82cfa8e7d 100644 --- a/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp +++ b/lib/src/optimizers/branch-and-bound/nodes/DefaultNodeInfo.cpp @@ -3,6 +3,7 @@ // #include "idol/optimizers/branch-and-bound/nodes/DefaultNodeInfo.h" #include "idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h" +#include "idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h" idol::DefaultNodeUpdator * idol::DefaultNodeInfo::create_updator(idol::Model &t_relaxation) { From e102d0b1dce681dd991683e7db08a4b8feea4846 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 11:28:18 +0100 Subject: [PATCH 06/12] add initialize method to NodeUpdator --- .../optimizers/branch-and-bound/Optimizers_BranchAndBound.h | 2 ++ .../idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h | 2 ++ .../idol/optimizers/branch-and-bound/nodes/NodeUpdator.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h index f15eb9120..420d89a94 100644 --- a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h +++ b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h @@ -366,6 +366,8 @@ void idol::Optimizers::BranchAndBound::hook_before_optimize() { m_root_node_best_obj = Inf; m_callback->initialize(parent()); + + m_node_updator->initialize(); } template diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h index 74962a1c3..96fc8a1c5 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/DefaultNodeUpdator.h @@ -40,6 +40,8 @@ class idol::DefaultNodeUpdator : public NodeUpdator { DefaultNodeUpdator(const DefaultNodeUpdator&) = delete; + void initialize() override {} + void prepare(const Node &t_node) override; void clear() override; diff --git a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h b/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h index e1d046136..e138229b4 100644 --- a/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h +++ b/lib/include/idol/optimizers/branch-and-bound/nodes/NodeUpdator.h @@ -20,6 +20,8 @@ class idol::NodeUpdator { public: virtual ~NodeUpdator() = default; + virtual void initialize() = 0; + virtual void prepare(const Node& t_node) = 0; virtual void clear() = 0; From e987d7d04eac63f20bea8dda157b2a43b611bff7 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 14:19:15 +0100 Subject: [PATCH 07/12] add Pair to use std::equal_to for comparison, allowing Var == Var to define a TempCtr --- lib/CMakeLists.txt | 1 + lib/include/idol/containers/Map.h | 22 +++++++------- lib/include/idol/containers/Pair.h | 29 +++++++++++++++++++ .../to_rotated_quadratic_cone.h | 2 +- .../idol/modeling/expressions/AbstractExpr.h | 12 +++++--- .../idol/modeling/expressions/Constant.h | 4 +-- .../idol/modeling/expressions/QuadExpr.h | 13 +++++---- lib/include/idol/modeling/objects/Object.h | 21 +++++--------- lib/src/modeling/expressions/Constant.cpp | 10 +++---- lib/src/modeling/matrix/Matrix.cpp | 4 +-- 10 files changed, 74 insertions(+), 44 deletions(-) create mode 100644 lib/include/idol/containers/Pair.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fe0eb899c..6adc4772d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -197,6 +197,7 @@ add_library(idol STATIC src/optimizers/logs.cpp include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingWithPriority.h include/idol/optimizers/branch-and-bound/nodes/BranchingDecision.h + include/idol/containers/Pair.h ) find_package(OpenMP REQUIRED) diff --git a/lib/include/idol/containers/Map.h b/lib/include/idol/containers/Map.h index 93c719920..463b1661d 100644 --- a/lib/include/idol/containers/Map.h +++ b/lib/include/idol/containers/Map.h @@ -8,11 +8,13 @@ #ifdef IDOL_USE_ROBINHOOD #include +#include "Pair.h" + #else #include #endif -// Implements hash for pairs (non-symmetric by default (std::hash>) and symmetric impls) +// Implements hash for pairs (non-symmetric by default (std::hash>) and symmetric impls) // See https://youngforest.github.io/2020/05/27/best-implement-to-use-pair-as-key-to-std-unordered-map-in-C/ namespace idol::impl { @@ -47,18 +49,18 @@ namespace idol::impl { struct symmetric_pair_hash { template - std::size_t operator()(const std::pair &t_pair) const { + std::size_t operator()(const idol::Pair &t_pair) const { return std::less()(t_pair.first, t_pair.second) ? - hash>()(t_pair) - : hash>()(std::make_pair(t_pair.second, t_pair.first)); + hash>()(t_pair) + : hash>()(idol::Pair(t_pair.second, t_pair.first)); } }; struct symmetric_pair_equal_to { template - std::size_t operator()(const std::pair& t_a, const std::pair& t_b) const { - const auto a = std::less()(t_a.first, t_a.second) ? t_a : std::make_pair(t_a.second, t_a.first); - const auto b = std::less()(t_b.first, t_b.second) ? t_b : std::make_pair(t_b.second, t_b.first); + std::size_t operator()(const idol::Pair& t_a, const idol::Pair& t_b) const { + const auto a = std::less()(t_a.first, t_a.second) ? t_a : idol::Pair(t_a.second, t_a.first); + const auto b = std::less()(t_b.first, t_b.second) ? t_b : idol::Pair(t_b.second, t_b.first); return std::equal_to()(a.first, b.first) && std::equal_to()(a.second, b.second); } }; @@ -66,8 +68,8 @@ namespace idol::impl { } template -struct std::hash> { - std::size_t operator()(const std::pair& t_pair) const { +struct std::hash> { + std::size_t operator()(const idol::Pair& t_pair) const { return idol::impl::hash_val(t_pair.first, t_pair.second); } }; @@ -95,7 +97,7 @@ namespace idol { class T, class Hash = std::hash, class KeyEqual = std::equal_to, - class Allocator = std::allocator > + class Allocator = std::allocator > > using Map = std::unordered_map; diff --git a/lib/include/idol/containers/Pair.h b/lib/include/idol/containers/Pair.h new file mode 100644 index 000000000..d906e790d --- /dev/null +++ b/lib/include/idol/containers/Pair.h @@ -0,0 +1,29 @@ +// +// Created by henri on 15.11.23. +// + +#ifndef IDOL_PAIR_H +#define IDOL_PAIR_H + +namespace idol { + template struct Pair; +} + +template +struct idol::Pair { + T1 first; + T2 second; + Pair(const T1& t_1, const T2& t_2) : first(t_1), second(t_2) {} + + Pair(const Pair&) = default; + Pair(Pair&&) = default; + + Pair& operator=(const Pair&) = default; + Pair& operator=(Pair&&) = default; + + bool operator==(const Pair& t_rhs) const { + return std::equal_to()(first, t_rhs.first) && std::equal_to()(second, t_rhs.second); + } +}; + +#endif //IDOL_PAIR_H diff --git a/lib/include/idol/linear-algebra/to_rotated_quadratic_cone.h b/lib/include/idol/linear-algebra/to_rotated_quadratic_cone.h index e0899cbfe..f4ed35c2c 100644 --- a/lib/include/idol/linear-algebra/to_rotated_quadratic_cone.h +++ b/lib/include/idol/linear-algebra/to_rotated_quadratic_cone.h @@ -27,7 +27,7 @@ namespace idol { SquareMatrix Q(indices); for (const auto &[var1, var2, constant]: t_expr) { - if (var1 == var2) { + if (var1.id() == var2.id()) { Q.set(var1, var2, constant.numerical()); } else { Q.set(var1, var2, constant.numerical() / 2.); diff --git a/lib/include/idol/modeling/expressions/AbstractExpr.h b/lib/include/idol/modeling/expressions/AbstractExpr.h index b8cd3c091..43b1e2762 100644 --- a/lib/include/idol/modeling/expressions/AbstractExpr.h +++ b/lib/include/idol/modeling/expressions/AbstractExpr.h @@ -13,6 +13,7 @@ #include "idol/modeling/matrix/MatrixCoefficient.h" #include "idol/modeling/variables/Var.h" #include "idol/errors/Exception.h" +#include "idol/containers/Pair.h" #include #include #include @@ -169,13 +170,16 @@ template idol::impl::AbstractExpr & idol::impl::AbstractExpr::operator=(const impl::AbstractExpr &t_src) { - if (this == &t_src) { return *this; } + if (this == &t_src) { + return *this; + } + m_map.clear(); for (const auto& [key, ptr_to_value] : t_src.m_map) { m_map.template emplace(key, std::make_unique(ptr_to_value->value())); } - return *this; + return *this; } template @@ -325,11 +329,11 @@ class idol::impl::AbstractExpr::const_itera bool operator!=(const const_iterator& t_rhs) const { return m_it != t_rhs.m_it; } bool operator==(const const_iterator& t_rhs) const { return m_it == t_rhs.m_it; } const_iterator operator++() { ++m_it; return *this; } - IteratorOutputT operator*() const { return { m_it->first, m_it->second->value() }; } + IteratorOutputT operator*() const { return IteratorOutputT(m_it->first, m_it->second->value()); } }; template, + class IteratorOutputT = idol::Pair, class Hash = std::hash, class EqualTo = std::equal_to > diff --git a/lib/include/idol/modeling/expressions/Constant.h b/lib/include/idol/modeling/expressions/Constant.h index b7ceb7f00..6cf33e5af 100644 --- a/lib/include/idol/modeling/expressions/Constant.h +++ b/lib/include/idol/modeling/expressions/Constant.h @@ -48,7 +48,7 @@ struct idol::QuadParam { */ class idol::Constant { Map m_linear_terms; - Map, double, idol::impl::symmetric_pair_hash, idol::impl::symmetric_pair_equal_to> m_quadratic_terms; + Map, double, idol::impl::symmetric_pair_hash, idol::impl::symmetric_pair_equal_to> m_quadratic_terms; double m_constant = 0.; void insert_or_add(const Param& t_param, double t_value); @@ -285,7 +285,7 @@ namespace idol { t_os << '!' << t_param.name(); }; - const auto print_quad_term = [&t_os](const std::pair &t_pair, double t_coeff) { + const auto print_quad_term = [&t_os](const auto &t_pair, double t_coeff) { if (!idol::equals(t_coeff, 1., idol::Tolerance::Sparsity)) { t_os << t_coeff << ' '; } diff --git a/lib/include/idol/modeling/expressions/QuadExpr.h b/lib/include/idol/modeling/expressions/QuadExpr.h index 9e7ac5b65..3e4ca0a7b 100644 --- a/lib/include/idol/modeling/expressions/QuadExpr.h +++ b/lib/include/idol/modeling/expressions/QuadExpr.h @@ -6,6 +6,7 @@ #define IDOL_QUADEXPR_H #include "AbstractExpr.h" +#include "idol/containers/Pair.h" namespace idol { template @@ -24,7 +25,7 @@ struct idol::QuadTerm { const Key1& key1; const Key2& key2; const Constant& constant; - QuadTerm(const std::pair& t_vars, const Constant& t_constant) + QuadTerm(const Pair& t_vars, const Constant& t_constant) : key1(t_vars.first), key2(t_vars.second), constant(t_constant) {} }; @@ -33,18 +34,18 @@ template, idol::impl::symmetric_pair_hash, - std::hash> + std::hash> >, class EqualTo = std::conditional_t< std::is_same_v, idol::impl::symmetric_pair_equal_to, - std::equal_to> + std::equal_to> > > -class idol::QuadExpr : public AbstractExpr, QuadTerm, Hash, EqualTo> { +class idol::QuadExpr : public AbstractExpr, QuadTerm, Hash, EqualTo> { friend class Matrix; - using ParentT = AbstractExpr, QuadTerm, Hash, EqualTo>; + using ParentT = AbstractExpr, QuadTerm, Hash, EqualTo>; using ParentT::get; using ParentT::set; public: @@ -83,7 +84,7 @@ idol::QuadExpr::QuadExpr(const Constant &t_factor, co template void idol::QuadExpr::set(const Key1 &t_a, const Key2& t_b, Constant t_coefficient) { - return set(std::make_pair(t_a, t_b), std::move(t_coefficient)); + return set({ t_a, t_b }, std::move(t_coefficient)); } template diff --git a/lib/include/idol/modeling/objects/Object.h b/lib/include/idol/modeling/objects/Object.h index 9c0cec068..46c7094a8 100644 --- a/lib/include/idol/modeling/objects/Object.h +++ b/lib/include/idol/modeling/objects/Object.h @@ -8,6 +8,7 @@ #include #include "ObjectId.h" #include "idol/containers/Vector.h" +#include "idol/containers/Pair.h" #include "idol/modeling/annotations/Annotation.h" #include "idol/errors/Exception.h" @@ -55,20 +56,6 @@ class idol::Object { */ [[nodiscard]] unsigned int id() const { return m_object_id->id(); } - /** - * Returns true if the two optimization objects have the same id, false otherwise. - * @param t_rhs the other optimization object. - * @return True if the two optimization objects have the same id, false otherwise. - */ - bool operator==(const Object& t_rhs) const { return id() == t_rhs.id(); } - - /** - * Returns false if the two optimization objects have the same id, true otherwise. - * @param t_rhs the other optimization object. - * @return False if the two optimization objects have the same id, true otherwise. - */ - bool operator!=(const Object& t_rhs) const { return id() != t_rhs.id(); } - /** * Returns true if the optimization object is part of the model `t_model`, false otherwise. * @param t_model The model. @@ -154,6 +141,12 @@ struct std::less { \ std::size_t operator()(const idol::name& t_a, const idol::name& t_b) const { \ return t_a.id() < t_b.id(); \ } \ +}; \ +template<> \ +struct std::equal_to> { \ + std::size_t operator()(const idol::Pair& t_a, const idol::Pair& t_b) const { \ + return std::equal_to()(t_a.first, t_b.first) && std::equal_to()(t_a.second, t_b.second); \ + } \ }; #endif //IDOL_OBJECT_H diff --git a/lib/src/modeling/expressions/Constant.cpp b/lib/src/modeling/expressions/Constant.cpp index 62c54acf3..9a86eb328 100644 --- a/lib/src/modeling/expressions/Constant.cpp +++ b/lib/src/modeling/expressions/Constant.cpp @@ -16,7 +16,7 @@ idol::Constant::Constant(const Param &t_param, double t_value) : m_linear_terms( idol::Constant::Constant(const idol::Param &t_param_1, const idol::Param &t_param_2, double t_value) { - m_quadratic_terms.emplace(std::make_pair<>(t_param_1, t_param_2), t_value); + m_quadratic_terms.emplace(Pair(t_param_1, t_param_2), t_value); if (equals(t_value, 0., Tolerance::Sparsity)) { m_quadratic_terms.clear(); @@ -46,7 +46,7 @@ double idol::Constant::get(const Param &t_param) const { } double idol::Constant::get(const idol::Param &t_param_1, const idol::Param &t_param_2) const { - auto it = m_quadratic_terms.find(std::make_pair(t_param_1, t_param_2)); + auto it = m_quadratic_terms.find(Pair(t_param_1, t_param_2)); return it == m_quadratic_terms.end() ? 0. : it->second; } @@ -131,7 +131,7 @@ void idol::Constant::insert_or_add(const idol::Param &t_param_1, const idol::Par return; } - auto [it, success] = m_quadratic_terms.emplace(std::make_pair(t_param_1, t_param_2), t_value); + auto [it, success] = m_quadratic_terms.emplace(Pair(t_param_1, t_param_2), t_value); if (!success) { it->second += t_value; if (equals(it->second, 0., Tolerance::Sparsity)) { @@ -175,11 +175,11 @@ double idol::Constant::fix(const Solution::Dual &t_duals) const { void idol::Constant::set(const idol::Param &t_param_1, const idol::Param &t_param_2, double t_value) { if (equals(t_value, 0., Tolerance::Sparsity)) { - m_quadratic_terms.erase(std::make_pair( t_param_1, t_param_2 )); + m_quadratic_terms.erase(Pair( t_param_1, t_param_2 )); return; } - auto [it, success] = m_quadratic_terms.emplace(std::make_pair(t_param_1, t_param_2), t_value); + auto [it, success] = m_quadratic_terms.emplace(Pair(t_param_1, t_param_2), t_value); if (!success) { it->second = t_value; } diff --git a/lib/src/modeling/matrix/Matrix.cpp b/lib/src/modeling/matrix/Matrix.cpp index 9ca931275..4d4bf2cac 100644 --- a/lib/src/modeling/matrix/Matrix.cpp +++ b/lib/src/modeling/matrix/Matrix.cpp @@ -17,7 +17,7 @@ void idol::Matrix::add_row_to_columns(const Ctr &t_ctr) { access_column(pair.first).quadratic().refs().set({ t_ctr, pair.second }, std::move(ref)); } for (auto [pair, ref] : row.quadratic().refs()) { - if (pair.first != pair.second) { + if (pair.first.id() != pair.second.id()) { access_column(pair.second).quadratic().refs().set({t_ctr, pair.first}, std::move(ref)); } } @@ -56,7 +56,7 @@ void idol::Matrix::remove_row_from_columns(const Ctr &t_ctr) { } for (const auto& [pair, ptr_to_coeff] : row.quadratic().refs()) { access_column(pair.first).impl().quadratic().remove({ t_ctr, pair.second }); - if (pair.first != pair.second) { + if (pair.first.id() != pair.second.id()) { access_column(pair.second).impl().quadratic().remove({ t_ctr, pair.first }); } } From 5a3917361685663cb69ed4ddccf9bea4cc13ae0d Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 14:20:27 +0100 Subject: [PATCH 08/12] add include Pair.h --- lib/include/idol/containers/Map.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/include/idol/containers/Map.h b/lib/include/idol/containers/Map.h index 463b1661d..cf6717a3b 100644 --- a/lib/include/idol/containers/Map.h +++ b/lib/include/idol/containers/Map.h @@ -8,12 +8,13 @@ #ifdef IDOL_USE_ROBINHOOD #include -#include "Pair.h" #else #include #endif +#include "Pair.h" + // Implements hash for pairs (non-symmetric by default (std::hash>) and symmetric impls) // See https://youngforest.github.io/2020/05/27/best-implement-to-use-pair-as-key-to-std-unordered-map-in-C/ namespace idol::impl { From a6648e723469de7ad626a0467a111930226f8c8c Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 15:38:02 +0100 Subject: [PATCH 09/12] add initialize to branching rules --- .../optimizers/branch-and-bound/Optimizers_BranchAndBound.h | 2 +- .../branch-and-bound/branching-rules/impls/BranchingRule.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h index 420d89a94..7f1d96cb8 100644 --- a/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h +++ b/lib/include/idol/optimizers/branch-and-bound/Optimizers_BranchAndBound.h @@ -366,7 +366,7 @@ void idol::Optimizers::BranchAndBound::hook_before_optimize() { m_root_node_best_obj = Inf; m_callback->initialize(parent()); - + m_branching_rule->initialize(); m_node_updator->initialize(); } diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h index 0b1439f28..e2cf1f021 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h @@ -37,6 +37,8 @@ class idol::BranchingRule { [[nodiscard]] const Model& model() const { return m_parent.parent(); } + virtual void initialize() {} + [[nodiscard]] virtual bool is_valid(const Node& t_node) const = 0; [[nodiscard]] virtual std::list create_child_nodes(const Node& t_node) = 0; From 853b1aa733a4ff718ea33e32639bf5ab484f5647 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 15:38:28 +0100 Subject: [PATCH 10/12] write now write all sub-prolbems to file in Optimizers::DWD --- .../Optimizers_DantzigWolfeDecomposition.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/src/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp b/lib/src/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp index af43c2057..cd43bc9d9 100644 --- a/lib/src/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp +++ b/lib/src/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp @@ -104,7 +104,17 @@ void idol::Optimizers::DantzigWolfeDecomposition::update() { } void idol::Optimizers::DantzigWolfeDecomposition::write(const std::string &t_name) { - m_formulation.master().write(t_name); + + const auto extension_index = t_name.find_last_of('.'); + const auto base_name = (extension_index != std::string::npos) ? t_name.substr(0, extension_index) : t_name; + const auto extension = (extension_index != std::string::npos) ? t_name.substr(extension_index) : ""; + + + m_formulation.master().write( base_name + ".master" + extension ); + for (unsigned int sub_problem_index = 0, n_sub_problems = m_formulation.n_sub_problems() ; sub_problem_index < n_sub_problems ; ++sub_problem_index) { + m_formulation.sub_problem(sub_problem_index) + .write( base_name + ".sub_problem." + std::to_string(sub_problem_index) + extension ); + } } double idol::Optimizers::DantzigWolfeDecomposition::get_var_primal(const idol::Var &t_var) const { From 7e34e8d6f885df9c652c5322bfc379f4e0af31f2 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 17:16:22 +0100 Subject: [PATCH 11/12] remove const for is_valid --- .../branch-and-bound/branching-rules/impls/BranchingRule.h | 2 +- .../branch-and-bound/branching-rules/impls/VariableBranching.h | 2 +- lib/include/idol/optimizers/dantzig-wolfe/logs/Debug.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h index e2cf1f021..03b866190 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/BranchingRule.h @@ -39,7 +39,7 @@ class idol::BranchingRule { virtual void initialize() {} - [[nodiscard]] virtual bool is_valid(const Node& t_node) const = 0; + [[nodiscard]] virtual bool is_valid(const Node& t_node) = 0; [[nodiscard]] virtual std::list create_child_nodes(const Node& t_node) = 0; diff --git a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h index 769ecb038..11590c392 100644 --- a/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h +++ b/lib/include/idol/optimizers/branch-and-bound/branching-rules/impls/VariableBranching.h @@ -20,7 +20,7 @@ class idol::BranchingRules::VariableBranching : public BranchingRule std::list m_branching_candidates; public: - virtual bool is_valid(const Node &t_node) const { + virtual bool is_valid(const Node &t_node) { const auto& primal_solution = t_node.info().primal_solution(); diff --git a/lib/include/idol/optimizers/dantzig-wolfe/logs/Debug.h b/lib/include/idol/optimizers/dantzig-wolfe/logs/Debug.h index 90974bc3c..efa8baea8 100644 --- a/lib/include/idol/optimizers/dantzig-wolfe/logs/Debug.h +++ b/lib/include/idol/optimizers/dantzig-wolfe/logs/Debug.h @@ -60,7 +60,7 @@ class idol::DantzigWolfe::Loggers::Debug : public DantzigWolfe::LoggerFactory { Strategy *operator()() const override { return new Strategy( m_frequency.has_value() ? m_frequency.value() : 1, - m_log_for_sub_problems.has_value() && m_log_for_sub_problems.value() + true // m_log_for_sub_problems.has_value() && m_log_for_sub_problems.value() ); } From 98277d46ac88e6803606c0bc0ba65bf9c4f9ae56 Mon Sep 17 00:00:00 2001 From: Henri Lefebvre Date: Wed, 15 Nov 2023 22:29:37 +0100 Subject: [PATCH 12/12] fixes add constraint in DW Formulation --- lib/src/optimizers/dantzig-wolfe/Formulation.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/optimizers/dantzig-wolfe/Formulation.cpp b/lib/src/optimizers/dantzig-wolfe/Formulation.cpp index 59d831e77..744df926f 100644 --- a/lib/src/optimizers/dantzig-wolfe/Formulation.cpp +++ b/lib/src/optimizers/dantzig-wolfe/Formulation.cpp @@ -639,10 +639,13 @@ void idol::DantzigWolfe::Formulation::add(const idol::Ctr &t_ctr, idol::CtrType const auto sub_problem_id = t_ctr.get(m_decomposition_by_ctr); if (sub_problem_id != MasterId) { - m_sub_problems[sub_problem_id].add(t_ctr, TempCtr(Row(t_row), t_type)); + remove_column_if(sub_problem_id, [&](const Var& t_alpha, const Solution::Primal& t_generator) { return t_row.is_violated(t_generator, t_type); }); + + m_sub_problems[sub_problem_id].add(t_ctr, TempCtr(Row(t_row), t_type)); + return; }