diff --git a/dev/main.cpp b/dev/main.cpp index 05192861..b07cf99e 100644 --- a/dev/main.cpp +++ b/dev/main.cpp @@ -11,6 +11,8 @@ #include "idol/optimizers/branch-and-bound/node-selection-rules/factories/WorstBound.h" #include "idol/optimizers/dantzig-wolfe/DantzigWolfeDecomposition.h" #include "idol/optimizers/dantzig-wolfe/infeasibility-strategies/FarkasPricing.h" +#include "idol/optimizers/dantzig-wolfe/stabilization/Neame.h" +#include "idol/optimizers/dantzig-wolfe/stabilization/Wentges.h" using namespace idol; @@ -53,15 +55,16 @@ int main(int t_argc, char** t_argv) { model.use(BranchAndBound() .with_node_optimizer( DantzigWolfe::Decomposition(decomposition) - .with_master_optimizer(HiGHS::ContinuousRelaxation()) + .with_master_optimizer(Gurobi::ContinuousRelaxation()) .with_default_sub_problem_spec( DantzigWolfe::SubProblem() - .add_optimizer(HiGHS()) + .add_optimizer(Gurobi()) .with_multiplicities(0, 1) ) .with_infeasibility_strategy(DantzigWolfe::FarkasPricing()) .with_hard_branching(false) .with_max_parallel_sub_problems(5) + .with_dual_price_smoothing_stabilization(DantzigWolfe::Wentges(.3)) .with_log_level(Info, Yellow) ) .with_subtree_depth(0) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index cf5b5649..414da51e 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -193,6 +193,10 @@ add_library(idol STATIC include/idol/optimizers/dantzig-wolfe/infeasibility-strategies/FarkasPricing.h src/optimizers/column-generation/infeasibility-strategies/FarkasPricing.cpp src/optimizers/column-generation/infeasibility-strategies/DantzigWolfeInfeasibilityStrategy.cpp + include/idol/optimizers/dantzig-wolfe/stabilization/DualPriceSmoothingStabilization.h + include/idol/optimizers/dantzig-wolfe/stabilization/Neame.h + include/idol/optimizers/dantzig-wolfe/stabilization/Wentges.h + include/idol/optimizers/dantzig-wolfe/stabilization/NoStabilization.h ) find_package(OpenMP REQUIRED) diff --git a/lib/include/idol/optimizers/dantzig-wolfe/DantzigWolfeDecomposition.h b/lib/include/idol/optimizers/dantzig-wolfe/DantzigWolfeDecomposition.h index e0c6ed51..b5b9a9a4 100644 --- a/lib/include/idol/optimizers/dantzig-wolfe/DantzigWolfeDecomposition.h +++ b/lib/include/idol/optimizers/dantzig-wolfe/DantzigWolfeDecomposition.h @@ -11,6 +11,7 @@ #include "idol/containers/Map.h" #include "DantzigWolfeFormulation.h" #include "idol/optimizers/dantzig-wolfe/infeasibility-strategies/DantzigWolfeInfeasibilityStrategy.h" +#include "idol/optimizers/dantzig-wolfe/stabilization/DualPriceSmoothingStabilization.h" namespace idol::DantzigWolfe { class Decomposition; @@ -20,6 +21,7 @@ class idol::DantzigWolfe::Decomposition : public OptimizerFactoryWithDefaultPara Annotation m_decomposition; std::unique_ptr m_master_optimizer_factory; std::unique_ptr m_infeasibility_strategy; + std::unique_ptr m_dual_price_smoothing_stabilization; std::optional m_max_parallel_sub_problems; std::optional m_use_hard_branching; std::optional m_default_sub_problem_spec; @@ -47,6 +49,8 @@ class idol::DantzigWolfe::Decomposition : public OptimizerFactoryWithDefaultPara Decomposition& with_max_parallel_sub_problems(unsigned int t_n_sub_problems); + Decomposition& with_dual_price_smoothing_stabilization(const DualPriceSmoothingStabilization& t_stabilization); + const SubProblem& get_sub_problem_spec(unsigned int t_id) const; }; diff --git a/lib/include/idol/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.h b/lib/include/idol/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.h index e8f86f42..f7a3d7ea 100644 --- a/lib/include/idol/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.h +++ b/lib/include/idol/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.h @@ -9,6 +9,7 @@ #include "DantzigWolfeFormulation.h" #include "DantzigWolfeSubProblem.h" #include "idol/optimizers/dantzig-wolfe/infeasibility-strategies/DantzigWolfeInfeasibilityStrategy.h" +#include "idol/optimizers/dantzig-wolfe/stabilization/DualPriceSmoothingStabilization.h" namespace idol::Optimizers { class DantzigWolfeDecomposition; @@ -18,6 +19,7 @@ class idol::Optimizers::DantzigWolfeDecomposition : public Algorithm { idol::DantzigWolfe::Formulation m_formulation; std::unique_ptr m_master_optimizer_factory; std::unique_ptr m_strategy; + std::unique_ptr m_stabilization; std::vector m_sub_problem_specifications; unsigned int m_max_parallel_pricing; bool m_use_hard_branching = false; @@ -25,6 +27,7 @@ class idol::Optimizers::DantzigWolfeDecomposition : public Algorithm { DantzigWolfeDecomposition(const Model& t_model, idol::DantzigWolfe::Formulation&& t_formulation, const OptimizerFactory& t_master_optimizer_factory, + const DantzigWolfe::DualPriceSmoothingStabilization& t_stabilization, unsigned int t_max_parallel_pricing, bool t_use_hard_branching, std::vector&& t_sub_problem_specifications, diff --git a/lib/include/idol/optimizers/dantzig-wolfe/column-generation/ColumnGeneration.h b/lib/include/idol/optimizers/dantzig-wolfe/column-generation/ColumnGeneration.h index 9c9edf10..35815d8b 100644 --- a/lib/include/idol/optimizers/dantzig-wolfe/column-generation/ColumnGeneration.h +++ b/lib/include/idol/optimizers/dantzig-wolfe/column-generation/ColumnGeneration.h @@ -14,7 +14,6 @@ class idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration { SolutionReason m_reason = NotSpecified; std::optional m_master_primal_solution; std::optional m_last_master_solution; - std::optional m_stability_center; std::vector m_sub_problems_phases; double m_best_obj = -Inf; double m_best_bound = +Inf; diff --git a/lib/include/idol/optimizers/dantzig-wolfe/stabilization/DualPriceSmoothingStabilization.h b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/DualPriceSmoothingStabilization.h new file mode 100644 index 00000000..d2ff094d --- /dev/null +++ b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/DualPriceSmoothingStabilization.h @@ -0,0 +1,34 @@ +// +// Created by henri on 31.10.23. +// + +#ifndef IDOL_DUALPRICESMOOTHINGSTABILIZATION_H +#define IDOL_DUALPRICESMOOTHINGSTABILIZATION_H + +#include "idol/modeling/solutions/Solution.h" + +namespace idol::DantzigWolfe { + class DualPriceSmoothingStabilization; +} + +class idol::DantzigWolfe::DualPriceSmoothingStabilization { +public: + virtual ~DualPriceSmoothingStabilization() = default; + + class Strategy { + public: + virtual ~Strategy() = default; + + virtual void initialize() = 0; + + virtual void update_stability_center(const Solution::Dual& t_master_dual) = 0; + + virtual Solution::Dual compute_smoothed_dual_solution(const Solution::Dual& t_master_dual) = 0; + }; + + virtual Strategy* operator()() const = 0; + + [[nodiscard]] virtual DualPriceSmoothingStabilization* clone() const = 0; +}; + +#endif //IDOL_DUALPRICESMOOTHINGSTABILIZATION_H diff --git a/lib/include/idol/optimizers/dantzig-wolfe/stabilization/Neame.h b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/Neame.h new file mode 100644 index 00000000..2331fc9b --- /dev/null +++ b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/Neame.h @@ -0,0 +1,58 @@ +// +// Created by henri on 31.10.23. +// + +#ifndef IDOL_NEAME_H +#define IDOL_NEAME_H + +#include "DualPriceSmoothingStabilization.h" + +namespace idol::DantzigWolfe { + class Neame; +} + +class idol::DantzigWolfe::Neame : public DualPriceSmoothingStabilization { + double m_initial_factor; +public: + explicit Neame(double t_initial_factor) : m_initial_factor(t_initial_factor) {} + + class Strategy : public DualPriceSmoothingStabilization::Strategy { + double m_factor; + std::optional m_last_master_dual; + public: + explicit Strategy(double t_initial_factor) : m_factor(t_initial_factor) {} + + void initialize() override { + m_last_master_dual.reset(); + } + + void update_stability_center(const Solution::Dual &t_master_dual) override { + // intentionally left blank + } + + Solution::Dual compute_smoothed_dual_solution(const Solution::Dual &t_master_dual) override { + + if (!m_last_master_dual.has_value() || m_factor <= 1e-4) { + m_last_master_dual = t_master_dual; + return t_master_dual; + } + + auto result = m_factor * m_last_master_dual.value() + (1. - m_factor) * t_master_dual; + + m_last_master_dual = t_master_dual; + + return result; + } + + }; + + DualPriceSmoothingStabilization::Strategy *operator()() const override { + return new Strategy(m_initial_factor); + } + + DualPriceSmoothingStabilization *clone() const override { + return new Neame(*this); + } +}; + +#endif //IDOL_NEAME_H diff --git a/lib/include/idol/optimizers/dantzig-wolfe/stabilization/NoStabilization.h b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/NoStabilization.h new file mode 100644 index 00000000..817f0dd1 --- /dev/null +++ b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/NoStabilization.h @@ -0,0 +1,45 @@ +// +// Created by henri on 31.10.23. +// + +#ifndef IDOL_NOSTABILIZATION_H +#define IDOL_NOSTABILIZATION_H + +#include "DualPriceSmoothingStabilization.h" + +namespace idol::DantzigWolfe { + class NoStabilization; +} + +class idol::DantzigWolfe::NoStabilization : public DualPriceSmoothingStabilization { +public: + explicit NoStabilization() = default; + + class Strategy : public DualPriceSmoothingStabilization::Strategy { + public: + explicit Strategy() = default; + + void initialize() override { + // intentionally left blank + } + + void update_stability_center(const Solution::Dual &t_master_dual) override { + // intentionally left blank + } + + Solution::Dual compute_smoothed_dual_solution(const Solution::Dual &t_master_dual) override { + return t_master_dual; + } + + }; + + DualPriceSmoothingStabilization::Strategy *operator()() const override { + return new Strategy(); + } + + DualPriceSmoothingStabilization *clone() const override { + return new NoStabilization(*this); + } +}; + +#endif //IDOL_NOSTABILIZATION_H diff --git a/lib/include/idol/optimizers/dantzig-wolfe/stabilization/Wentges.h b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/Wentges.h new file mode 100644 index 00000000..d2a2c552 --- /dev/null +++ b/lib/include/idol/optimizers/dantzig-wolfe/stabilization/Wentges.h @@ -0,0 +1,59 @@ +// +// Created by henri on 31.10.23. +// + +#ifndef IDOL_WENTGES_H +#define IDOL_WENTGES_H + +#include +#include "DualPriceSmoothingStabilization.h" + +namespace idol::DantzigWolfe { + class Wentges; +} + +class idol::DantzigWolfe::Wentges : public DualPriceSmoothingStabilization { + double m_initial_factor; +public: + explicit Wentges(double t_initial_factor) : m_initial_factor(t_initial_factor) {} + + class Strategy : public DualPriceSmoothingStabilization::Strategy { + double m_factor; + std::optional m_stability_center; + public: + explicit Strategy(double t_initial_factor) : m_factor(t_initial_factor) {} + + void initialize() override { + m_stability_center.reset(); + } + + void update_stability_center(const Solution::Dual &t_master_dual) override { + m_stability_center = t_master_dual; + } + + Solution::Dual compute_smoothed_dual_solution(const Solution::Dual &t_master_dual) override { + + if (!m_stability_center.has_value() || m_factor <= 1e-4) { + m_stability_center = t_master_dual; + return t_master_dual; + } + + auto result = m_factor * m_stability_center.value() + (1. - m_factor) * t_master_dual; + + m_stability_center = t_master_dual; + + return result; + } + + }; + + DualPriceSmoothingStabilization::Strategy *operator()() const override { + return new Strategy(m_initial_factor); + } + + DualPriceSmoothingStabilization *clone() const override { + return new Wentges(*this); + } +}; + +#endif //IDOL_WENTGES_H diff --git a/lib/src/optimizers/column-generation/DantzigWolfeDecomposition.cpp b/lib/src/optimizers/column-generation/DantzigWolfeDecomposition.cpp index 0007d166..4a78cec2 100644 --- a/lib/src/optimizers/column-generation/DantzigWolfeDecomposition.cpp +++ b/lib/src/optimizers/column-generation/DantzigWolfeDecomposition.cpp @@ -7,6 +7,7 @@ #include "idol/optimizers/dantzig-wolfe/DantzigWolfeFormulation.h" #include "idol/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.h" #include "idol/optimizers/dantzig-wolfe/infeasibility-strategies/FarkasPricing.h" +#include "idol/optimizers/dantzig-wolfe/stabilization/NoStabilization.h" idol::OptimizerFactory *idol::DantzigWolfe::Decomposition::clone() const { return new Decomposition(*this); @@ -19,6 +20,7 @@ idol::DantzigWolfe::Decomposition::Decomposition(const Decomposition& t_src) : OptimizerFactoryWithDefaultParameters(t_src), m_decomposition(t_src.m_decomposition), m_master_optimizer_factory(t_src.m_master_optimizer_factory ? t_src.m_master_optimizer_factory->clone() : nullptr), + m_dual_price_smoothing_stabilization(t_src.m_dual_price_smoothing_stabilization ? t_src.m_dual_price_smoothing_stabilization->clone() : nullptr), m_max_parallel_sub_problems(t_src.m_max_parallel_sub_problems), m_use_hard_branching(t_src.m_use_hard_branching), m_infeasibility_strategy(t_src.m_infeasibility_strategy ? t_src.m_infeasibility_strategy->clone() : nullptr), @@ -43,9 +45,15 @@ idol::Optimizer *idol::DantzigWolfe::Decomposition::operator()(const Model &t_mo default_strategy = std::make_unique(); } + std::unique_ptr dual_price_smoothing; + if (!m_dual_price_smoothing_stabilization) { + dual_price_smoothing = std::make_unique(); + } + return new Optimizers::DantzigWolfeDecomposition(t_model, std::move(dantzig_wolfe_formulation), *m_master_optimizer_factory, + m_dual_price_smoothing_stabilization ? *m_dual_price_smoothing_stabilization : *dual_price_smoothing, m_max_parallel_sub_problems.has_value() ? m_max_parallel_sub_problems.value() : 1, m_use_hard_branching.has_value() && m_use_hard_branching.value(), std::move(sub_problems_specifications), @@ -181,3 +189,15 @@ idol::DantzigWolfe::Decomposition::with_max_parallel_sub_problems(unsigned int t return *this; } +idol::DantzigWolfe::Decomposition &idol::DantzigWolfe::Decomposition::with_dual_price_smoothing_stabilization( + const idol::DantzigWolfe::DualPriceSmoothingStabilization &t_stabilization) { + + if (m_dual_price_smoothing_stabilization) { + throw Exception("A dual price smoothing stabilization has already been configured."); + } + + m_dual_price_smoothing_stabilization.reset(t_stabilization.clone()); + + return *this; +} + diff --git a/lib/src/optimizers/column-generation/Optimizers_DantzigWolfeDecomposition.cpp b/lib/src/optimizers/column-generation/Optimizers_DantzigWolfeDecomposition.cpp index 485cfd2d..66075367 100644 --- a/lib/src/optimizers/column-generation/Optimizers_DantzigWolfeDecomposition.cpp +++ b/lib/src/optimizers/column-generation/Optimizers_DantzigWolfeDecomposition.cpp @@ -6,6 +6,7 @@ idol::Optimizers::DantzigWolfeDecomposition::DantzigWolfeDecomposition(const Model& t_model, idol::DantzigWolfe::Formulation &&t_formulation, const OptimizerFactory& t_master_optimizer_factory, + const DantzigWolfe::DualPriceSmoothingStabilization& t_stabilization, unsigned int t_max_parallel_pricing, bool t_use_hard_branching, std::vector&& t_sub_problem_specifications, @@ -16,6 +17,7 @@ idol::Optimizers::DantzigWolfeDecomposition::DantzigWolfeDecomposition(const Mod m_max_parallel_pricing(t_max_parallel_pricing), m_use_hard_branching(t_use_hard_branching), m_sub_problem_specifications(std::move(t_sub_problem_specifications)), + m_stabilization(t_stabilization()), m_strategy(t_strategy()) { diff --git a/lib/src/optimizers/column-generation/column-generation/ColumnGeneration.cpp b/lib/src/optimizers/column-generation/column-generation/ColumnGeneration.cpp index 2ca68d28..e5a6d6a1 100644 --- a/lib/src/optimizers/column-generation/column-generation/ColumnGeneration.cpp +++ b/lib/src/optimizers/column-generation/column-generation/ColumnGeneration.cpp @@ -16,7 +16,6 @@ void idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration::execute() { m_status = Loaded; m_reason = NotSpecified; m_last_master_solution.reset(); - m_stability_center.reset(); m_iteration_count = 0; m_n_generated_columns = 0; m_solve_dual_master = true; @@ -25,6 +24,7 @@ void idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration::execute() { m_is_terminated = false; m_current_iteration_is_using_farkas = false; initialize_sub_problem_phases(); + m_parent.m_stabilization->initialize(); if (m_use_farkas_for_infeasibility) { m_parent.m_formulation.master().optimizer().set_param_infeasible_or_unbounded_info(true); @@ -96,7 +96,7 @@ void idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration::solve_dual_m void idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration::update_sub_problems() { auto& formulation = m_parent.m_formulation; - auto& dual_values = m_last_master_solution.value(); // TODO change this for the smoothened one + auto dual_values = m_parent.m_stabilization->compute_smoothed_dual_solution(m_last_master_solution.value()); for (unsigned int i = 0, n = formulation.n_sub_problems() ; i < n ; ++i) { formulation.update_sub_problem_objective(i, dual_values, m_current_iteration_is_using_farkas); @@ -163,7 +163,7 @@ void idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration::analyze_sub_ if (m_best_bound <= iter_lower_bound) { - m_stability_center = m_last_master_solution; + m_parent.m_stabilization->update_stability_center(m_last_master_solution.value()); m_best_bound = iter_lower_bound; if (m_best_bound >= m_parent.get_param_best_bound_stop()) {