Skip to content

Commit

Permalink
fix PADM
Browse files Browse the repository at this point in the history
  • Loading branch information
hlefebvr committed Nov 29, 2024
1 parent 97f5832 commit c361251
Show file tree
Hide file tree
Showing 18 changed files with 523 additions and 497 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ auto follower_c4 = high_point_relaxation.add_ctr(2 * x + 10 * y >= 15);
// Prepare bilevel description
Bilevel::Description description(env);
description.set_lower_objective(y);
description.set_lower_level_obj(y);
description.set_follower_var(y);
description.set_follower_ctr(follower_c1);
description.set_follower_ctr(follower_c2);
Expand Down
10 changes: 5 additions & 5 deletions examples/bilevel/kkt.example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ int main(int t_argc, const char** t_argv) {

// Prepare bilevel description
Bilevel::Description description(env);
description.set_lower_objective(-y);
description.set_lower_level_obj(-y);
description.make_lower_level(y);
description.make_follower(follower_c1);
description.make_follower(follower_c2);
description.make_follower(follower_c3);
description.make_follower(follower_c4);
description.make_lower_level(follower_c1);
description.make_lower_level(follower_c2);
description.make_lower_level(follower_c3);
description.make_lower_level(follower_c4);

Reformulators::KKT reformulator(high_point_relaxation, description);

Expand Down
10 changes: 5 additions & 5 deletions examples/bilevel/mibs.example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ int main(int t_argc, const char** t_argv) {

// Prepare bilevel description
Bilevel::Description description(env);
description.set_lower_objective(y);
description.set_lower_level_obj(y);
description.make_lower_level(y);
description.make_follower(follower_c1);
description.make_follower(follower_c2);
description.make_follower(follower_c3);
description.make_follower(follower_c4);
description.make_lower_level(follower_c1);
description.make_lower_level(follower_c2);
description.make_lower_level(follower_c3);
description.make_lower_level(follower_c4);

// Use coin-or/MibS as external solver
high_point_relaxation.use(
Expand Down
38 changes: 23 additions & 15 deletions examples/bilevel/padm.example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "idol/mixed-integer/optimizers/padm/SubProblem.h"
#include "idol/mixed-integer/optimizers/padm/PenaltyUpdates.h"
#include "idol/mixed-integer/modeling/models/KKT.h"
#include "idol/mixed-integer/optimizers/wrappers/GLPK/GLPK.h"
#include "idol/mixed-integer/optimizers/wrappers/HiGHS/HiGHS.h"

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

Expand All @@ -23,21 +25,22 @@ int main(int t_argc, const char** t_argv) {

model.set_obj_expr(delta * delta);

auto c = model.add_qctr(x + delta * x + y - 1, LessOrEqual);
model.add_ctr(y == 1);
auto c = model.add_qctr((1 + delta) * x + y - 1, LessOrEqual);
auto coupling = model.add_ctr(y == 1);

Bilevel::Description description(env);
description.make_lower_level(x);
description.make_lower_level(y);
description.make_lower_level(c);
description.set_lower_objective(-2 * x - y);
description.set_lower_level_obj(-2 * x - y);

Reformulators::KKT reformulator(model, description);

Model single_level(env);
reformulator.add_coupling_variables(single_level);
reformulator.add_strong_duality_reformulation(single_level);
reformulator.add_coupling_constraints(single_level);
single_level.set_obj_expr(single_level.get_obj_expr());

std::cout << model << std::endl;
std::cout << single_level << std::endl;
Expand All @@ -52,24 +55,29 @@ int main(int t_argc, const char** t_argv) {
var.set(decomposition, var.id() == delta.id());
}

Annotation<bool> penalized_constraints(env, "penalized_constraints");
for (const auto& ctr : single_level.ctrs()) {
ctr.set(penalized_constraints, true);
}
/*
* All constraints will be penalized.
* The initial penalty value is set to 1e2.
* If you do not want to penalize a constraint, you can set the initial penalty value to < 0.
*/
Annotation<double> initial_penalties(env, "initial_penalties", 1e2);

Point<Var> initial_point;

single_level.use(
PADM(decomposition, penalized_constraints)
.with_default_sub_problem_spec(ADM::SubProblem().with_optimizer(Gurobi()))
.with_penalty_update(PenaltyUpdates::Multiplicative(2))
.with_rescaling(true, 1e5)
.with_initial_penalty_parameter(1e2)
.with_logs(true)
PADM(decomposition, initial_penalties)
.with_default_sub_problem_spec(
ADM::SubProblem()
.with_optimizer(Gurobi())
.with_initial_point(initial_point)
)
.with_penalty_update(PenaltyUpdates::Multiplicative(2))
.with_rescaling_threshold(1e4)
.with_logs(true)
);

single_level.optimize();

std::cout << single_level.get_status() << std::endl;

std::cout << save_primal(single_level) << std::endl;

return 0;
Expand Down
4 changes: 2 additions & 2 deletions examples/mixed-integer/assignment-penalty-bap.example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ int main(int t_argc, const char** t_argv) {
Annotation decomposition(env, "decomposition", MasterId);

// Create penalized constraints annotation
Annotation<Ctr, bool> penalized_constraints(env, "penalized_constraints", false);
Annotation<bool> penalized_constraints(env, "penalized_constraints", false);

// Create assignment variables (x_ij binaries)
auto x = model.add_vars(Dim<2>(n_agents, n_jobs), 0., 1., Binary, 0., "x");
Expand Down Expand Up @@ -72,7 +72,7 @@ int main(int t_argc, const char** t_argv) {

const auto penalty_method = PenaltyMethod(penalized_constraints)
.with_penalty_update(PenaltyUpdates::Multiplicative(2))
.with_rescaling(true, 1e3)
.with_rescaling_threshold(1e3)
.with_feasible_solution_status(Optimal);

const auto branch_and_bound = BranchAndBound()
Expand Down
36 changes: 18 additions & 18 deletions lib/include/idol/bilevel/modeling/Description.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,49 @@ namespace idol::Bilevel {
}

class idol::Bilevel::Description {
Annotation<unsigned int> m_lower_level;
Annotation<unsigned int> m_level;
QuadExpr<Var> m_follower_objective;
public:
Description(Env& t_env, const std::string& t_name) : m_lower_level(t_env, t_name + "_lower_level", MasterId) {}
Description(Env& t_env, const std::string& t_name) : m_level(t_env, t_name + "_lower_level", MasterId) {}

explicit Description(Env& t_env) : Description(t_env, "bilevel") {}

explicit Description(const Annotation<unsigned int>& t_lower_level) : m_lower_level(t_lower_level) {}
explicit Description(const Annotation<unsigned int>& t_lower_level) : m_level(t_lower_level) {}

Description(const Annotation<unsigned int>& t_lower_level,
AffExpr<Var> t_follower_objective)
: m_lower_level(t_lower_level),
: m_level(t_lower_level),
m_follower_objective(std::move(t_follower_objective)) {}

[[nodiscard]] const Annotation<unsigned int>& lower_level() const { return m_lower_level; }
[[nodiscard]] const Annotation<unsigned int>& lower_level() const { return m_level; }

[[nodiscard]] const QuadExpr<Var>& follower_obj() const { return m_follower_objective; }

void make_leader(const Var& t_var) { t_var.set(m_lower_level, MasterId); }
void make_upper_level(const Var& t_var) { t_var.set(m_level, MasterId); }

void make_leader(const Ctr& t_ctr) { t_ctr.set(m_lower_level, MasterId); }
void make_upper_level(const Ctr& t_ctr) { t_ctr.set(m_level, MasterId); }

void make_leader(const QCtr& t_ctr) { t_ctr.set(m_lower_level, MasterId); }
void make_upper_level(const QCtr& t_ctr) { t_ctr.set(m_level, MasterId); }

void make_lower_level(const Var& t_var) { t_var.set(m_lower_level, 0); }
void make_lower_level(const Var& t_var) { t_var.set(m_level, 0); }

void make_follower(const Ctr& t_ctr) { t_ctr.set(m_lower_level, 0); }
void make_lower_level(const Ctr& t_ctr) { t_ctr.set(m_level, 0); }

void make_lower_level(const QCtr& t_ctr) { t_ctr.set(m_lower_level, 0); }
void make_lower_level(const QCtr& t_ctr) { t_ctr.set(m_level, 0); }

void set_lower_objective(QuadExpr<Var> t_objective) { m_follower_objective = std::move(t_objective); }
void set_lower_level_obj(QuadExpr<Var> t_objective) { m_follower_objective = std::move(t_objective); }

[[nodiscard]] bool is_leader(const Var& t_var) const { return t_var.get(m_lower_level) == MasterId; }
[[nodiscard]] bool is_leader(const Var& t_var) const { return t_var.get(m_level) == MasterId; }

[[nodiscard]] bool is_leader(const Ctr& t_ctr) const { return t_ctr.get(m_lower_level) == MasterId; }
[[nodiscard]] bool is_leader(const Ctr& t_ctr) const { return t_ctr.get(m_level) == MasterId; }

[[nodiscard]] bool is_leader(const QCtr& t_ctr) const { return t_ctr.get(m_lower_level) == MasterId; }
[[nodiscard]] bool is_leader(const QCtr& t_ctr) const { return t_ctr.get(m_level) == MasterId; }

[[nodiscard]] bool is_follower(const Var& t_var) const { return t_var.get(m_lower_level) != MasterId; }
[[nodiscard]] bool is_follower(const Var& t_var) const { return t_var.get(m_level) != MasterId; }

[[nodiscard]] bool is_follower(const Ctr& t_ctr) const { return t_ctr.get(m_lower_level) != MasterId; }
[[nodiscard]] bool is_follower(const Ctr& t_ctr) const { return t_ctr.get(m_level) != MasterId; }

[[nodiscard]] bool is_follower(const QCtr& t_ctr) const { return t_ctr.get(m_lower_level) != MasterId; }
[[nodiscard]] bool is_follower(const QCtr& t_ctr) const { return t_ctr.get(m_level) != MasterId; }
};

#endif //IDOL_BILEVEL_DESCRIPTION_H
8 changes: 4 additions & 4 deletions lib/include/idol/mixed-integer/modeling/expressions/AffExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace idol {
template<class KeyT = idol::Var, class ValueT = double>
class idol::AffExpr {
LinExpr<KeyT, ValueT> m_linear;
double m_constant = 0.;
ValueT m_constant = 0.;
public:
AffExpr();
AffExpr(ValueT t_constant); // NOLINT(google-explicit-constructor)
Expand All @@ -43,11 +43,11 @@ class idol::AffExpr {
LinExpr<KeyT, ValueT>& linear() { return m_linear; }
[[nodiscard]] const LinExpr<KeyT, ValueT>& linear() const { return m_linear; }

double& constant() { return m_constant; }
ValueT& constant() { return m_constant; }

[[nodiscard]] double constant() const { return m_constant; }
[[nodiscard]] const ValueT& constant() const { return m_constant; }

[[nodiscard]] bool is_zero(double t_tolerance) const { return std::abs(constant()) < t_tolerance && linear().is_zero(t_tolerance); }
[[nodiscard]] bool is_zero(double t_tolerance) const { return ::idol::is_zero(constant(), t_tolerance) && linear().is_zero(t_tolerance); }

void clear() {
constant() = 0;
Expand Down
84 changes: 53 additions & 31 deletions lib/include/idol/mixed-integer/optimizers/padm/Formulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "idol/mixed-integer/modeling/models/Model.h"
#include "idol/general/utils/Map.h"
#include "idol/general/utils/GenerationPattern.h"
#include <variant>

namespace idol {
class PenaltyUpdate;
Expand All @@ -21,74 +23,94 @@ class idol::ADM::Formulation {
public:
Formulation(const Model& t_src_model,
Annotation<unsigned int> t_decomposition,
std::optional<Annotation<bool>> t_penalized_constraints,
bool t_independent_penalty_update,
std::pair<bool, double> t_rescaling);
std::optional<Annotation<double>> t_penalized_constraints,
double t_rescaling_threshold);

Model& sub_problem(const Var& t_var);
struct SubProblem {

[[nodiscard]] const Model& sub_problem(const Var& t_var) const;
struct RhsFixation {
Ctr ctr;
QuadExpr<Var> rhs_pattern;

RhsFixation(Ctr t_sub_problem_ctr, QuadExpr<Var> t_pattern)
: ctr(std::move(t_sub_problem_ctr)), rhs_pattern(std::move(t_pattern)) {}
};

struct RowFixation {
std::variant<Ctr, QCtr> ctr;
QuadExpr<Var, QuadExpr<Var>> row;

RowFixation(const Ctr& t_sub_problem_ctr, QuadExpr<Var, QuadExpr<Var>> t_row) : ctr(t_sub_problem_ctr), row(std::move(t_row)) {}
};

Model model;
std::list<Var> l1_epigraph_vars;
std::list<RhsFixation> rhs_fixations;
std::list<RowFixation> row_fixations;
QuadExpr<Var, QuadExpr<Var>> obj_fixation;

explicit SubProblem(Env& t_env);
};

SubProblem& sub_problem(const Var& t_var);

[[nodiscard]] const SubProblem& sub_problem(const Var& t_var) const;

[[nodiscard]] unsigned int sub_problem_id(const Var& t_var) const;

Model& sub_problem(unsigned int t_sub_problem_id) { return m_sub_problems[t_sub_problem_id]; }
SubProblem& sub_problem(unsigned int t_sub_problem_id) { return m_sub_problems[t_sub_problem_id]; }

[[nodiscard]] const Model& sub_problem(unsigned int t_sub_problem_id) const { return m_sub_problems[t_sub_problem_id]; }
[[nodiscard]] const SubProblem& sub_problem(unsigned int t_sub_problem_id) const { return m_sub_problems[t_sub_problem_id]; }

[[nodiscard]] unsigned int n_sub_problems() const { return m_sub_problems.size(); }

auto sub_problems() { return IteratorForward(m_sub_problems); }

[[nodiscard]] auto sub_problems() const { return ConstIteratorForward(m_sub_problems); }

[[nodiscard]] auto l1_vars(unsigned int t_sub_problem_id) const { return ConstIteratorForward(m_l1_vars_in_sub_problem[t_sub_problem_id]); }

[[nodiscard]] bool has_penalized_constraints() const { return m_penalized_constraints.has_value(); }
[[nodiscard]] auto l1_epigraph_vars() const { return ConstIteratorForward(m_l1_epigraph_vars); }

void fix_sub_problem(unsigned int t_sub_problem_id, const std::vector<PrimalPoint>& t_primals);
[[nodiscard]] bool has_penalized_constraints() const { return m_initial_penalty_parameters.has_value(); }

void initialize_penalty_parameters(double t_value);
void initialize_penalty_parameters(bool t_use_inverse_penalties);

bool update_penalty_parameters(const std::vector<PrimalPoint>& t_primals, PenaltyUpdate& t_penalty_update); // Returns true if penalty parameters have been resacled

void update(unsigned int t_sub_problem_id, const std::vector<PrimalPoint>& t_primals);

struct CurrentPenalty {
const Ctr constraint;
const Var variable;
const double max_violation;
double penalty;
CurrentPenalty(Ctr t_constraint, Var t_variable, double t_max_violation, double t_penalty)
: constraint(std::move(t_constraint)), variable(std::move(t_variable)), max_violation(t_max_violation), penalty(t_penalty) {}
CurrentPenalty(Var t_variable, double t_max_violation, double t_penalty)
: variable(std::move(t_variable)), max_violation(t_max_violation), penalty(t_penalty) {}
};

private:
Annotation<unsigned int> m_decomposition;
std::optional<Annotation<bool>> m_penalized_constraints;
bool m_independent_penalty_update;
std::pair<bool, double> m_rescaling;
std::optional<Annotation<double>> m_initial_penalty_parameters;
double m_rescaling_threshold;

std::vector<Model> m_sub_problems;
std::vector<std::optional<QuadExpr<Var>>> m_objective_patterns;
std::vector<std::list<std::pair<Ctr, AffExpr<Var>>>> m_constraint_patterns; // as ctr: row <= 0
std::vector<std::list<std::pair<QCtr, AffExpr<Var>>>> m_qconstraint_patterns; // as ctr: row <= 0
std::vector<std::list<Var>> m_l1_vars_in_sub_problem;
Map<Ctr, Var> m_l1_vars;
std::vector<SubProblem> m_sub_problems;
Map<unsigned int, Var> m_l1_epigraph_vars; // object id -> l1 epigraph variable

[[nodiscard]] unsigned int compute_n_sub_problems(const Model& t_src_model) const;
void initialize_sub_problems(const Model& t_src_model, unsigned int n_sub_problems);
void initialize_patterns(const Model& t_src_model, unsigned int n_sub_problems);
void initialize_slacks(const Model& t_src_model, unsigned int n_sub_problems);
void dispatch_vars(const Model& t_src_model);
void dispatch_ctrs(const Model& t_src_model);
void dispatch_ctr(const Model& t_src_model, const Ctr& t_ctr, unsigned int t_sub_problem_id);
void dispatch_qctrs(const Model& t_src_model);
void dispatch_qctr(const Model& t_src_model, const QCtr& t_ctr, unsigned int t_sub_problem_id);
void dispatch_obj(const Model& t_src_model);
void dispatch_obj(const Model& t_src_model, unsigned int t_sub_problem_id);
std::pair<AffExpr<Var>, bool> dispatch(const Model& t_src_model, const LinExpr<Var>& t_lin_expr, /* const QuadExpr<Var>& t_quad_expr, */ unsigned int t_sub_problem_id);
Var get_or_create_l1_var(const Ctr& t_ctr);
double evaluate(const QuadExpr<Var>& t_expr, const std::vector<Point<Var>>& t_primals);
QuadExpr<Var> evaluate(const QuadExpr<Var, QuadExpr<Var>>& t_expr, const std::vector<Point<Var>>& t_primals);
std::pair<LinExpr<Var>, AffExpr<Var>> dispatch(const LinExpr<Var>& t_expr, unsigned int t_sub_problem_id);
void set_penalty_in_all_sub_problems(const Var& t_var, double t_value);
void update_penalty_parameters_independently(const std::vector<PrimalPoint>& t_primals, PenaltyUpdate& t_penalty_update);
bool rescale_penalty_parameters(std::list<CurrentPenalty>& t_penalties);

double fix(const Constant& t_constant, const std::vector<PrimalPoint>& t_primals);
LinExpr<Var> add_l1_vars(const Ctr& t_ctr, CtrType t_type, unsigned int t_sub_problem_id);
LinExpr<Var> add_l1_vars(const QCtr& t_ctr, CtrType t_type, unsigned int t_sub_problem_id);
LinExpr<Var> add_l1_vars(unsigned int t_ctr_id, double t_initial_penalty_parameter, CtrType t_type, unsigned int t_sub_problem_id);
};


Expand Down
Loading

0 comments on commit c361251

Please sign in to comment.