Skip to content

Commit

Permalink
add affine decision rules
Browse files Browse the repository at this point in the history
  • Loading branch information
hlefebvr committed Nov 28, 2024
1 parent 7917bce commit 80b3fda
Show file tree
Hide file tree
Showing 11 changed files with 539 additions and 125 deletions.
143 changes: 35 additions & 108 deletions dev/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,145 +23,72 @@
#include "idol/mixed-integer/problems/facility-location-problem/FLP_Instance.h"
#include "idol/robust/modeling/Description.h"
#include "idol/bilevel/modeling/Description.h"
#include "idol/robust/optimizers/deterministic/Deterministic.h"
#include "idol/robust/optimizers/affine-decision-rule/AffineDecisionRule.h"

using namespace idol;

Model make_deterministic(const Model& t_model, const Robust::Description& t_description) {

auto& env = t_model.env();
Model result(env);

for (const auto& var : t_model.vars()) {
const double lb = t_model.get_var_lb(var);
const double ub = t_model.get_var_ub(var);
const auto type = t_model.get_var_type(var);
result.add(var, TempVar(lb, ub, type, 0, LinExpr<Ctr>()));
}

for (const auto& ctr : t_model.ctrs()) {
const auto& row = t_model.get_ctr_row(ctr);
const auto rhs = t_model.get_ctr_rhs(ctr);
const auto type = t_model.get_ctr_type(ctr);

const auto& uncertain_mat_coeffs = t_description.uncertain_mat_coeffs(ctr);
const auto& uncertain_rhs = t_description.uncertain_rhs(ctr);

const bool has_lhs_uncertainty = !uncertain_mat_coeffs.is_zero(Tolerance::Sparsity);
const bool has_rhs_uncertainty = !uncertain_rhs.is_zero(Tolerance::Sparsity);

if (!has_lhs_uncertainty && !has_rhs_uncertainty) {
result.add(ctr, TempCtr(LinExpr(row), type, rhs));
continue;
}

if (type == Equal) {
throw Exception("Cannot make equality constraints deterministic.");
}

const auto& uncertainty_set = t_description.uncertainty_set();

QuadExpr<Var> objective = row;
for (const auto& [var, uncertain_coeff] : row) {
const auto& uncertain_mat_coeff = uncertain_mat_coeffs.get(var);
objective += uncertain_mat_coeff * var;
}
for (const auto& [var, uncertain_coeff] : uncertain_rhs) {
objective -= uncertain_coeff * var;
}

if (type == LessOrEqual) {
objective *= -1;
}

Reformulators::KKT kkt(uncertainty_set,
objective,
[&](const Var& t_var) { return uncertainty_set.has(t_var); },
[&](const Ctr& t_ctr) { return uncertainty_set.has(t_ctr); },
[&](const QCtr& t_qvar) { return uncertainty_set.has(t_qvar); }
);
kkt.set_prefix("robust_" + ctr.name() + "_");
kkt.add_dual_variables(result);
kkt.add_dual_constraints(result);

auto dual_objective = kkt.get_dual_obj_expr();
if (type == LessOrEqual) {
dual_objective *= -1;
}

assert(!dual_objective.has_quadratic());

result.add(ctr, TempCtr(
std::move(dual_objective.affine().linear()),
type,
-dual_objective.affine().constant())
);

}

return result;
}

