Skip to content

Commit

Permalink
use new CG in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hlefebvr committed Oct 31, 2023
1 parent 972241a commit 04f6ad2
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 114 deletions.
59 changes: 32 additions & 27 deletions dev/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#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"
#include "idol/optimizers/archive/column-generation/IntegerMaster.h"
#include "idol/optimizers/callbacks/IntegerMaster.h"
#include "idol/optimizers/wrappers/HiGHS/HiGHS.h"
#include "idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h"
#include "idol/optimizers/wrappers/Gurobi/Gurobi.h"
Expand All @@ -19,57 +19,62 @@ using namespace idol;
int main(int t_argc, char** t_argv) {


const auto instance = Problems::GAP::read_instance("/home/henri/Research/idol/examples/assignment.data.txt");
const auto instance = Problems::GAP::read_instance("/home/henri/Research/idol/tests/data/generalized-assignment-problem/GAP_instance0.txt");

const unsigned int n_agents = instance.n_agents();
const unsigned int n_jobs = instance.n_jobs();

// Create optimization environment
Env env;

// Create model
Model model(env);
Annotation<Ctr> std_decomposition(env, "by_machines", MasterId);

Annotation<Ctr> nested_decomposition1(env, "nested_decomposition1", MasterId);
Annotation<Ctr> nested_decomposition2(env, "nested_decomposition2", MasterId);

// Create decomposition annotation
Annotation<Ctr> decomposition(env, "decomposition", MasterId);
Model model(env);

// Create assignment variables (x_ij binaries)
auto x = model.add_vars(Dim<2>(n_agents, n_jobs), 0., 1., Binary, "x");
auto x = Var::make_vector(env, Dim<2>(n_agents, n_jobs), 0., 1., Binary, "x");
model.add_vector<Var, 2>(x);

// Create knapsack constraints (i.e., capacity constraints)
for (unsigned int i = 0 ; i < n_agents ; ++i) {
auto capacity = model.add_ctr(idol_Sum(j, Range(n_jobs), instance.resource_consumption(i, j) * x[i][j]) <= instance.capacity(i), "capacity_" + std::to_string(i));

capacity.set(decomposition, i); // Assign constraint to i-th subproblem
Ctr capacity(env, idol_Sum(j, Range(n_jobs), instance.resource_consumption(i, j) * x[i][j]) <= instance.capacity(i), "capacity_" + std::to_string(i));

capacity.set(std_decomposition, i);
capacity.set(nested_decomposition1, i / 2);
capacity.set(nested_decomposition2, i % 2);

model.add(capacity);

}

// Create assignment constraints
for (unsigned int j = 0 ; j < n_jobs ; ++j) {
model.add_ctr(idol_Sum(i, Range(n_agents), x[i][j]) == 1, "assignment_" + std::to_string(j));
Ctr assignment(env, idol_Sum(i, Range(n_agents), x[i][j]) == 1, "assignment_" + std::to_string(j));
model.add(assignment);
}

// Set the objective function
model.set_obj_expr(idol_Sum(i, Range(n_agents), idol_Sum(j, Range(n_jobs), instance.cost(i, j) * x[i][j])));

bool branching_on_sub_problem = true;
double smoothing_factor = .0;
bool integer_master_heuristic = true;

// Set optimizer
model.use(BranchAndBound()
.with_node_optimizer(
DantzigWolfeDecomposition(decomposition)
.with_master_optimizer(Gurobi::ContinuousRelaxation())
.with_default_sub_problem_spec(
DantzigWolfe::SubProblem()
.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)
DantzigWolfeDecomposition(std_decomposition)
.with_master_optimizer(Gurobi::ContinuousRelaxation())
.with_default_sub_problem_spec(DantzigWolfe::SubProblem().add_optimizer(Gurobi()))
.with_hard_branching(branching_on_sub_problem)
.with_infeasibility_strategy(DantzigWolfe::FarkasPricing())
.with_dual_price_smoothing_stabilization(DantzigWolfe::Neame(smoothing_factor))
.with_log_level(Mute, Yellow)
)
.with_subtree_depth(0)
.with_branching_rule(MostInfeasible())
.with_node_selection_rule(WorstBound())
.with_node_selection_rule(BestBound())
.add_callback(Heuristics::IntegerMaster().with_optimizer(Gurobi()))
.with_log_level(Info, Blue)
.with_log_frequency(1)
);
Expand Down
36 changes: 18 additions & 18 deletions examples/assignment.example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#include "idol/optimizers/branch-and-bound/node-selection-rules/factories/WorstBound.h"
#include "idol/optimizers/branch-and-bound/BranchAndBound.h"
#include "idol/optimizers/archive/dantzig-wolfe/ArchivedDantzigWolfeDecomposition.h"
#include "idol/optimizers/archive/column-generation/IntegerMaster.h"
#include "idol/optimizers/callbacks/IntegerMaster.h"
#include "idol/optimizers/callbacks/SimpleRounding.h"
#include "idol/optimizers/branch-and-bound/branching-rules/factories/MostInfeasible.h"
#include "idol/optimizers/wrappers/HiGHS/HiGHS.h"
Expand Down Expand Up @@ -51,23 +51,23 @@ int main(int t_argc, const char** t_argv) {

// Set optimizer
model.use(BranchAndBound()
.with_node_optimizer(
ArchivedDantzigWolfeDecomposition(decomposition)
.with_master_optimizer(HiGHS::ContinuousRelaxation())
.with_pricing_optimizer(HiGHS())
.with_log_level(Info, Yellow)
.with_farkas_pricing(true)
.with_artificial_variables_cost(1e+4)
.with_branching_on_master(true)
.with_dual_price_smoothing_stabilization(.3)
.with_column_pool_clean_up(1e+8, .75)
)
.with_subtree_depth(0)
.with_branching_rule(MostInfeasible())
.with_node_selection_rule(WorstBound())
.with_log_level(Info, Blue)
.with_log_frequency(1)
.with_callback(Heuristics::IntegerMaster().with_optimizer(HiGHS()))
.with_node_optimizer(
ArchivedDantzigWolfeDecomposition(decomposition)
.with_master_optimizer(HiGHS::ContinuousRelaxation())
.with_pricing_optimizer(HiGHS())
.with_log_level(Info, Yellow)
.with_farkas_pricing(true)
.with_artificial_variables_cost(1e+4)
.with_branching_on_master(true)
.with_dual_price_smoothing_stabilization(.3)
.with_column_pool_clean_up(1e+8, .75)
)
.with_subtree_depth(0)
.with_branching_rule(MostInfeasible())
.with_node_selection_rule(WorstBound())
.with_log_level(Info, Blue)
.with_log_frequency(1)
.add_callback(Heuristics::IntegerMaster().with_optimizer(HiGHS()))
);

// Solve
Expand Down
4 changes: 2 additions & 2 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ add_library(idol STATIC
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/archive/column-generation/IntegerMaster.h
src/optimizers/archive/column-generation/IntegerMaster.cpp
include/idol/optimizers/callbacks/IntegerMaster.h
src/optimizers/callbacks/IntegerMaster.cpp
src/optimizers/wrappers/gurobi/GurobiCallbackI.cpp
include/idol/optimizers/callbacks/Callback.h
src/optimizers/callbacks/Callback.cpp
Expand Down
6 changes: 3 additions & 3 deletions lib/include/idol/optimizers/branch-and-bound/BranchAndBound.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class idol::BranchAndBound : public OptimizerFactoryWithDefaultParameters<Branch
* @param t_callback the callback factory
* @return the optimizer factory itself
*/
BranchAndBound<NodeT>& with_callback(const BranchAndBoundCallbackFactory<NodeT> & t_callback);
BranchAndBound<NodeT>& add_callback(const BranchAndBoundCallbackFactory<NodeT> & t_callback);

/**
* Adds a (solver independent) callback which will be called by the optimizer.
Expand Down Expand Up @@ -222,11 +222,11 @@ idol::BranchAndBound<NodeT>::with_cutting_planes(const CuttingPlaneGenerator &t_
template<class NodeT>
idol::BranchAndBound<NodeT> &
idol::BranchAndBound<NodeT>::with_callback(const CallbackFactory &t_callback) {
return with_callback(CallbackAsBranchAndBoundCallback<NodeT>(t_callback));
return add_callback(CallbackAsBranchAndBoundCallback<NodeT>(t_callback));
}

template<class NodeT>
idol::BranchAndBound<NodeT> &idol::BranchAndBound<NodeT>::with_callback(const BranchAndBoundCallbackFactory<NodeT> &t_callback) {
idol::BranchAndBound<NodeT> &idol::BranchAndBound<NodeT>::add_callback(const BranchAndBoundCallbackFactory<NodeT> &t_callback) {

m_callbacks.emplace_back(t_callback.clone());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class idol::DantzigWolfe::Formulation {
auto sub_problems() { return IteratorForward(m_sub_problems); }
auto sub_problems() const { return ConstIteratorForward(m_sub_problems); }

auto present_generators(unsigned int t_id) const { return ConstIteratorForward(m_present_generators[t_id]); }

Model& sub_problem(unsigned int t_id) { return m_sub_problems[t_id]; }
const Model& sub_problem(unsigned int t_id) const { return m_sub_problems[t_id]; }

Expand All @@ -76,6 +78,8 @@ class idol::DantzigWolfe::Formulation {
void update_var_ub(const Var& t_var, double t_ub, bool t_hard);

void remove_column_if(unsigned int t_sub_problem_id, const std::function<bool(const Var &, const Solution::Primal &)> &t_indicator_for_removal);

void update_obj(const Expr<Var, Var>& t_expr);
};

#endif //IDOL_DANTZIGWOLFEFORMULATION_H
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class idol::Optimizers::DantzigWolfeDecomposition : public Algorithm {

std::string name() const override;

[[nodiscard]] const DantzigWolfe::Formulation& formulation() const { return m_formulation; }

class ColumnGeneration;
protected:
void hook_before_optimize() override;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//
// Created by henri on 30/03/23.
//
#include "idol/optimizers/archive/column-generation/IntegerMaster.h"
#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)
: m_optimizer_factory(t_src.m_optimizer_factory ? t_src.m_optimizer_factory->clone() : nullptr),
Expand Down Expand Up @@ -106,10 +107,13 @@ void idol::Heuristics::IntegerMaster::Strategy::operator()(CallbackEvent t_event
}

const auto& relaxation = this->relaxation();
const auto& column_generation_optimizer = relaxation.optimizer().as<Optimizers::ArchivedColumnGeneration>();
const auto& dantzig_wolfe_optimizer = relaxation.optimizer().as<Optimizers::DantzigWolfeDecomposition>();
const auto& original_model = this->original_model();
const auto& formulation = dantzig_wolfe_optimizer.formulation();

std::unique_ptr<Model> integer_master(column_generation_optimizer.master().clone());
const unsigned int n_sub_problems = formulation.n_sub_problems();

std::unique_ptr<Model> integer_master(formulation.master().clone());

integer_master->unuse();

Expand All @@ -121,8 +125,8 @@ void idol::Heuristics::IntegerMaster::Strategy::operator()(CallbackEvent t_event
}

if (m_integer_columns) {
for (const auto &subproblem: column_generation_optimizer.subproblems()) {
for (const auto &[alpha, generator]: subproblem.present_generators()) {
for (unsigned int i = 0 ; i < n_sub_problems ; ++i) {
for (const auto &[alpha, generator]: formulation.present_generators(i)) {
integer_master->set_var_type(alpha, Binary);
}
}
Expand All @@ -134,6 +138,8 @@ void idol::Heuristics::IntegerMaster::Strategy::operator()(CallbackEvent t_event
// TODO set bound stop
integer_master->optimizer().set_param_iteration_limit(m_iteration_limit);

integer_master->optimize();

const int status = integer_master->get_status();

if (status != Optimal && status != Feasible) {
Expand All @@ -143,8 +149,8 @@ void idol::Heuristics::IntegerMaster::Strategy::operator()(CallbackEvent t_event
auto solution = save_primal(*integer_master);

// search for alpha = 1, add generator to solution
for (const auto& subproblem : column_generation_optimizer.subproblems()) {
for (const auto &[alpha, generator]: subproblem.present_generators()) {
for (unsigned int i = 0 ; i < n_sub_problems ; ++i) {
for (const auto &[alpha, generator]: formulation.present_generators(i)) {
if (solution.get(alpha) > .5) {
solution.merge_without_conflict(generator);
solution.set(alpha, 0.);
Expand Down
27 changes: 24 additions & 3 deletions lib/src/optimizers/column-generation/DantzigWolfeFormulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ void idol::DantzigWolfe::Formulation::update_var_ub(const idol::Var &t_var, doub
}

remove_column_if(sub_problem_id, [&](const Var& t_object, const Solution::Primal& t_generator) {
//return true;
// return true;
return t_generator.get(t_var) > t_ub;
});

Expand All @@ -454,7 +454,6 @@ void idol::DantzigWolfe::Formulation::apply_sub_problem_bound_on_master(bool t_i
const idol::Var &t_var,
unsigned int t_sub_problem_id, double t_value) {


auto& applied_bounds = t_is_lb ? m_soft_branching_lower_bound_constraints : m_soft_branching_upper_bound_constraints;
const auto it = applied_bounds.find(t_var);

Expand All @@ -465,7 +464,7 @@ void idol::DantzigWolfe::Formulation::apply_sub_problem_bound_on_master(bool t_i

Ctr bound_constraint(m_master.env(), Equal, 0);

m_master.add(bound_constraint, TempCtr(::idol::Row(expanded, t_value), type));
m_master.add(bound_constraint, TempCtr(Row(expanded, t_value), type));
m_generation_patterns[t_sub_problem_id].linear().set(bound_constraint, !t_var);

applied_bounds.emplace(t_var, bound_constraint);
Expand Down Expand Up @@ -507,3 +506,25 @@ void idol::DantzigWolfe::Formulation::remove_column_if(unsigned int t_sub_proble
}

}

void idol::DantzigWolfe::Formulation::update_obj(const idol::Expr<idol::Var, idol::Var> &t_expr) {

const unsigned int n_sub_problems = m_sub_problems.size();

auto [master_part, sub_problem_parts] = decompose_expression(t_expr.linear(), t_expr.quadratic());

master_part += t_expr.constant();

m_master.set_obj_expr(std::move(master_part));

for (unsigned int i = 0 ; i < n_sub_problems ; ++i) {

for (const auto& [var, generator] : m_present_generators[i]) {
m_master.set_var_obj(var, sub_problem_parts[i].fix(generator));
}

m_generation_patterns[i].set_obj(std::move(sub_problem_parts[i]));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void idol::Optimizers::DantzigWolfeDecomposition::update_obj_sense() {
}

void idol::Optimizers::DantzigWolfeDecomposition::update_obj() {
throw Exception("Not implemented update_obj");
m_formulation.update_obj(parent().get_obj_expr());
}

void idol::Optimizers::DantzigWolfeDecomposition::update_rhs() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ void idol::Optimizers::DantzigWolfeDecomposition::ColumnGeneration::initialize_s
m_sub_problems_phases.reserve(n_sub_problems);
for (unsigned int i = 0 ; i < n_sub_problems ; ++i) {
const auto phase = m_parent.m_sub_problem_specifications[i].phases().begin();
if (formulation.sub_problem(i).has_optimizer()) { continue; }
formulation.sub_problem(i).use(**phase);
m_sub_problems_phases.emplace_back(phase);
}
Expand Down
Loading

0 comments on commit 04f6ad2

Please sign in to comment.