diff --git a/dev/main.cpp b/dev/main.cpp index 38276f4a..b0f6f7ca 100644 --- a/dev/main.cpp +++ b/dev/main.cpp @@ -40,7 +40,7 @@ int main(int t_argc, const char** t_argv) { // Uncertainty set Model uncertainty_set(env); - const double Gamma = 0; + const double Gamma = 2; 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); @@ -56,7 +56,7 @@ int main(int t_argc, const char** t_argv) { 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]); + description.set_uncertain_rhs(c, -instance.capacity(i) * xi[j]); } } @@ -68,6 +68,7 @@ int main(int t_argc, const char** t_argv) { for (unsigned int i = 0 ; i < n_facilities ; ++i) { for (unsigned int j = 0 ; j < n_customers ; ++j) { model.add_ctr(y[i][j] <= x[i]); + description.set_stage(y[i][j], 1); } } diff --git a/lib/include/idol/robust/optimizers/column-and-constraint-generation/Formulation.h b/lib/include/idol/robust/optimizers/column-and-constraint-generation/Formulation.h index 44b85c37..e120bc52 100644 --- a/lib/include/idol/robust/optimizers/column-and-constraint-generation/Formulation.h +++ b/lib/include/idol/robust/optimizers/column-and-constraint-generation/Formulation.h @@ -15,8 +15,25 @@ namespace idol::CCG { class idol::CCG::Formulation { const Model& m_parent; const ::idol::Robust::Description &m_description; + + Model m_master; + std::vector m_second_stage_variables; + std::vector m_second_stage_constraints; + + unsigned int m_n_added_scenario = 0; + std::optional m_second_stage_epigraph; + + void parse_variables(); + void parse_objective(); + void parse_constraints(); public: Formulation(const Model& t_parent, const ::idol::Robust::Description &t_description); + + Model& master() { return m_master; } + + const Model& master() const { return m_master; } + + void add_scenario_to_master(const Point& t_scenario); }; #endif //IDOL_CCG_FORMULATION_H diff --git a/lib/src/bilevel/modeling/read_from_file.cpp b/lib/src/bilevel/modeling/read_from_file.cpp index f5c35fae..0db4e73d 100644 --- a/lib/src/bilevel/modeling/read_from_file.cpp +++ b/lib/src/bilevel/modeling/read_from_file.cpp @@ -95,7 +95,7 @@ void AuxParser::read_aux_file(const std::string &t_path_to_aux, const std::funct if (read_tag(file, "@MPS", false) || read_tag(file, "@LP", false)) { read_file(file, t_path_to_aux, t_read_model_from_file); } else { - throw Exception("Parsing error: could not parse LP or MPS tags."); + throw Exception("Parsing error: could not parse_variables LP or MPS tags."); } file.close(); @@ -190,7 +190,7 @@ bool AuxParser::read_tag(std::ifstream &t_file, const std::string &t_tag, bool t return false; } - throw Exception("Parsing error, could not parse tag: " + t_tag); + throw Exception("Parsing error, could not parse_variables tag: " + t_tag); } diff --git a/lib/src/mixed-integer/optimizers/wrappers/GLPK/Optimizers_GLPK.cpp b/lib/src/mixed-integer/optimizers/wrappers/GLPK/Optimizers_GLPK.cpp index 519c6447..efc09da1 100644 --- a/lib/src/mixed-integer/optimizers/wrappers/GLPK/Optimizers_GLPK.cpp +++ b/lib/src/mixed-integer/optimizers/wrappers/GLPK/Optimizers_GLPK.cpp @@ -766,7 +766,7 @@ idol::Model idol::Optimizers::GLPK::read_from_lp_file(idol::Env &t_env, const st glp_prob* model = glp_create_prob(); auto result = glp_read_lp(model, NULL, t_filename.c_str()); if (result != 0) { - throw Exception("Could not parse MPS file."); + throw Exception("Could not parse_variables MPS file."); } return read_from_glpk(t_env, model); @@ -779,7 +779,7 @@ idol::Model idol::Optimizers::GLPK::read_from_mps_file(idol::Env &t_env, const s glp_prob *model = glp_create_prob(); auto result = glp_read_mps(model, use_fixed_format ? GLP_MPS_DECK : GLP_MPS_FILE, NULL, t_filename.c_str()); if (result != 0) { - throw Exception("Could not parse MPS file."); + throw Exception("Could not parse_variables MPS file."); } return read_from_glpk(t_env, model); } diff --git a/lib/src/mixed-integer/problems/facility-location-problem/FLP_Instance.cpp b/lib/src/mixed-integer/problems/facility-location-problem/FLP_Instance.cpp index 5f447b6c..3641bed9 100644 --- a/lib/src/mixed-integer/problems/facility-location-problem/FLP_Instance.cpp +++ b/lib/src/mixed-integer/problems/facility-location-problem/FLP_Instance.cpp @@ -22,11 +22,11 @@ idol::Problems::FLP::Instance idol::Problems::FLP::read_instance_2021_Cheng_et_a auto data = parse_delimited(t_filename, '\t'); if (data[0][0] != "FacNum") { - throw Exception("Could not parse instance, missing header FacNum."); + throw Exception("Could not parse_variables instance, missing header FacNum."); } if (data[0][2] != "CustNum") { - throw Exception("Could not parse instance, missing header CustNum."); + throw Exception("Could not parse_variables instance, missing header CustNum."); } unsigned int n_facilities = std::stoul(data[0][1]); diff --git a/lib/src/robust/optimizers/column-and-constraint-generation/Formulation.cpp b/lib/src/robust/optimizers/column-and-constraint-generation/Formulation.cpp index ff3c030b..4b7fe344 100644 --- a/lib/src/robust/optimizers/column-and-constraint-generation/Formulation.cpp +++ b/lib/src/robust/optimizers/column-and-constraint-generation/Formulation.cpp @@ -2,9 +2,147 @@ // Created by henri on 11.12.24. // #include +#include +#include +idol::CCG::Formulation::Formulation(const Model &t_parent, const ::idol::Robust::Description &t_description) + : m_parent(t_parent), m_description(t_description), m_master(t_parent.env()) { -idol::CCG::Formulation::Formulation(const idol::Model &t_parent, const ::idol::Robust::Description &t_description) - : m_parent(t_parent), m_description(t_description) { + parse_variables(); + parse_objective(); + parse_constraints(); } + +void idol::CCG::Formulation::parse_variables() { + + for (const auto& var : m_parent.vars()) { + + if (m_description.stage(var) > 0) { + m_second_stage_variables.emplace_back(var); + continue; + } + + const double lb = m_parent.get_var_lb(var); + const double ub = m_parent.get_var_ub(var); + const auto type = m_parent.get_var_type(var); + const double obj = m_parent.get_var_obj(var); + + m_master.add(var, TempVar(lb, ub, type, obj, LinExpr())); + + } + +} + +void idol::CCG::Formulation::parse_objective() { + + const auto& objective = m_parent.get_obj_expr(); + + if (!objective.has_quadratic()) { + return; // Because the objective has been added while parsing the variables + } + + auto master_objective = m_master.get_obj_expr(); + + for (const auto& [pair, coeff] : objective) { + + if (m_description.stage(pair.first) > 0) { + continue; + } + + if (m_description.stage(pair.second) > 0) { + continue; + } + + master_objective += coeff * pair.first * pair.second; + + } + +} + +void idol::CCG::Formulation::parse_constraints() { + + for (const auto& ctr : m_parent.ctrs()) { + + const auto& row = m_parent.get_ctr_row(ctr); + + const bool has_second_stage = std::any_of(row.begin(), row.end(), [this](const auto& term) { + return m_description.stage(term.first) > 0; + }); + + if (has_second_stage || !m_description.uncertain_mat_coeffs(ctr).empty()) { + m_second_stage_constraints.emplace_back(ctr); + continue; + } + + const auto type = m_parent.get_ctr_type(ctr); + const double rhs = m_parent.get_ctr_rhs(ctr); + + m_master.add(ctr, TempCtr(LinExpr(row), type, rhs)); + } + +} + +void idol::CCG::Formulation::add_scenario_to_master(const idol::Point &t_scenario) { + + std::vector> new_vars; + new_vars.resize(m_parent.vars().size()); + + // Add Variables + for (const auto& var : m_second_stage_variables) { + + const double lb = m_parent.get_var_lb(var); + const double ub = m_parent.get_var_ub(var); + const auto type = m_parent.get_var_type(var); + const auto index = m_parent.get_var_index(var); + + new_vars[index] = m_master.add_var(lb, ub, type, 0, var.name() + "_" + std::to_string(m_n_added_scenario)); + + } + + // Add Constraints + for (const auto& ctr : m_second_stage_constraints) { + + const auto& row = m_parent.get_ctr_row(ctr); + const auto type = m_parent.get_ctr_type(ctr); + double rhs = m_parent.get_ctr_rhs(ctr); + + LinExpr new_row; + for (const auto& [var, coeff] : row) { + + if (m_description.stage(var) == 0) { + new_row += coeff * var; + continue; + } + + new_row += coeff * new_vars[m_parent.get_var_index(var)].value(); + } + + const auto uncertainties = m_description.uncertain_mat_coeffs(ctr); + for (const auto& [var, coeff] : uncertainties) { + new_row += evaluate(coeff, t_scenario) * new_vars[m_parent.get_var_index(var)].value(); + } + + rhs += evaluate(m_description.uncertain_rhs(ctr), t_scenario); + + m_master.add_ctr(TempCtr(std::move(new_row), type, rhs), ctr.name() + "_" + std::to_string(m_n_added_scenario)); + + } + + if (!m_second_stage_epigraph) { + m_second_stage_epigraph = m_master.add_var(-Inf, Inf, Continuous, 1, "second_stage_epigraph"); + } + + // Add Objective + LinExpr objective; + for (const auto& var : m_second_stage_variables) { + objective += m_parent.get_var_obj(var) * new_vars[m_parent.get_var_index(var)].value(); + } + for (const auto& [var, coeff] : m_description.uncertain_obj()) { + objective += evaluate(coeff, t_scenario) * new_vars[m_parent.get_var_index(var)].value(); + } + + m_master.add_ctr(*m_second_stage_epigraph >= std::move(objective)); + + ++m_n_added_scenario; +} diff --git a/lib/src/robust/optimizers/column-and-constraint-generation/Optimizers_ColumnAndConstraintGeneration.cpp b/lib/src/robust/optimizers/column-and-constraint-generation/Optimizers_ColumnAndConstraintGeneration.cpp index 61bf2148..32b0fbc4 100644 --- a/lib/src/robust/optimizers/column-and-constraint-generation/Optimizers_ColumnAndConstraintGeneration.cpp +++ b/lib/src/robust/optimizers/column-and-constraint-generation/Optimizers_ColumnAndConstraintGeneration.cpp @@ -69,7 +69,7 @@ void idol::Optimizers::Robust::ColumnAndConstraintGeneration::remove(const idol: } void idol::Optimizers::Robust::ColumnAndConstraintGeneration::update() { - throw Exception("Not implemented update"); + } void idol::Optimizers::Robust::ColumnAndConstraintGeneration::write(const std::string &t_name) { @@ -78,6 +78,10 @@ void idol::Optimizers::Robust::ColumnAndConstraintGeneration::write(const std::s void idol::Optimizers::Robust::ColumnAndConstraintGeneration::hook_before_optimize() { Optimizer::hook_before_optimize(); + + m_formulation = std::make_unique(parent(), m_description); + m_formulation->master().use(*m_master_optimizer); + } void idol::Optimizers::Robust::ColumnAndConstraintGeneration::hook_optimize() {