int main(int t_argc, const char** t_argv) {

Env env;

const double Gamma = 3;

// Generate parameters
const auto filename = "/home/henri/Research/idol/tests/data/facility-location-problem/instance_F10_C20__0.txt";

// Read instance
const auto instance = idol::Problems::FLP::read_instance_1991_Cornuejols_et_al(filename);
const auto instance = Problems::FLP::read_instance_1991_Cornuejols_et_al("/home/henri/Research/idol/examples/robust/robust-facility-location.data.txt");
const unsigned int n_customers = instance.n_customers();
const unsigned int n_facilities = instance.n_facilities();

// Make model
auto x = Var::make_vector(env, Dim<1>(n_facilities), 0., 1., Continuous, 0., "x");
auto y = Var::make_vector(env, Dim<2>(n_facilities, n_customers), 0., 1., Continuous, 0., "y");

// Uncertainty set
Model uncertainty_set(env);
const auto xi = uncertainty_set.add_vars(Dim<1>(n_facilities), 0., 1., Continuous, 0., "xi");
uncertainty_set.add_ctr(idol_Sum(i, Range(n_facilities), xi[i]) <= Gamma);
const double Gamma = 0;
const auto xi = uncertainty_set.add_vars(Dim<1>(n_customers), 0, 1, Continuous, 0., "xi");
uncertainty_set.add_ctr(idol_Sum(i, Range(n_customers), xi[i]) <= Gamma);

// Make model
Model model(env);
Robust::Description description(uncertainty_set);

model.add_vector<Var, 1>(x);
model.add_vector<Var, 2>(y);
auto x = model.add_vars(Dim<1>(n_facilities), 0., 1., Binary, 0., "x");
auto y = model.add_vars(Dim<2>(n_facilities, n_customers), 0., 1., Continuous, 0., "y");

for (unsigned int i = 0 ; i < n_facilities ; ++i) {
model.add(Ctr(env, idol_Sum(j, Range(n_customers), instance.demand(j) * y[i][j]) <= instance.capacity(i) * x[i]));

const auto c = model.add_ctr(idol_Sum(j, Range(n_customers), instance.demand(j) * y[i][j]) <= instance.capacity(i));

for (unsigned int j = 0 ; j < n_customers ; ++j) {
description.set_uncertain_rhs(c, -xi[j]);
}

}

for (unsigned int j = 0 ; j < n_customers ; ++j) {
model.add(Ctr(env, idol_Sum(i, Range(n_facilities), y[i][j]) == 1));
model.add_ctr(idol_Sum(i, Range(n_facilities), y[i][j]) >= 1);
}

Robust::Description description(uncertainty_set);
for (unsigned int i = 0 ; i < n_facilities ; ++i) {
for (unsigned int j = 0 ; j < n_customers ; ++j) {
description.make_stage_var(y[i][j], 1);
auto activation = model.add_ctr(y[i][j] <= 1);
description.set_uncertain_rhs(activation, -xi[i]);
model.add_ctr(y[i][j] <= x[i]);
}
}

model.set_obj_expr(idol_Sum(i, Range(n_facilities), instance.fixed_cost(i) * x[i] + idol_Sum(j, Range(n_customers),
instance.per_unit_transportation_cost(
i, j) *
instance.demand(j) *
y[i][j])));

std::cout << Robust::Description::View(model, description) << std::endl;
model.set_obj_expr(idol_Sum(i, Range(n_facilities),
instance.fixed_cost(i) * x[i]
+ idol_Sum(j, Range(n_customers),
instance.per_unit_transportation_cost(i, j) *
instance.demand(j) *
y[i][j]
)
)
);

auto deterministic = make_deterministic(model, description);
model.use(Gurobi());
model.optimize();

deterministic.use(Gurobi());
std::cout << "Deterministic Problem has value: " << model.get_best_obj() << std::endl;

deterministic.optimize();
model.use(Robust::AffineDecisionRule(description).with_deterministic_optimizer(Gurobi()));
model.optimize();

std::cout << deterministic.get_status() << std::endl;
std::cout << deterministic.optimizer().time().count() << std::endl;
std::cout << save_primal(deterministic) << std::endl;
std::cout << "ADR Problem has value: " << model.get_best_obj() << std::endl;

return 0;
}
4 changes: 4 additions & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ add_library(idol STATIC
src/robust/optimizers/deterministic/Deterministic.cpp
include/idol/robust/optimizers/deterministic/Optimizers_Deterministic.h
src/robust/optimizers/deterministic/Optimizers_Deterministic.cpp
include/idol/robust/optimizers/affine-decision-rule/AffineDecisionRule.h
include/idol/robust/optimizers/affine-decision-rule/Optimizers_AffineDecisionRule.h
src/robust/optimizers/affine-decision-rule/AffineDecisionRule.cpp
src/robust/optimizers/affine-decision-rule/Optimizers_AffineDecisionRule.cpp
)

