diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 11efa54a..c4969a14 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -107,18 +107,9 @@ add_library(idol STATIC include/idol/optimizers/wrappers/Gurobi/Gurobi.h include/idol/optimizers/wrappers/GLPK/GLPK.h include/idol/optimizers/wrappers/Mosek/Mosek.h - include/idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h - include/idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h - src/optimizers/archive/column-generation/ColumnGeneration.cpp - src/optimizers/archive/column-generation/Optimizers_ColumnGeneration.cpp - include/idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h - src/optimizers/archive/dantzig-wolfe/DantzigWolfeDecomposition.cpp - include/idol/optimizers/archive/dantzig-wolfe/Optimizers_ArchivedDantzigWolfeDecomposition.h - src/optimizers/archive/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp src/optimizers/wrappers/gurobi/Gurobi.cpp src/optimizers/wrappers/GLPK/GLPK.cpp src/optimizers/wrappers/Mosek/Mosek.cpp - include/idol/optimizers/archive/column-generation/OptimizerFactoryWithColumnGenerationParameters.h include/idol/optimizers/branch-and-bound/callbacks/BranchAndBoundCallbackFactory.h include/idol/optimizers/callbacks/IntegerMaster.h src/optimizers/callbacks/IntegerMaster.cpp diff --git a/lib/include/idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h b/lib/include/idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h deleted file mode 100644 index 5a4fd8cd..00000000 --- a/lib/include/idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// Created by henri on 24/03/23. -// - -#ifndef IDOL_ARCHIVEDCOLUMNGENERATION_H -#define IDOL_ARCHIVEDCOLUMNGENERATION_H - -#include -#include "idol/modeling/models/Model.h" -#include "OptimizerFactoryWithColumnGenerationParameters.h" - -namespace idol { - class ArchivedColumnGeneration; -} - -class idol::ArchivedColumnGeneration : public impl::OptimizerFactoryWithColumnGenerationParameters { -public: - explicit ArchivedColumnGeneration(unsigned int t_n_subproblems); - - Optimizer *operator()(const Model &t_model) const override; - - [[nodiscard]] ArchivedColumnGeneration *clone() const override; - - ArchivedColumnGeneration& reserve_for_subproblems(unsigned int t_n_subproblems); - - [[nodiscard]] unsigned int n_subproblems() const { return m_subproblems.size(); } - - ArchivedColumnGeneration& with_master_optimizer(const OptimizerFactory& t_master_optimizer); - - ArchivedColumnGeneration& with_subproblem(const Model& t_model, Column t_column); -private: - std::vector m_subproblems; - std::vector m_generation_patterns; - std::unique_ptr m_master_optimizer; - - ArchivedColumnGeneration(const ArchivedColumnGeneration& t_src); -}; - -#endif //IDOL_ARCHIVEDCOLUMNGENERATION_H diff --git a/lib/include/idol/optimizers/archive/column-generation/OptimizerFactoryWithColumnGenerationParameters.h b/lib/include/idol/optimizers/archive/column-generation/OptimizerFactoryWithColumnGenerationParameters.h deleted file mode 100644 index c942d6d1..00000000 --- a/lib/include/idol/optimizers/archive/column-generation/OptimizerFactoryWithColumnGenerationParameters.h +++ /dev/null @@ -1,252 +0,0 @@ -// -// Created by henri on 29/03/23. -// - -#ifndef IDOL_OPTIMIZERFACTORYWITHCOLUMNGENERATIONPARAMETERS_H -#define IDOL_OPTIMIZERFACTORYWITHCOLUMNGENERATIONPARAMETERS_H - -#include "idol/optimizers/OptimizerFactory.h" -#include "Optimizers_ColumnGeneration.h" - -#include - -namespace idol::impl { - template class OptimizerFactoryWithColumnGenerationParameters; -} - -template -class idol::impl::OptimizerFactoryWithColumnGenerationParameters : public OptimizerFactoryWithDefaultParameters { - // Multithreading parameters - std::optional m_parallel_pricing_limit; - - // Column pool parameters - std::optional m_clean_up_ratio; - std::optional m_clean_up_threshold; - - // Infeasible master parameters - std::optional m_use_farkas_pricing; - std::optional m_artificial_variables_cost; - - // Stabilization parameters - std::optional m_smoothing_factor; - - // Logging parameters - std::optional m_log_frequency; - - // Maximum number of columns generated per pricing operation - std::optional m_max_columns_per_pricing; - - // Non-optimal pricing phase (time_limit, gap) - std::optional> m_non_optimal_pricing_phase; -protected: - void handle_column_generation_parameters(Optimizers::ArchivedColumnGeneration* t_optimizer) const; -public: - /** - * Configures the maximum number of pricing problems which can be solved in parallel - * @param t_limit the desired limit - * @return the optimizer factory itself - */ - CRTP& with_parallel_pricing_limit(unsigned int t_limit); - - /** - * Configures the column pool clean up - * - * When the number of columns in the column pool is larger than the given threshold, - * columns are removed from the pool (and possibly from the master problem) from the oldest columns to the - * newest. Only t_threshold * t_ratio are kept. - * @param t_threshold the desired threshold - * @param t_ratio the clean up ratio - * @return the optimizer factory itself - */ - CRTP& with_column_pool_clean_up(unsigned int t_threshold, double t_ratio); - - /** - * Configures the activation of Farkas pricing. - * - * If set to true, a Farkas certificate is asked to the master problem solver so as to generate new columns to improve - * feasibility or to show infeasibility of the problem. - * - * By default, it is turned off. - * @param t_value true if Farkas pricing is desired, false otherwise - * @return the optimizer factory itself - */ - CRTP& with_farkas_pricing(bool t_value); - - /** - * Configures the cost for the artificial variables added when the master problem is infeasible and - * Farkas pricing is turned off. - * - * When the master problem is infeasible, artificial variables are added to the master problem with an objective - * cost of t_artificial_variable_cost. In case this approach fails to generate columns making the master problem - * feasible (e.g., the costs are too small), the algorithm switches to pure phase I where the constraint violations - * are optimized. - * - * If t_artificial_variable_cost is less or equal than zero, pure phase I is directly applied. - * - * @param t_artificial_variable_cost the desired cost for the artificial variables. - * @return the optimizer factory itself - */ - CRTP& with_artificial_variables_cost(double t_artificial_variable_cost); - - /** - * Configures the stabilization factor of the dual price smoothing stabilization. This value should be in [0,1). - * Note that when this parameter is zero, no stabilization is performed. - * @param t_smoothing_factor the desired stabilization factor - * @return the optimizer factory itself - */ - CRTP& with_dual_price_smoothing_stabilization(double t_smoothing_factor); - - /** - * Configures the frequency for logging. Logging information is displayed every t_log_frequency iterations. - * @param t_log_frequency the desired log frequency - * @return the optimizer factory itself - */ - CRTP& with_log_frequency(unsigned int t_log_frequency); - - /** - * Configures the maximum number of columns which are added for each pricing operation. - * @param t_n_columns the maximum nuber of added columns - * @return the optimizer itself - */ - CRTP& with_max_columns_per_pricing(unsigned int t_n_columns); - - CRTP& with_non_optimal_pricing_phase(double t_time_limit, double t_relative_gap); -}; - -template -CRTP &idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_non_optimal_pricing_phase(double t_time_limit, - double t_relative_gap) { - - if (m_non_optimal_pricing_phase.has_value()) { - throw Exception("Non-optimal pricing phase parameters have already been provided."); - } - - m_non_optimal_pricing_phase = { t_time_limit, t_relative_gap }; - - return this->crtp(); -} - -template -CRTP & -idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_max_columns_per_pricing(unsigned int t_n_columns) { - m_max_columns_per_pricing = t_n_columns; - return this->crtp(); -} - -template -void idol::impl::OptimizerFactoryWithColumnGenerationParameters::handle_column_generation_parameters(Optimizers::ArchivedColumnGeneration *t_optimizer) const { - - if (m_parallel_pricing_limit.has_value()) { - t_optimizer->set_parallel_pricing_limit(m_parallel_pricing_limit.value()); - } - - if (m_clean_up_ratio.has_value()) { - t_optimizer->set_clean_up_ratio(m_clean_up_ratio.value()); - } - - if (m_clean_up_threshold.has_value()) { - t_optimizer->set_clean_up_threshold(m_clean_up_threshold.value()); - } - - if (m_use_farkas_pricing.has_value()) { - t_optimizer->set_farkas_pricing(m_use_farkas_pricing.value()); - } - - if (m_artificial_variables_cost.has_value()) { - t_optimizer->set_artificial_variables_cost(m_artificial_variables_cost.value()); - } - - if (m_smoothing_factor.has_value()) { - t_optimizer->set_dual_price_smoothing_stabilization_factor(m_smoothing_factor.value()); - } - - if (m_log_frequency.has_value()) { - t_optimizer->set_log_frequency(m_log_frequency.value()); - } - - if (m_max_columns_per_pricing.has_value()) { - t_optimizer->set_max_columns_per_pricing(m_max_columns_per_pricing.value()); - } - - if (m_non_optimal_pricing_phase.has_value()) { - const auto [time_limit, relative_gap] = m_non_optimal_pricing_phase.value(); - t_optimizer->set_non_optimal_pricing_phase(time_limit, relative_gap); - } - -} - -template -CRTP &idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_log_frequency(unsigned int t_log_frequency) { - - if (m_log_frequency.has_value()) { - throw Exception("A log frequency has already been set."); - } - - m_log_frequency = t_log_frequency; - - return this->crtp(); -} - -template -CRTP &idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_dual_price_smoothing_stabilization(double t_smoothing_factor) { - - if (m_smoothing_factor.has_value()) { - throw Exception("Dual price smoothing has already been turned on."); - } - - m_smoothing_factor = t_smoothing_factor; - - return this->crtp(); -} - -template -CRTP & -idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_artificial_variables_cost(double t_artificial_variable_cost) { - - if (m_artificial_variables_cost.has_value()) { - throw Exception("Artificial variables cost have already been given."); - } - - m_artificial_variables_cost = t_artificial_variable_cost; - - return this->crtp(); -} - -template -CRTP &idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_farkas_pricing(bool t_value) { - - if (m_use_farkas_pricing.has_value()) { - throw Exception("Farkas pricing setting has already been given."); - } - - m_use_farkas_pricing = t_value; - - return this->crtp(); -} - -template -CRTP &idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_column_pool_clean_up(unsigned int t_threshold, double t_ratio) { - - if (m_clean_up_ratio.has_value() || m_clean_up_threshold.has_value()) { - throw Exception("Column pool clean up settings have already been given."); - } - - m_clean_up_threshold = t_threshold; - m_clean_up_ratio = t_ratio; - - return this->crtp(); -} - -template -CRTP &idol::impl::OptimizerFactoryWithColumnGenerationParameters::with_parallel_pricing_limit(unsigned int t_limit) { - - if (m_parallel_pricing_limit.has_value()) { - throw Exception("A limit for parallel pricing has already been given."); - } - - m_parallel_pricing_limit = t_limit; - - return this->crtp(); -} - -#endif //IDOL_OPTIMIZERFACTORYWITHCOLUMNGENERATIONPARAMETERS_H diff --git a/lib/include/idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h b/lib/include/idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h deleted file mode 100644 index 1feecc95..00000000 --- a/lib/include/idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h +++ /dev/null @@ -1,191 +0,0 @@ -// -// Created by henri on 24/03/23. -// - -#ifndef IDOL_ARCHIVED_COLUMNGENERATION_H -#define IDOL_ARCHIVED_COLUMNGENERATION_H - -#include "idol/optimizers/Algorithm.h" -#include "idol/modeling/models/Model.h" -#include "idol/containers/GeneratorPool.h" - -namespace idol::Optimizers { - class ArchivedColumnGeneration; - class ArchivedDantzigWolfeDecomposition; -} - -class idol::Optimizers::ArchivedColumnGeneration : public Algorithm { -public: - class Subproblem; - - ArchivedColumnGeneration(const Model& t_model, - Model *t_master_problem, - const std::vector &t_subproblems, - std::vector t_generation_patterns); - - [[nodiscard]] std::string name() const override { return "dantzig-wolfe"; } - - [[nodiscard]] const Model& master() const { return *m_master; } - - [[nodiscard]] auto subproblems() const { return ConstIteratorForward(m_subproblems); } - - virtual void set_parallel_pricing_limit(unsigned int t_limit) { m_parallel_pricing_limit = t_limit; } - - [[nodiscard]] unsigned int parallel_pricing_limit() const { return m_parallel_pricing_limit; } - - virtual void set_clean_up_ratio(double t_ratio) { m_clean_up_ratio = t_ratio; } - - [[nodiscard]] double clean_up_ratio() const { return m_clean_up_ratio; } - - virtual void set_clean_up_threshold(unsigned int t_threshold) { m_clean_up_threshold = t_threshold; } - - [[nodiscard]] unsigned int clean_up_threshold() const { return m_clean_up_threshold; } - - virtual void set_farkas_pricing(bool t_value) { m_farkas_pricing = t_value; } - - [[nodiscard]] bool farkas_pricing() const { return m_farkas_pricing; } - - virtual void set_artificial_variables_cost(double t_cost) { m_artificial_variables_cost = t_cost; } - - [[nodiscard]] double artificial_variable_cost() const { return m_artificial_variables_cost; } - - virtual void set_dual_price_smoothing_stabilization_factor(double t_factor) { m_smoothing_factor = t_factor; } - - [[nodiscard]] double dual_price_smoothing_stabilization_factor() const { return m_smoothing_factor; } - - virtual void set_log_frequency(unsigned int t_frequency) { m_log_frequency = t_frequency; } - - [[nodiscard]] unsigned int log_frequency() const { return m_log_frequency; } - - Subproblem& add_subproblem(Model* t_sub_problem_model, Column t_generation_pattern); - - [[nodiscard]] unsigned int n_generated_columns() const { return m_n_generated_columns; } - - void set_max_columns_per_pricing(unsigned int t_n_columns) { m_max_columns_per_pricing = t_n_columns; } - - [[nodiscard]] unsigned int max_columns_per_pricing() const { return m_max_columns_per_pricing; } - - void set_non_optimal_pricing_phase(double t_time_limit, double t_relative_gap); -protected: - void hook_before_optimize() override; - void hook_optimize() override; - void add(const Var &t_var) override; - void add(const Ctr &t_ctr) override; - void remove(const Var &t_var) override; - void remove(const Ctr &t_ctr) override; - void update() override; - void write(const std::string &t_name) override; - - virtual void run_column_generation(); - virtual void add_artificial_variables(); - [[nodiscard]] virtual bool has_artificial_variable_in_basis() const; - virtual void solve_master_problem(); - void log_master_solution(bool t_force = false) const; - void log_subproblem_solution(const Subproblem& t_subproblem, bool t_force = false) const; - virtual void analyze_master_problem_solution(); - virtual void update_subproblems(); - virtual void solve_subproblems(); - virtual void analyze_subproblems_solution(); - virtual void enrich_master_problem(); - virtual void clean_up(); - virtual void remove_artificial_variables(); - [[nodiscard]] virtual bool stopping_condition() const; - void terminate_for_master_infeasible_with_artificial_variables(); - void set_phase_I_objective_function(); - void restore_objective_function(); - - virtual void set_objective(Expr&& t_objective); - virtual void set_objective(const Expr& t_objective); - - [[nodiscard]] double get_var_primal(const Var &t_var) const override; - [[nodiscard]] double get_var_ray(const Var &t_var) const override; - [[nodiscard]] double get_ctr_dual(const Ctr &t_ctr) const override; - [[nodiscard]] double get_ctr_farkas(const Ctr &t_ctr) const override; - [[nodiscard]] unsigned int get_n_solutions() const override; - [[nodiscard]] unsigned int get_solution_index() const override; - void set_solution_index(unsigned int t_index) override; - - void update_obj_sense() override; - void update_obj() override; - void update_rhs() override; - void update_obj_constant() override; - void update_mat_coeff(const Ctr &t_ctr, const Var &t_var) override; - void update_ctr_type(const Ctr &t_ctr) override; - void update_ctr_rhs(const Ctr &t_ctr) override; - void update_var_type(const Var &t_var) override; - void update_var_lb(const Var &t_var) override; - void update_var_ub(const Var &t_var) override; - void update_var_obj(const Var &t_var) override; - - std::unique_ptr m_master; - std::vector m_subproblems; - - double m_iter_lower_bound = -Inf; - double m_iter_upper_bound = +Inf; - unsigned int m_iteration_count = 0; - unsigned int m_n_generated_columns_at_last_iteration = 0; - unsigned int m_n_generated_columns = 0; - - std::list m_artificial_variables; - bool m_current_iteration_is_farkas_pricing = false; - - std::optional m_current_dual_solution; - std::optional m_adjusted_dual_solution; - - unsigned int m_parallel_pricing_limit = 1; - double m_clean_up_ratio = .75; - unsigned int m_clean_up_threshold = 1e4; - bool m_farkas_pricing = true; - double m_artificial_variables_cost = 1e6; - double m_smoothing_factor = 0.; - unsigned int m_log_frequency = 10; - unsigned int m_max_columns_per_pricing = 5; - std::optional> m_non_optimal_pricing_phase; -}; - -class idol::Optimizers::ArchivedColumnGeneration::Subproblem { - friend class ::idol::Optimizers::ArchivedColumnGeneration; - friend class ::idol::Optimizers::ArchivedDantzigWolfeDecomposition; - - using PresentGeneratorsList = std::list>; - - unsigned int m_index; - ArchivedColumnGeneration& m_parent; - std::unique_ptr m_model; - Column m_generation_pattern; - bool m_skip = false; - bool m_is_non_optimal_phase = false; - - GeneratorPool m_pool; - PresentGeneratorsList m_present_generators; - - void hook_before_optimize(); - - void update_objective(bool t_farkas_pricing, const Solution::Dual& t_duals); - - void optimize(); - - [[nodiscard]] double compute_reduced_cost(const Solution::Dual& t_duals) const; - - void enrich_master_problem(); - - [[nodiscard]] TempVar create_column_from_generator(const Solution::Primal& t_primals) const; - - void clean_up(); - - void remove_column_if(const std::function& t_indicator_for_removal); - - void update_generation_pattern_objective(Constant&& t_objective); -public: - Subproblem(ArchivedColumnGeneration& t_parent, unsigned int t_index, Model* t_model, Column&& t_generation_pattern); - - [[nodiscard]] auto present_generators() const { return ConstIteratorForward(m_present_generators); } - - [[nodiscard]] const auto& pool() const { return m_pool; } - - [[nodiscard]] const auto& model() const { return *m_model; } - - [[nodiscard]] unsigned int index() const { return m_index; } -}; - -#endif //IDOL_ARCHIVED_COLUMNGENERATION_H diff --git a/lib/include/idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h b/lib/include/idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h deleted file mode 100644 index 67b10d25..00000000 --- a/lib/include/idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h +++ /dev/null @@ -1,91 +0,0 @@ -// -// Created by henri on 24/03/23. -// - -#ifndef IDOL_ARCHIVEDDANTZIGWOLFEDECOMPOSITION_H -#define IDOL_ARCHIVEDDANTZIGWOLFEDECOMPOSITION_H - -#include "idol/optimizers/OptimizerFactory.h" -#include "idol/modeling/constraints/Ctr.h" -#include "idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h" - -namespace idol { - class ArchivedDantzigWolfeDecomposition; -} - -class idol::ArchivedDantzigWolfeDecomposition : public impl::OptimizerFactoryWithColumnGenerationParameters { - // Basic parameters - Annotation m_decomposition; - std::unique_ptr m_master_optimizer; - std::unique_ptr m_pricing_optimizer; - - // Branching parameters - std::optional m_branching_on_master; - - std::optional m_aggregation_type; - - ArchivedDantzigWolfeDecomposition(const ArchivedDantzigWolfeDecomposition& t_src); - - Annotation create_variable_flag(const Model& t_model, unsigned int *t_n_subproblem) const; - std::vector create_empty_subproblems(Env& t_env, unsigned int t_n_subproblems) const; - [[nodiscard]] std::vector create_empty_generation_patterns(unsigned int t_n_subproblems) const; - void dispatch_variables(const Annotation& t_variable_flag, - const Model& t_original_formulation, - Model* t_master, - const std::vector& t_subproblems) const; - void dispatch_constraints(const Annotation& t_variable_flag, - const Model& t_original_formulation, - Model* t_master, - const std::vector& t_subproblems, - std::vector& t_generation_patterns) const; - void dispatch_linking_constraint(const Annotation& t_variable_flag, - const Ctr& t_ctr, - const Row& t_row, - CtrType t_type, - Model* t_master, - const std::vector& t_subproblems, - std::vector& t_generation_patterns) const; - void dispatch_objective(const Annotation& t_variable_flag, - const Model& t_original_formulation, - Model* t_master, - std::vector& t_generation_patterns) const; - std::pair, std::vector> dispatch_linking_expression( - const Annotation& t_variable_flag, - const QuadExpr& t_qaud, - const LinExpr& t_lin, - Model* t_master, - unsigned int t_n_subproblems) const; - void add_convexity_constraints(Env& t_env, Model* t_master, std::vector& t_generation_patterns) const; -public: - explicit ArchivedDantzigWolfeDecomposition(const Annotation& t_decomposition); - - /** - * Configures the optimizer factory for the master problem - * @param t_master_optimizer the desired optimizer factory for the master problem - * @return the optimizer factory itself - */ - ArchivedDantzigWolfeDecomposition& with_master_optimizer(const OptimizerFactory& t_master_optimizer); - - /** - * Configures the optimizer factory for the pricing problems - * @param t_pricing_optimizer the desired optimizer factory for the pricing problems - * @return the optimizer factory itself - */ - ArchivedDantzigWolfeDecomposition& with_pricing_optimizer(const OptimizerFactory& t_pricing_optimizer); - - /** - * When set to true, branching is applied to the master problem. Otherwise, branching decisions are applied to the - * pricing problems. - * @param t_value the desired value - * @return the optimizer factory itself - */ - ArchivedDantzigWolfeDecomposition& with_branching_on_master(bool t_value); - - ArchivedDantzigWolfeDecomposition& with_aggregation_type(CtrType t_type); - - Optimizer *operator()(const Model &t_original_formulation) const override; - - [[nodiscard]] ArchivedDantzigWolfeDecomposition *clone() const override; -}; - -#endif //IDOL_ARCHIVEDDANTZIGWOLFEDECOMPOSITION_H diff --git a/lib/include/idol/optimizers/archive/dantzig-wolfe/Optimizers_ArchivedDantzigWolfeDecomposition.h b/lib/include/idol/optimizers/archive/dantzig-wolfe/Optimizers_ArchivedDantzigWolfeDecomposition.h deleted file mode 100644 index 7b14f5a4..00000000 --- a/lib/include/idol/optimizers/archive/dantzig-wolfe/Optimizers_ArchivedDantzigWolfeDecomposition.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// Created by henri on 24/03/23. -// - -#ifndef IDOL_OPTIMIZERS_ARCHIVEDDANTZIGWOLFEDECOMPOSITION_H -#define IDOL_OPTIMIZERS_ARCHIVEDDANTZIGWOLFEDECOMPOSITION_H - -#include "idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h" - -namespace idol::Optimizers { - class ArchivedDantzigWolfeDecomposition; -} - -class idol::Optimizers::ArchivedDantzigWolfeDecomposition : public Optimizers::ArchivedColumnGeneration { - std::unique_ptr m_pricing_optimizer; - - Annotation m_ctr_annotation; - Annotation m_var_annotation; - - bool m_branching_on_master = true; - - Map m_lower_bound_constraints; - Map m_upper_bound_constraints; -public: - ArchivedDantzigWolfeDecomposition(const Model& t_original_formulation, - const Annotation& t_constraint_flag, - const Annotation& t_variable_flag, - Model *t_master_problem, - const std::vector &t_subproblems, - std::vector t_generation_patterns, - const OptimizerFactory& t_pricing_optimizer); - - [[nodiscard]] std::string name() const override { return "dantzig-wolfe"; } - - virtual void set_branching_on_master(bool t_value) { m_branching_on_master = t_value; } - - [[nodiscard]] bool branching_on_master() const { return m_branching_on_master; } - - [[nodiscard]] const Annotation& ctr_annotation() const { return m_ctr_annotation; }; - - [[nodiscard]] const Annotation& var_annotation() const { return m_var_annotation; }; -protected: - void set_objective(Expr &&t_objective) override; - - [[nodiscard]] double get_var_primal(const Var &t_var) const override; - - void update_var_lb(const Var &t_var) override; - void update_var_ub(const Var &t_var) override; - - [[nodiscard]] double get_subproblem_primal_value(const Var& t_var, unsigned int t_subproblem_id) const; - void set_subproblem_lower_bound(const Var& t_var, unsigned int t_subproblem_id, double t_value); - void set_subproblem_upper_bound(const Var& t_var, unsigned int t_subproblem_id, double t_value); - void apply_subproblem_bound_on_master(bool t_is_lb, const Var &t_var, unsigned int t_subproblem_id, double t_value); - - LinExpr expand_subproblem_variable(const Var& t_var, unsigned int t_subproblem_id); - - void add(const Var &t_var) override; - - void add(const Ctr &t_ctr) override; -}; - -#endif //IDOL_OPTIMIZERS_ARCHIVEDDANTZIGWOLFEDECOMPOSITION_H diff --git a/lib/src/optimizers/archive/column-generation/ColumnGeneration.cpp b/lib/src/optimizers/archive/column-generation/ColumnGeneration.cpp deleted file mode 100644 index 1aa57bf0..00000000 --- a/lib/src/optimizers/archive/column-generation/ColumnGeneration.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Created by henri on 24/03/23. -// -#include "idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h" -#include "idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h" - -idol::Optimizer *idol::ArchivedColumnGeneration::operator()(const Model &t_model) const { - - if (!m_master_optimizer) { - throw Exception("No optimizer have been given for master problem."); - } - - const unsigned int n_subproblems = this->n_subproblems(); - - std::vector subproblems(n_subproblems); - - for (unsigned int i = 0 ; i < n_subproblems ; ++i) { - subproblems[i] = m_subproblems[i]->clone(); - } - - auto* master = t_model.clone(); - master->use(*m_master_optimizer); - - auto* result = new Optimizers::ArchivedColumnGeneration(t_model, master, subproblems, m_generation_patterns); - - this->handle_default_parameters(result); - this->handle_column_generation_parameters(result); - - return result; -} - -idol::ArchivedColumnGeneration *idol::ArchivedColumnGeneration::clone() const { - return new ArchivedColumnGeneration(*this); -} - -idol::ArchivedColumnGeneration::ArchivedColumnGeneration(unsigned int t_n_subproblems){ - - m_subproblems.reserve(t_n_subproblems); - -} - -idol::ArchivedColumnGeneration::ArchivedColumnGeneration(const ArchivedColumnGeneration &t_src) - : impl::OptimizerFactoryWithColumnGenerationParameters(t_src), - m_master_optimizer(t_src.m_master_optimizer ? t_src.m_master_optimizer->clone() : nullptr), - m_subproblems(t_src.m_subproblems), - m_generation_patterns(t_src.m_generation_patterns) { - -} - -idol::ArchivedColumnGeneration& idol::ArchivedColumnGeneration::reserve_for_subproblems(unsigned int t_n_subproblems) { - m_subproblems.reserve(t_n_subproblems); - m_generation_patterns.reserve(t_n_subproblems); - return *this; -} - -idol::ArchivedColumnGeneration &idol::ArchivedColumnGeneration::with_subproblem(const Model &t_model, Column t_column) { - m_subproblems.emplace_back(&t_model); - m_generation_patterns.emplace_back(std::move(t_column)); - return *this; -} - -idol::ArchivedColumnGeneration &idol::ArchivedColumnGeneration::with_master_optimizer(const OptimizerFactory &t_master_optimizer) { - - if (m_master_optimizer) { - throw Exception("A master solver has already been given."); - } - - m_master_optimizer.reset(t_master_optimizer.clone()); - - return *this; -} diff --git a/lib/src/optimizers/archive/column-generation/Optimizers_ColumnGeneration.cpp b/lib/src/optimizers/archive/column-generation/Optimizers_ColumnGeneration.cpp deleted file mode 100644 index f70fa655..00000000 --- a/lib/src/optimizers/archive/column-generation/Optimizers_ColumnGeneration.cpp +++ /dev/null @@ -1,891 +0,0 @@ -// -// Created by henri on 24/03/23. -// -#include "idol/optimizers/archive/column-generation/Optimizers_ColumnGeneration.h" -#include "idol/optimizers/Logger.h" -#include "idol/modeling//expressions/operations/operators.h" - -idol::Optimizers::ArchivedColumnGeneration::ArchivedColumnGeneration(const Model& t_model, - Model *t_master_problem, - const std::vector &t_subproblems, - std::vector t_generation_patterns) - : Algorithm(t_model), - m_master(t_master_problem) { - - const unsigned int n_subproblems = t_subproblems.size(); - - m_subproblems.reserve(t_subproblems.capacity()); - - for (unsigned int i = 0 ; i < n_subproblems ; ++i) { - add_subproblem(t_subproblems[i], std::move(t_generation_patterns[i])); - } - - -} - -void idol::Optimizers::ArchivedColumnGeneration::add(const Var &t_var) { - m_master->add(t_var); -} - -void idol::Optimizers::ArchivedColumnGeneration::add(const Ctr &t_ctr) { - m_master->add(t_ctr); -} - -void idol::Optimizers::ArchivedColumnGeneration::remove(const Var &t_var) { - m_master->remove(t_var); -} - -void idol::Optimizers::ArchivedColumnGeneration::remove(const Ctr &t_ctr) { - m_master->remove(t_ctr); -} - -void idol::Optimizers::ArchivedColumnGeneration::update() { - m_master->update(); -} - -void idol::Optimizers::ArchivedColumnGeneration::write(const std::string &t_name) { - m_master->write(t_name); -} - -void idol::Optimizers::ArchivedColumnGeneration::hook_before_optimize() { - - set_best_bound(-Inf); - set_best_obj(+Inf); - m_iter_lower_bound = -Inf; - m_iter_upper_bound = +Inf; - m_iteration_count = 0; - m_n_generated_columns_at_last_iteration = 0; - m_n_generated_columns = 0; - m_current_dual_solution.reset(); - m_adjusted_dual_solution.reset(); - m_current_iteration_is_farkas_pricing = false; - - if (m_farkas_pricing && !m_master->optimizer().get_param_infeasible_or_unbounded_info()) { - m_master->optimizer().set_param_infeasible_or_unbounded_info(true); - } - - for (auto& subproblem : m_subproblems) { - subproblem.hook_before_optimize(); - } - - Algorithm::hook_before_optimize(); -} - -void idol::Optimizers::ArchivedColumnGeneration::add_artificial_variables() { - - auto& env = m_master->env(); - const auto add_to = [&](const Ctr& t_ctr, double t_sign) { - - Column column(m_artificial_variables_cost); - column.linear().set(t_ctr, t_sign); - - Var artifical_var(env, 0., Inf, Continuous, std::move(column)); - m_master->add(artifical_var); - - m_artificial_variables.emplace_back(artifical_var); - - }; - - for (const auto& ctr : m_master->ctrs()) { - - const auto type = m_master->get_ctr_type(ctr); - - if (type == LessOrEqual) { - add_to(ctr, -1); - } else if (type == GreaterOrEqual) { - add_to(ctr, 1); - } else { - add_to(ctr, -1); - add_to(ctr, 1); - } - - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::hook_optimize() { - - if (m_farkas_pricing) { - run_column_generation(); - return; - } - - add_artificial_variables(); - - // min c^Tx + M^Ts - if (m_artificial_variables_cost > 0) { - - run_column_generation(); - - if (m_artificial_variables.empty()) { - return; - } - - if (get_status() == Infeasible) { - remove_artificial_variables(); - terminate_for_master_infeasible_with_artificial_variables(); - return; - - } - - } - - // min e^Ts - idol_Log(Debug, "Current status is " << get_status() << ", switching to pure phase I.") - - set_phase_I_objective_function(); - - restart(); - hook_before_optimize(); - run_column_generation(); - - remove_artificial_variables(); - - if (get_status() != Optimal) { - return; - } - - restore_objective_function(); - - restart(); - hook_before_optimize(); - run_column_generation(); - -} - -void idol::Optimizers::ArchivedColumnGeneration::set_phase_I_objective_function() { - - Expr objective; - for (const auto& var : m_artificial_variables) { - objective += var; - } - - set_objective(std::move(objective)); - -} - -void idol::Optimizers::ArchivedColumnGeneration::restore_objective_function() { - set_objective(parent().get_obj_expr()); -} - -bool idol::Optimizers::ArchivedColumnGeneration::has_artificial_variable_in_basis() const { - - std::function value; - - if (get_status() == Unbounded) { - value = [this](const Var& t_var) { return m_master->get_var_ray(t_var); }; - } else { - value = [this](const Var& t_var) { return m_master->get_var_primal(t_var); }; - } - - for (const auto& var : m_artificial_variables) { - if (!equals(value(var), 0, 1e-8)) { - return true; - } - } - - return false; - -} - -void idol::Optimizers::ArchivedColumnGeneration::run_column_generation() { - do { - - if (m_n_generated_columns_at_last_iteration > 0 || m_iteration_count == 0) { - - idol_Log(Trace, "Beginning to solve master problem."); - solve_master_problem(); - idol_Log(Trace, "Master problem has been solved."); - - analyze_master_problem_solution(); - - log_master_solution(); - - } else if (m_current_iteration_is_farkas_pricing) { - - set_status(Infeasible); - set_reason(Proved); - terminate(); - - } - - if (is_terminated() || stopping_condition()) { break; } - - update_subproblems(); - - solve_subproblems(); - - analyze_subproblems_solution(); - - if (is_terminated() || stopping_condition()) { break; } - - clean_up(); - - enrich_master_problem(); - - ++m_iteration_count; - - if (m_iteration_count >= get_param_iteration_limit()) { - set_reason(IterLimit); - terminate(); - break; - } - - } while (true); - - if (get_relative_gap() <= get_tol_mip_relative_gap()) { - set_status(Optimal); - set_reason(Proved); - } - - log_master_solution(true); - -} - -void idol::Optimizers::ArchivedColumnGeneration::solve_master_problem() { - m_master->optimize(); -} - -void idol::Optimizers::ArchivedColumnGeneration::log_master_solution(bool t_force) const { - - if (!t_force && m_iteration_count % m_log_frequency != 0) { - return; - } - - auto status = m_master->get_status(); - - std::stringstream objective_value; - if (status == InfOrUnbnd || status == Fail || status == Loaded) { - objective_value << "-"; - } else { - objective_value << m_master->get_best_obj(); - } - - idol_Log(Info, - " " - << " " - << " " - << "optimizer().time().count() << "> " - << " " - << "get_reason() << "> " - << " " - << " " - << " " - << " " - << " " - << " " - ); - -} - -void idol::Optimizers::ArchivedColumnGeneration::log_subproblem_solution(const Optimizers::ArchivedColumnGeneration::Subproblem &t_subproblem, - bool t_force) const { - - if (!t_force && m_iteration_count % m_log_frequency != 0) { - return; - } - - const auto& pricing = t_subproblem.m_model; - - idol_Log(Info, - " " - << " " - << " " - << "optimizer().time().count() << "> " - << "get_status() << "> " - << "get_reason() << "> " - << "get_best_obj() << "> " - << " " - << " " - << " " - << " " - << " " - ); - -} - -void idol::Optimizers::ArchivedColumnGeneration::analyze_master_problem_solution() { - - auto status = m_master->get_status(); - - if (status == Optimal) { - - set_status(Feasible); - - m_iter_upper_bound = m_master->get_best_obj(); - - set_best_obj(std::min(m_iter_upper_bound, get_best_obj())); - - m_current_dual_solution = save_dual(*m_master); - - if (m_current_iteration_is_farkas_pricing) { - m_adjusted_dual_solution.reset(); - } - - if (!m_artificial_variables.empty() && !has_artificial_variable_in_basis()) { - idol_Log(Trace, "Master problem is feasible, removing artificial variables."); - remove_artificial_variables(); - } - - m_current_iteration_is_farkas_pricing = false; - - return; - } - - if (status == Infeasible) { - - if (!m_farkas_pricing) { - set_status(Infeasible); - set_reason(Proved); - terminate(); - return; - } - - m_current_iteration_is_farkas_pricing = true; - m_current_dual_solution = save_farkas(*m_master); - m_adjusted_dual_solution.reset(); - - return; - } - - if (status == Unbounded) { - - set_reason(Proved); - idol_Log(Trace, "Terminate. Unbounded master problem."); - terminate(); - return; - - } - - set_status(status); - set_reason(NotSpecified); - idol_Log(Trace, "Terminate. Master problem could not be solved to optimality (status = " << status << ")."); - terminate(); - -} - -void idol::Optimizers::ArchivedColumnGeneration::update_subproblems() { - - if (!m_adjusted_dual_solution) { - - m_adjusted_dual_solution = m_current_dual_solution.value(); - - } else { - - if (m_smoothing_factor == 0.) { - m_adjusted_dual_solution = m_current_dual_solution.value(); - } else { - m_adjusted_dual_solution = m_smoothing_factor * m_adjusted_dual_solution.value() + (1. - m_smoothing_factor) * m_current_dual_solution.value(); - } - - } - - for (auto& subproblem : m_subproblems) { - subproblem.update_objective(m_current_iteration_is_farkas_pricing, m_adjusted_dual_solution.value()); - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::solve_subproblems() { - - const unsigned int n_threads = std::min(get_param_threads(), m_parallel_pricing_limit); - -#pragma omp parallel for num_threads(n_threads) default(none) - for (auto& subproblem : m_subproblems) { - - if (subproblem.m_skip) { - idol_Log(Trace, "Skipping subproblem " << subproblem.m_index << ".") - continue; - } - - idol_Log(Trace, "Beginning to solve subproblem " << subproblem.m_index << ".") - subproblem.optimize(); - idol_Log(Trace, "Subproblem " << subproblem.m_index << " has been solved.") - } - - for (auto& subproblem : m_subproblems) { - - if (subproblem.m_skip) { - continue; - } - - log_subproblem_solution(subproblem); - - if (is_terminated()) { break; } - - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::analyze_subproblems_solution() { - - double reduced_costs = 0; - bool some_subproblems_had_been_skipped = false; - bool all_subproblems_were_optimally_solved = true; - - for (auto& subproblem : m_subproblems) { - - if (subproblem.m_skip) { - some_subproblems_had_been_skipped = true; - } - - const auto status = subproblem.m_model->get_status(); - - - if (status == Feasible || subproblem.m_is_non_optimal_phase) { - - all_subproblems_were_optimally_solved = false; - - } else if (status == Optimal) { - - const double pricing_optimum = subproblem.m_model->get_best_obj(); - reduced_costs += std::min(0., pricing_optimum); - - } else { - set_status(status); - set_reason(Proved); - terminate(); - return; - } - - } - - if (some_subproblems_had_been_skipped) { - return; - } - - if (!all_subproblems_were_optimally_solved) { - return; - } - - if (!m_current_iteration_is_farkas_pricing) { - - const double lower_bound = m_master->get_best_obj() + reduced_costs; - - m_iter_lower_bound = lower_bound; - - if (get_best_bound() <= lower_bound) { - set_best_bound(std::min(lower_bound, get_best_obj())); - } - - if (m_artificial_variables.empty() && get_best_bound() > get_param_best_bound_stop()) { - set_status(get_relative_gap() > 0 ? Feasible : Optimal); - set_reason(ObjLimit); - terminate(); - idol_Log(Trace, - "Terminate. Given get_param_best_bound_stop is " << get_param_best_bound_stop() - << " while current best bound is " << get_best_bound() - ) - } - - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::enrich_master_problem() { - - m_n_generated_columns_at_last_iteration = 0; - - for (auto& subproblem : m_subproblems) { - - bool can_enrich_master; - - // TODO add rules for adding columns - - if (m_current_iteration_is_farkas_pricing) { - can_enrich_master = subproblem.m_model->get_best_obj() < -1e-4; - } else { - can_enrich_master = subproblem.compute_reduced_cost(m_current_dual_solution.value()) < 0; - } - - if (can_enrich_master) { - subproblem.enrich_master_problem(); - } - - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::clean_up() { - - for (auto& subproblem : m_subproblems) { - subproblem.clean_up(); - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::remove_artificial_variables() { - - for (const Var& var : m_artificial_variables) { - m_master->remove(var); - } - - m_artificial_variables.clear(); -} - -bool idol::Optimizers::ArchivedColumnGeneration::stopping_condition() const { - return get_absolute_gap() <= Tolerance::MIPAbsoluteGap - || get_relative_gap() <= Tolerance::MIPRelativeGap - || get_remaining_time() == 0; -} - -idol::Optimizers::ArchivedColumnGeneration::Subproblem& idol::Optimizers::ArchivedColumnGeneration::add_subproblem(Model *t_sub_problem_model, Column t_generation_pattern) { - m_subproblems.emplace_back(*this, m_subproblems.size(), t_sub_problem_model, std::move(t_generation_pattern)); - return m_subproblems.back(); -} - -void idol::Optimizers::ArchivedColumnGeneration::terminate_for_master_infeasible_with_artificial_variables() { - - idol_Log(Fatal, "Master problem should not be infeasible when using artificial variables."); - set_status(Fail); - set_reason(NotSpecified); - terminate(); - -} - -double idol::Optimizers::ArchivedColumnGeneration::get_var_primal(const Var &t_var) const { - return m_master->get_var_primal(t_var); -} - -double idol::Optimizers::ArchivedColumnGeneration::get_var_ray(const Var &t_var) const { - return m_master->get_var_ray(t_var); -} - -double idol::Optimizers::ArchivedColumnGeneration::get_ctr_dual(const Ctr &t_ctr) const { - return m_master->get_ctr_dual(t_ctr); -} - -double idol::Optimizers::ArchivedColumnGeneration::get_ctr_farkas(const Ctr &t_ctr) const { - return m_master->get_ctr_farkas(t_ctr); -} - -void idol::Optimizers::ArchivedColumnGeneration::set_objective(Expr &&t_objective) { - m_master->set_obj_expr(std::move(t_objective)); -} - -void idol::Optimizers::ArchivedColumnGeneration::set_objective(const Expr &t_objective) { - set_objective(Expr(t_objective)); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_obj_sense() { - throw Exception("Not implemented"); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_obj() { - set_objective(parent().get_obj_expr()); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_rhs() { - m_master->set_rhs_expr(parent().get_rhs_expr()); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_obj_constant() { - m_master->set_obj_const(parent().get_obj_expr().constant()); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_mat_coeff(const Ctr &t_ctr, const Var &t_var) { - m_master->set_mat_coeff(t_ctr, t_var, parent().get_mat_coeff(t_ctr, t_var)); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_ctr_type(const Ctr &t_ctr) { - m_master->set_ctr_type(t_ctr, parent().get_ctr_type(t_ctr)); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_ctr_rhs(const Ctr &t_ctr) { - m_master->set_ctr_rhs(t_ctr, parent().get_ctr_row(t_ctr).rhs()); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_var_type(const Var &t_var) { - m_master->set_var_type(t_var, parent().get_var_type(t_var)); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_var_lb(const Var &t_var) { - m_master->set_var_lb(t_var, parent().get_var_lb(t_var)); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_var_ub(const Var &t_var) { - m_master->set_var_ub(t_var, parent().get_var_ub(t_var)); -} - -void idol::Optimizers::ArchivedColumnGeneration::update_var_obj(const Var &t_var) { - m_master->set_var_obj(t_var, parent().get_var_column(t_var).obj()); -} - -unsigned int idol::Optimizers::ArchivedColumnGeneration::get_n_solutions() const { - return 1; -} - -unsigned int idol::Optimizers::ArchivedColumnGeneration::get_solution_index() const { - return 0; -} - -void idol::Optimizers::ArchivedColumnGeneration::set_solution_index(unsigned int t_index) { - if (t_index != 0) { - throw Exception("Solution index out of bounds."); - } -} - -void idol::Optimizers::ArchivedColumnGeneration::set_non_optimal_pricing_phase(double t_time_limit, double t_relative_gap) { - m_non_optimal_pricing_phase = { t_time_limit, t_relative_gap }; -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::hook_before_optimize() { - m_skip = false; - m_is_non_optimal_phase = m_parent.m_non_optimal_pricing_phase.has_value(); -} - -idol::Optimizers::ArchivedColumnGeneration::Subproblem::Subproblem(Optimizers::ArchivedColumnGeneration &t_parent, - unsigned int t_index, - Model* t_model, - Column&& t_generation_pattern) - : m_parent(t_parent), - m_index(t_index), - m_model(t_model), - m_generation_pattern(std::move(t_generation_pattern)) - - { - -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::update_objective(bool t_farkas_pricing, const Solution::Dual &t_duals) { - - ::idol::Expr objective; - - for (const auto &[ctr, constant] : m_generation_pattern.linear()) { - objective += constant.numerical() * -t_duals.get(ctr); - for (const auto &[param, coeff] : constant.linear()) { - const double cost = -t_duals.get(ctr) * coeff; - if (!equals(cost, 0., Tolerance::Sparsity)) { - objective += cost * param.as(); - } - } - for (const auto &[pair, coeff] : constant.quadratic()) { - const double cost = -t_duals.get(ctr) * coeff; - if (!equals(cost, 0., Tolerance::Sparsity)) { - objective += cost * pair.first.as() * pair.second.as(); - } - } - } - - if (!t_farkas_pricing) { - for (const auto &[param, coeff] : m_generation_pattern.obj().linear()) { - if (!equals(coeff, 0., Tolerance::Sparsity)) { - objective += coeff * param.as(); - } - } - for (const auto &[pair, coeff] : m_generation_pattern.obj().quadratic()) { - if (!equals(coeff, 0., Tolerance::Sparsity)) { - objective += coeff * pair.first.as() * pair.second.as(); - } - } - } - - m_model->set_obj_expr(std::move(objective)); - -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::optimize() { - - if (!m_parent.m_current_iteration_is_farkas_pricing && m_is_non_optimal_phase) { - - std::cout << "non-optimal pricing phase" << std::endl; - - const auto [time_limit, relative_gap] = m_parent.m_non_optimal_pricing_phase.value(); - const auto duals = save_dual(*m_parent.m_master); - - m_model->optimizer().set_tol_mip_relative_gap(relative_gap); - m_model->optimizer().set_param_time_limit(std::min(m_parent.get_remaining_time(), time_limit)); - m_model->optimize(); - - if (compute_reduced_cost(duals) < 0) { - return; - } - - std::cout << "continuing with time limit" << std::endl; - - m_model->optimizer().set_param_time_limit(std::min(m_parent.get_remaining_time(), 2 * time_limit)); - m_model->optimize(); - - if (compute_reduced_cost(duals) < 0) { - return; - } - - std::cout << "switching to exact pricing, current red. cost is " << compute_reduced_cost(duals) << std::endl; - m_is_non_optimal_phase = false; - - } - - m_model->optimizer().set_tol_mip_relative_gap(1e-4); - m_model->optimizer().set_param_time_limit(m_parent.get_remaining_time()); - m_model->optimize(); - -} - -double idol::Optimizers::ArchivedColumnGeneration::Subproblem::compute_reduced_cost(const Solution::Dual &t_duals) const { - - double result = 0.; - - const auto& primals = save_primal(*m_model); - - for (const auto &[ctr, constant] : m_generation_pattern.linear()) { - result += constant.numerical() * -t_duals.get(ctr); - for (const auto &[param, coeff] : constant.linear()) { - result += -t_duals.get(ctr) * coeff * primals.get(param.as()); - } - for (const auto &[pair, coeff] : constant.quadratic()) { - result += -t_duals.get(ctr) * coeff * primals.get(pair.first.as()) * primals.get(pair.second.as()); - } - } - - for (const auto &[param, coeff] : m_generation_pattern.obj().linear()) { - result += coeff * primals.get(param.as()); - } - - for (const auto &[pair, coeff] : m_generation_pattern.obj().quadratic()) { - result += coeff * primals.get(pair.first.as()) * primals.get(pair.second.as()); - } - - return result; -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::enrich_master_problem() { - - auto& env = m_model->env(); - const unsigned int n_solutions = m_model->get_n_solutions(); - const unsigned int max_columns_added = m_parent.m_max_columns_per_pricing; - - bool at_least_one_column_was_added = false; - - try { - for (unsigned int k = 0; k < n_solutions && k < max_columns_added; ++k) { - - m_model->set_solution_index(k); - - const int status = m_model->get_status(); - - Solution::Primal generator; - if (status == Unbounded) { - generator = save_ray(*m_model); - } else { - generator = save_primal(*m_model); - } - - auto column = create_column_from_generator(generator); - - Var alpha(env, - 0., - Inf, - Continuous); - - auto &master = *m_parent.m_master; - - master.add(alpha, std::move(column)); - m_pool.add(alpha, std::move(generator)); - m_present_generators.emplace_back(alpha, m_pool.last_inserted()); - - ++m_parent.m_n_generated_columns_at_last_iteration; - ++m_parent.m_n_generated_columns; - - at_least_one_column_was_added = true; - } - - } catch (...) { - if (!at_least_one_column_was_added) { - __throw_exception_again; - } - std::cout << "Warning: exception was thrown while adding more than one column" << std::endl; - } - - // m_parent.m_master->write("master.lp"); -} - -idol::TempVar -idol::Optimizers::ArchivedColumnGeneration::Subproblem::create_column_from_generator(const Solution::Primal &t_primals) const { - return { - 0., - Inf, - Continuous, - m_generation_pattern.fix(t_primals) - }; - -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::clean_up() { - - if (m_pool.size() < m_parent.m_clean_up_threshold) { - return; - } - - auto& master = *m_parent.m_master; - const double ratio = m_parent.m_clean_up_ratio; - const auto n_to_remove = (unsigned int) (m_pool.size() * (1 - ratio)); - unsigned int n_removed = 0; - - m_present_generators.clear(); - - for (auto it = m_pool.values().begin(), end = m_pool.values().end() ; it != end ; ) { - - const bool is_already_in_master = master.has(it->first); - const bool done_removing = n_removed >= n_to_remove; - - if (done_removing) { - - if (is_already_in_master) { - m_present_generators.emplace_back(it->first, it->second); - } - - ++it; - continue; - - } - - if (is_already_in_master) { - - if (master.get_var_primal(it->first) > 0) { - - m_present_generators.emplace_back(it->first, it->second); - ++it; - continue; - - } - - master.remove(it->first); - - } - - it = m_pool.erase(it); - ++n_removed; - - } - - -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::remove_column_if(const std::function &t_indicator_for_removal) { - - auto& master = m_parent.m_master; - - auto it = m_present_generators.begin(); - const auto end = m_present_generators.end(); - - while (it != end) { - const auto& [column_variable, ptr_to_column] = *it; - if (t_indicator_for_removal(column_variable, ptr_to_column)) { - master->remove(column_variable); - it = m_present_generators.erase(it); - } else { - ++it; - } - } - -} - -void idol::Optimizers::ArchivedColumnGeneration::Subproblem::update_generation_pattern_objective(Constant &&t_objective) { - - for (const auto& [var, generator] : m_present_generators) { - m_parent.m_master->set_var_obj(var, t_objective.fix(generator)); - } - - m_generation_pattern.set_obj(std::move(t_objective)); - -} diff --git a/lib/src/optimizers/archive/dantzig-wolfe/DantzigWolfeDecomposition.cpp b/lib/src/optimizers/archive/dantzig-wolfe/DantzigWolfeDecomposition.cpp deleted file mode 100644 index d7cdc792..00000000 --- a/lib/src/optimizers/archive/dantzig-wolfe/DantzigWolfeDecomposition.cpp +++ /dev/null @@ -1,346 +0,0 @@ -// -// Created by henri on 24/03/23. -// -#include "idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h" -#include "idol/modeling//models/Model.h" -#include "idol/modeling//expressions/operations/operators.h" -#include "idol/modeling//objects/Versions.h" -#include "idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h" -#include "idol/optimizers/archive/dantzig-wolfe/Optimizers_ArchivedDantzigWolfeDecomposition.h" - -idol::ArchivedDantzigWolfeDecomposition::ArchivedDantzigWolfeDecomposition(const Annotation &t_decomposition) - : m_decomposition(t_decomposition) { - -} - -idol::ArchivedDantzigWolfeDecomposition::ArchivedDantzigWolfeDecomposition(const ArchivedDantzigWolfeDecomposition& t_src) - : impl::OptimizerFactoryWithColumnGenerationParameters(t_src), - m_decomposition(t_src.m_decomposition), - m_master_optimizer(t_src.m_master_optimizer ? t_src.m_master_optimizer->clone() : nullptr), - m_pricing_optimizer(t_src.m_pricing_optimizer ? t_src.m_pricing_optimizer->clone() : nullptr), - m_branching_on_master(t_src.m_branching_on_master), - m_aggregation_type(t_src.m_aggregation_type) { - -} - -idol::Optimizer *idol::ArchivedDantzigWolfeDecomposition::operator()(const Model &t_original_formulation) const { - - if (!m_master_optimizer) { - throw Exception("No master solver has been given, please call DantzigWolfeDecomposition::with_master_optimizer to configure."); - } - - if (!m_pricing_optimizer) { - throw Exception("No pricing solver has been given, please call DantzigWolfeDecomposition::with_pricing_optimizer to configure."); - } - - auto& env = t_original_formulation.env(); - - unsigned int n_subproblems; - auto* master = new Model(env); - - auto variable_flag = create_variable_flag(t_original_formulation, &n_subproblems); - - auto subproblems = create_empty_subproblems(env, n_subproblems); - auto generation_patterns = create_empty_generation_patterns(n_subproblems); - - dispatch_variables(variable_flag, t_original_formulation, master, subproblems); - dispatch_constraints(variable_flag, t_original_formulation, master, subproblems, generation_patterns); - dispatch_objective(variable_flag, t_original_formulation, master, generation_patterns); - - add_convexity_constraints(env, master, generation_patterns); - - master->use(*m_master_optimizer); - - for (unsigned int i = 0 ; i < n_subproblems ; ++i) { - subproblems[i]->use(*m_pricing_optimizer); - } - - auto* result = new Optimizers::ArchivedDantzigWolfeDecomposition(t_original_formulation, - m_decomposition, - variable_flag, - master, - subproblems, - generation_patterns, - *m_pricing_optimizer); - - this->handle_default_parameters(result); - this->handle_column_generation_parameters(result); - - if (m_branching_on_master.has_value()) { - result->set_branching_on_master(m_branching_on_master.value()); - } - - return result; -} - -idol::ArchivedDantzigWolfeDecomposition *idol::ArchivedDantzigWolfeDecomposition::clone() const { - return new ArchivedDantzigWolfeDecomposition(*this); -} - -idol::Annotation idol::ArchivedDantzigWolfeDecomposition::create_variable_flag(const Model &t_model, unsigned int *t_n_subproblem) const { - - Annotation result(t_model.env(), "variable_flag", MasterId); - - *t_n_subproblem = 0; - - const auto set_flag = [&result](const Var& t_var, unsigned int t_subproblem_id) { - - if (t_subproblem_id == MasterId) { return; } - - const unsigned int current_subproblem_id = t_var.get(result); - - if (current_subproblem_id != MasterId && current_subproblem_id != t_subproblem_id) { - throw Exception("Infeasible decomposition given."); - } - - t_var.set(result, t_subproblem_id); - }; - - bool has_at_least_one_subproblem = false; - - for (const auto& ctr : t_model.ctrs()) { - - const unsigned int subproblem_id = ctr.get(m_decomposition); - - if (subproblem_id != MasterId && subproblem_id >= *t_n_subproblem) { - has_at_least_one_subproblem = true; - *t_n_subproblem = subproblem_id; - } - - const auto& row = t_model.get_ctr_row(ctr); - - for (const auto& [var, constant] : row.linear()) { - set_flag(var, subproblem_id); - } - - for (const auto& [var1, var2, constant] : row.quadratic()) { - set_flag(var1, subproblem_id); - set_flag(var2, subproblem_id); - } - - } - - if (has_at_least_one_subproblem) { - *t_n_subproblem += 1; - } - - return result; -} - -std::vector -idol::ArchivedDantzigWolfeDecomposition::create_empty_subproblems(Env& t_env, unsigned int t_n_subproblems) const { - - std::vector result; - result.reserve(t_n_subproblems); - - for (unsigned int i = 0 ; i < t_n_subproblems ; ++i) { - result.emplace_back(new Model(t_env)); - } - - return result; -} - -std::vector -idol::ArchivedDantzigWolfeDecomposition::create_empty_generation_patterns(unsigned int t_n_subproblems) const { - return std::vector(t_n_subproblems); -} - -void idol::ArchivedDantzigWolfeDecomposition::dispatch_variables(const Annotation &t_variable_flag, - const Model& t_original_formulation, - Model *t_master, - const std::vector &t_subproblems) const { - - for (const auto& var : t_original_formulation.vars()) { - - const unsigned int subproblem_id = var.get(t_variable_flag); - const double lb = t_original_formulation.get_var_lb(var); - const double ub = t_original_formulation.get_var_ub(var); - const VarType type = t_original_formulation.get_var_type(var); - - auto* model = subproblem_id == MasterId ? t_master : t_subproblems[subproblem_id]; - - if (!model->has(var)) { - model->add(var, TempVar(lb, ub, type, Column())); - } - - } - -} - -void idol::ArchivedDantzigWolfeDecomposition::dispatch_constraints(const Annotation &t_variable_flag, - const Model &t_original_formulation, - Model *t_master, - const std::vector &t_subproblems, - std::vector& t_generation_patterns) const { - - if (t_subproblems.empty()) { - return; - } - - for (const auto& ctr : t_original_formulation.ctrs()) { - - const unsigned int subproblem_id = ctr.get(m_decomposition); - const auto& row = t_original_formulation.get_ctr_row(ctr); - const auto type = t_original_formulation.get_ctr_type(ctr); - - if (subproblem_id == MasterId) { - dispatch_linking_constraint(t_variable_flag, ctr, row, type, t_master, t_subproblems, t_generation_patterns); - } else { - t_subproblems[subproblem_id]->add(ctr, TempCtr(Row(row), type)); - } - - } - -} - -void idol::ArchivedDantzigWolfeDecomposition::dispatch_linking_constraint(const Annotation &t_variable_flag, - const Ctr& t_ctr, - const Row &t_row, - CtrType t_type, - Model *t_master, - const std::vector &t_subproblems, - std::vector &t_generation_patterns) const { - - const unsigned int n_subproblems = t_subproblems.size(); - - auto [master_lhs, subproblem_patterns] = dispatch_linking_expression(t_variable_flag, t_row.quadratic(), t_row.linear(), t_master, n_subproblems); - - t_master->add(t_ctr, TempCtr(Row(std::move(master_lhs), t_row.rhs()), t_type)); - - for (unsigned int i = 0 ; i < n_subproblems ; ++i) { - t_generation_patterns[i].linear().set(t_ctr, std::move(subproblem_patterns[i])); - } - -} - -void idol::ArchivedDantzigWolfeDecomposition::dispatch_objective(const Annotation &t_variable_flag, - const Model &t_original_formulation, Model *t_master, - std::vector &t_generation_patterns) const { - - const unsigned int n_subproblems = t_generation_patterns.size(); - - const auto& objective = t_original_formulation.get_obj_expr(); - - auto [master_obj, subproblem_patterns] = dispatch_linking_expression(t_variable_flag, objective.quadratic(), objective.linear(), t_master, n_subproblems); - - master_obj += objective.constant(); - - t_master->set_obj_expr(std::move(master_obj)); - - for (unsigned int i = 0 ; i < n_subproblems ; ++i) { - t_generation_patterns[i].set_obj(std::move(subproblem_patterns[i])); - } - -} - -std::pair, std::vector> -idol::ArchivedDantzigWolfeDecomposition::dispatch_linking_expression(const Annotation &t_variable_flag, - const QuadExpr &t_quad, - const LinExpr &t_lin, - Model *t_master, - unsigned int t_n_subproblems) const { - - Expr master_lhs; - std::vector subproblem_patterns(t_n_subproblems); - - for (const auto& [var, constant] : t_lin) { - - const unsigned int subproblem_id = var.get(t_variable_flag); - - if (subproblem_id == MasterId) { - master_lhs += constant * var; - continue; - } - - if (!constant.is_numerical()) { - throw Exception("Cannot have linking expression with subproblem's variable associated to a parametrized coefficient."); - } - - subproblem_patterns[subproblem_id] += constant.numerical() * !var; - - } - - for (const auto& [var1, var2, constant] : t_quad) { - - const unsigned int subproblem_id1 = var1.get(t_variable_flag); - const unsigned int subproblem_id2 = var2.get(t_variable_flag); - - if (subproblem_id1 != subproblem_id2) { - throw Exception("Cannot mix block-variables in quadratic expressions."); - } - - if (subproblem_id1 == MasterId) { - master_lhs += constant * var1 * var2; - continue; - } - - if (!constant.is_numerical()) { - throw Exception("Cannot have linking expression with subproblem's variable associated to a parametrized coefficient."); - } - - subproblem_patterns[subproblem_id1] += constant.numerical() * (!var1 * !var2); - - } - - return std::make_pair( - std::move(master_lhs), - std::move(subproblem_patterns) - ); - -} - -void idol::ArchivedDantzigWolfeDecomposition::add_convexity_constraints(Env& t_env, Model *t_master, std::vector &t_generation_patterns) const { - - const unsigned int n_subproblems = t_generation_patterns.size(); - - for (unsigned int i = 0 ; i < n_subproblems ; ++i) { - Ctr convexity_constraint(t_env, m_aggregation_type.has_value() ? m_aggregation_type.value() : Equal, 1); - t_master->add(convexity_constraint); - t_generation_patterns[i].linear().set(convexity_constraint, 1); - } - -} - -idol::ArchivedDantzigWolfeDecomposition &idol::ArchivedDantzigWolfeDecomposition::with_pricing_optimizer(const OptimizerFactory &t_pricing_optimizer) { - - if (m_pricing_optimizer) { - throw Exception("A pricing solver has already been set."); - } - - m_pricing_optimizer.reset(t_pricing_optimizer.clone()); - - return *this; -} - -idol::ArchivedDantzigWolfeDecomposition &idol::ArchivedDantzigWolfeDecomposition::with_master_optimizer(const OptimizerFactory &t_master_optimizer) { - - if (m_master_optimizer) { - throw Exception("A master solver has already been set."); - } - - m_master_optimizer.reset(t_master_optimizer.clone()); - - return *this; -} - -idol::ArchivedDantzigWolfeDecomposition &idol::ArchivedDantzigWolfeDecomposition::with_branching_on_master(bool t_value) { - - if (m_branching_on_master.has_value()) { - throw Exception("Branching on master setting has already been given."); - } - - m_branching_on_master = t_value; - - return *this; -} - -idol::ArchivedDantzigWolfeDecomposition &idol::ArchivedDantzigWolfeDecomposition::with_aggregation_type(idol::CtrType t_type) { - - if (m_aggregation_type.has_value()) { - throw Exception("Aggregation type has already been given."); - } - - m_aggregation_type = t_type; - - return *this; -} diff --git a/lib/src/optimizers/archive/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp b/lib/src/optimizers/archive/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp deleted file mode 100644 index a3cc9c8a..00000000 --- a/lib/src/optimizers/archive/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.cpp +++ /dev/null @@ -1,285 +0,0 @@ -// -// Created by henri on 24/03/23. -// -#include -#include "idol/optimizers/archive/dantzig-wolfe/Optimizers_ArchivedDantzigWolfeDecomposition.h" -#include "idol/modeling//objects/Versions.h" -#include "idol/modeling//expressions/operations/operators.h" -#include "idol/optimizers/Optimizer.h" - -idol::Optimizers::ArchivedDantzigWolfeDecomposition::ArchivedDantzigWolfeDecomposition(const Model& t_original_formulation, - const Annotation& t_constraint_flag, - const Annotation& t_variable_flag, - Model *t_master_problem, - const std::vector &t_subproblems, - std::vector t_generation_patterns, - const OptimizerFactory& t_pricing_optimizer) - : Optimizers::ArchivedColumnGeneration(t_original_formulation, t_master_problem, t_subproblems, std::move(t_generation_patterns)), - m_var_annotation(t_variable_flag), - m_ctr_annotation(t_constraint_flag), - m_pricing_optimizer(t_pricing_optimizer.clone()) { - -} - -double idol::Optimizers::ArchivedDantzigWolfeDecomposition::get_var_primal(const Var &t_var) const { - - const unsigned int subproblem_id = t_var.get(m_var_annotation); - - if (subproblem_id != MasterId) { - return get_subproblem_primal_value(t_var, subproblem_id); - } - - return ArchivedColumnGeneration::get_var_primal(t_var); -} - -double idol::Optimizers::ArchivedDantzigWolfeDecomposition::get_subproblem_primal_value(const Var &t_var, unsigned int t_subproblem_id) const { - - double result = 0; - for (const auto& [alpha, generator] : m_subproblems[t_subproblem_id].m_present_generators) { - const double alpha_val = m_master->get_var_primal(alpha); - if (alpha_val > 0) { - result += alpha_val * generator.get(t_var); - } - } - - return result; -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::set_subproblem_lower_bound(const Var &t_var, unsigned int t_subproblem_id, double t_value) { - - auto& subproblem = m_subproblems[t_subproblem_id]; - - subproblem.remove_column_if([&](const Var& t_object, const Solution::Primal& t_generator) { - //return true; - return t_generator.get(t_var) < t_value; - }); - - if (!m_branching_on_master) { - subproblem.m_model->set_var_lb(t_var, t_value); - return; - } - - apply_subproblem_bound_on_master(true, t_var, t_subproblem_id, t_value); - -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::set_subproblem_upper_bound(const Var &t_var, unsigned int t_subproblem_id, double t_value) { - - auto& subproblem = m_subproblems[t_subproblem_id]; - - subproblem.remove_column_if([&](const Var& t_object, const Solution::Primal& t_generator) { - //return true; - return t_generator.get(t_var) > t_value; - }); - - if (!m_branching_on_master) { - subproblem.m_model->set_var_ub(t_var, t_value); - return; - } - - apply_subproblem_bound_on_master(false, t_var, t_subproblem_id, t_value); - -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::apply_subproblem_bound_on_master(bool t_is_lb, const Var &t_var, unsigned int t_subproblem_id, double t_value) { - - auto& subproblem = m_subproblems[t_subproblem_id]; - - auto& applied_bounds = t_is_lb ? m_lower_bound_constraints : m_upper_bound_constraints; - const auto it = applied_bounds.find(t_var); - - if (it == applied_bounds.end()) { // Create a new constraint - - auto expanded = expand_subproblem_variable(t_var, t_subproblem_id); - const CtrType type = t_is_lb ? GreaterOrEqual : LessOrEqual; - - Ctr bound_constraint(m_master->env(), Equal, 0); - - m_master->add(bound_constraint, TempCtr(::idol::Row(expanded, t_value), type)); - subproblem.m_generation_pattern.linear().set(bound_constraint, !t_var); - - applied_bounds.emplace(t_var, bound_constraint); - - return; - } - - const double original_bound = t_is_lb ? parent().get_var_lb(t_var) : parent().get_var_ub(t_var); - - /* - if (equals(t_value, original_bound, Tolerance::Integer)) { // Remove existing constraint for it is not useful anymore - - m_master->remove(it->second); - subproblem.m_generation_pattern.linear().remove(it->second); - applied_bounds.erase(it); - - return; - } - */ - - m_master->set_ctr_rhs(it->second, t_value); - -} - -idol::LinExpr idol::Optimizers::ArchivedDantzigWolfeDecomposition::expand_subproblem_variable(const Var &t_var, unsigned int t_subproblem_id) { - - LinExpr result; - - for (const auto& [alpha, generator] : m_subproblems[t_subproblem_id].m_present_generators) { - result += generator.get(t_var) * alpha; - } - - return result; -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::update_var_lb(const Var &t_var) { - - const unsigned int subproblem_id = t_var.get(m_var_annotation); - - if (subproblem_id != MasterId) { - set_subproblem_lower_bound(t_var, subproblem_id, parent().get_var_lb(t_var)); - return; - } - - ArchivedColumnGeneration::update_var_lb(t_var); -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::update_var_ub(const Var &t_var) { - - const unsigned int subproblem_id = t_var.get(m_var_annotation); - - if (subproblem_id != MasterId) { - set_subproblem_upper_bound(t_var, subproblem_id, parent().get_var_ub(t_var)); - return; - } - - ArchivedColumnGeneration::update_var_ub(t_var); -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::set_objective(Expr &&t_objective) { - - const unsigned int n_subproblems = m_subproblems.size(); - - Expr master_obj = std::move(t_objective.constant()); - std::vector pricing_obj(n_subproblems); - - for (auto [var, coeff] : t_objective.linear()) { - const unsigned int subproblem_id = var.get(m_var_annotation); - if (subproblem_id == MasterId) { - master_obj += coeff * var; - } else { - if (!coeff.is_numerical()) { - throw Exception("Could not handle non-numerical objective coefficient as generation pattern."); - } - pricing_obj[subproblem_id] += coeff.numerical() * !var; - } - } - - m_master->set_obj_expr(std::move(master_obj)); - - for (unsigned int k = 0 ; k < n_subproblems ; ++k) { - m_subproblems[k].update_generation_pattern_objective(std::move(pricing_obj[k])); - } - -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::add(const Var &t_var) { - - const auto subproblem_id = t_var.get(m_var_annotation); - - if (subproblem_id == MasterId) { - ArchivedColumnGeneration::add(t_var); - return; - } - - if (m_subproblems.size() <= subproblem_id) { - - auto& env = m_master->env(); - - for (unsigned int i = subproblem_id ; i <= subproblem_id ; ++i) { - - auto* model = new Model(env); - - auto convex = m_master->add_ctr(Row(0, 1), Equal); - - Column column; - column.linear().set(convex, 1); - - model->use(*m_pricing_optimizer); - - add_subproblem(model, std::move(column)); - - } - - } - - m_subproblems[subproblem_id].m_model->add(t_var); - -} - -void idol::Optimizers::ArchivedDantzigWolfeDecomposition::add(const Ctr &t_ctr) { - - const auto subproblem_id = t_ctr.get(m_ctr_annotation); - - const auto& row = parent().get_ctr_row(t_ctr); - const auto type = parent().get_ctr_type(t_ctr); - - if (subproblem_id != MasterId) { - m_subproblems[subproblem_id].m_model->add(t_ctr, TempCtr(Row(row), type)); - return; - } - - const unsigned int n_subproblems = m_subproblems.size(); - - Row master_row; - std::vector pricing_pattern(n_subproblems); - - master_row.rhs() = row.rhs(); - - for (auto [var, coeff] : row.linear()) { - const unsigned int var_subproblem_id = var.get(m_var_annotation); - - if (var_subproblem_id == MasterId) { - master_row.linear() += coeff * var; - } else { - if (!coeff.is_numerical()) { - throw Exception("Could not handle non-numerical objective coefficient as generation pattern."); - } - pricing_pattern[var_subproblem_id] += coeff.numerical() * !var; - } - - } - - // Remove columns - - double master_lhs = 0; - - for (const auto& [var, constant] : master_row.linear()) { - master_lhs += constant.numerical() * m_master->get_var_primal(var); - } - - const double master_rhs = master_row.rhs().numerical(); - - for (auto& subproblem : m_subproblems) { - - if (pricing_pattern[subproblem.m_index].is_zero()) { - continue; - } - - subproblem.remove_column_if([&](const auto& t_alpha, const auto& t_generator) -> double { - const double value = master_lhs + pricing_pattern[subproblem.m_index].fix(t_generator) - master_rhs; - switch (type) { - case LessOrEqual: return value > 0; - case GreaterOrEqual: return value < 0; - default:; - } - return !equals(value, 0., 1e-6); - }); - - subproblem.m_generation_pattern.linear().set(t_ctr, std::move(pricing_pattern[subproblem.m_index])); - - } - - // Add cut - m_master->add(t_ctr, TempCtr(Row(std::move(master_row)), type)); - -} diff --git a/lib/src/optimizers/callbacks/IntegerMaster.cpp b/lib/src/optimizers/callbacks/IntegerMaster.cpp index 4104cd5b..1109a600 100644 --- a/lib/src/optimizers/callbacks/IntegerMaster.cpp +++ b/lib/src/optimizers/callbacks/IntegerMaster.cpp @@ -2,7 +2,6 @@ // Created by henri on 30/03/23. // #include "idol/optimizers/callbacks/IntegerMaster.h" -#include "idol/optimizers/archive/column-generation/ArchivedColumnGeneration.h" #include "idol/optimizers/dantzig-wolfe/Optimizers_DantzigWolfeDecomposition.h" idol::Heuristics::IntegerMaster::IntegerMaster(const IntegerMaster& t_src)