find_package(OpenMP REQUIRED)
Expand Down
11 changes: 8 additions & 3 deletions lib/include/idol/general/optimizers/OptimizerWithLazyUpdates.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,20 @@ idol::OptimizerWithLazyUpdates<VarImplT, CtrImplT, QCtrImplT>::OptimizerWithLazy
template<class VarImplT, class CtrImplT, class QCtrImplT>
void idol::OptimizerWithLazyUpdates<VarImplT, CtrImplT, QCtrImplT>::build() {

for (const auto& var : parent().vars()) {
const auto& parent = this->parent();

m_variables.reserve(parent.vars().size());
for (const auto& var : parent.vars()) {
add(var);
}

for (const auto& ctr : parent().ctrs()) {
m_constraints.reserve(parent.ctrs().size());
for (const auto& ctr : parent.ctrs()) {
add(ctr);
}

for (const auto& qctr : parent().qctrs()) {
m_qconstraints.reserve(parent.qctrs().size());
for (const auto& qctr : parent.qctrs()) {
add(qctr);
}

Expand Down
12 changes: 9 additions & 3 deletions lib/include/idol/robust/modeling/Description.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ class idol::Robust::Description {

[[nodiscard]] const Annotation<unsigned int>& stage_annotation() const;

void make_stage_var(const Var& t_var, unsigned int t_stage);

void make_stage_ctr(const Ctr& t_ctr, unsigned int t_stage);
void set_stage(const Var& t_var, unsigned int t_stage);

unsigned int stage(const Var& t_var) const;

Expand Down Expand Up @@ -61,6 +59,14 @@ class idol::Robust::Description {
m_uncertain_obj.set(t_var, t_obj);
}

void set_uncertain_obj(LinExpr<Var, LinExpr<Var>> t_obj) {
m_uncertain_obj = std::move(t_obj);
}

void set_uncertain_rhs(LinExpr<Ctr, LinExpr<Var>> t_rhs) {
m_uncertain_rhs = std::move(t_rhs);
}

class View {
const Description& m_description;
const Model& m_deterministic_model;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Created by henri on 28.11.24.
//

#ifndef IDOL_AFFINEDECISIONRULE_H
#define IDOL_AFFINEDECISIONRULE_H

#include "idol/general/optimizers/OptimizerFactory.h"
#include "idol/robust/modeling/Description.h"
#include "idol/mixed-integer/modeling/models/Model.h"

namespace idol::Robust {
class AffineDecisionRule;
}

class idol::Robust::AffineDecisionRule : public OptimizerFactoryWithDefaultParameters<AffineDecisionRule> {
const Robust::Description& m_description;
std::unique_ptr<OptimizerFactory> m_deterministic_optimizer;
public:
explicit AffineDecisionRule(const Robust::Description& t_description);

AffineDecisionRule(const AffineDecisionRule& t_src);

Optimizer *operator()(const Model &t_model) const override;

[[nodiscard]] OptimizerFactory *clone() const override;

AffineDecisionRule& with_deterministic_optimizer(const OptimizerFactory& t_deterministic_optimizer);

struct Result {

Model model;
std::vector<QuadExpr<Var>> affine_decision_rules;
Robust::Description description;

explicit Result(Model&& t_model, const Model& t_uncertainty_set) : model(std::move(t_model)), description(t_uncertainty_set) {}
};

static Result make_model(const Model& t_model, const Robust::Description& t_description);
};


#endif //IDOL_AFFINEDECISIONRULE_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// Created by henri on 28.11.24.
//

#ifndef IDOL_OPTIMIZERS_AFFINEDECISIONRULE_H
#define IDOL_OPTIMIZERS_AFFINEDECISIONRULE_H

#include "idol/robust/modeling/Description.h"
#include "idol/general/optimizers/Algorithm.h"
#include "idol/general/optimizers/OptimizerFactory.h"
#include "idol/mixed-integer/modeling/models/Model.h"
#include "idol/robust/optimizers/affine-decision-rule/AffineDecisionRule.h"

namespace idol::Optimizers::Robust {
class AffineDecisionRule;
}

class idol::Optimizers::Robust::AffineDecisionRule : public Algorithm {
const idol::Robust::Description& m_description;
std::unique_ptr<OptimizerFactory> m_deterministic_optimizer;
std::unique_ptr<idol::Robust::AffineDecisionRule::Result> m_deterministic_model;

void throw_if_no_deterministic_model() const;
public:
AffineDecisionRule(const Model& t_parent, const idol::Robust::Description& t_description, const OptimizerFactory& t_deterministic_optimizer);

[[nodiscard]] std::string name() const override;

[[nodiscard]] SolutionStatus get_status() const override;

[[nodiscard]] SolutionReason get_reason() const override;

[[nodiscard]] double get_best_obj() const override;

[[nodiscard]] double get_best_bound() const override;

[[nodiscard]] double get_var_primal(const Var &t_var) const override;

[[nodiscard]] double get_var_reduced_cost(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;

protected:
void add(const Var &t_var) override;

void add(const Ctr &t_ctr) override;

void add(const QCtr &t_ctr) override;

void remove(const Var &t_var) override;

void remove(const Ctr &t_ctr) override;

void remove(const QCtr &t_ctr) override;

void update() override;

void write(const std::string &t_name) override;

void hook_optimize() 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;
};

#endif //IDOL_OPTIMIZERS_AFFINEDECISIONRULE_H
Loading

0 comments on commit 80b3fda

Please sign in to comment.