Skip to content

Commit

Permalink
Several changes
Browse files Browse the repository at this point in the history
1. Adding a few more test cases

2. Adding support for quadratic objectives in Highs

3. Adding Variable::fix()
  • Loading branch information
whart222 committed May 7, 2024
1 parent 94eb2ba commit 4505ba5
Show file tree
Hide file tree
Showing 9 changed files with 405 additions and 213 deletions.
6 changes: 6 additions & 0 deletions lib/coek/coek/api/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ Variable& Variable::fix(double value)
return *this;
}

Variable& Variable::fix()
{
repn->fixed = true;
return *this;
}

Variable& Variable::fixed(bool _flag)
{
repn->fixed = _flag;
Expand Down
2 changes: 2 additions & 0 deletions lib/coek/coek/api/expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ class Variable {
Variable& fixed(bool flag);
/** Set the variable value and declare the variable fixed. \returns the variable object. */
Variable& fix(double value);
/** Set the variable value and declare the variable fixed. \returns the variable object. */
Variable& fix();
/** \returns \c true if the variable is fixed */
bool fixed() const;

Expand Down
308 changes: 154 additions & 154 deletions lib/coek/coek/solvers/gurobi/gurobi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ void add_gurobi_objective(GRBModel* gmodel, Expression& expr, bool sense,
std::unordered_map<int, GRBVar>& x, coek::QuadraticExpr& orepn)
{
orepn.reset();
// coek::QuadraticExpr orepn;
orepn.collect_terms(expr);

if (orepn.linear_coefs.size() + orepn.quadratic_coefs.size() > 0) {
GRBLinExpr term1;
auto iv = orepn.linear_vars.begin();
Expand Down Expand Up @@ -135,128 +135,46 @@ GurobiSolver::~GurobiSolver()
delete env;
}

void GurobiSolver::collect_results(Model& model, std::shared_ptr<SolverResults>& results)
void GurobiSolver::set_gurobi_options()
{
try {
int status = gmodel->get(GRB_IntAttr_Status);
if (status == GRB_OPTIMAL) {
results->termination_condition = TerminationCondition::convergence_criteria_satisfied;
results->solution_status = SolutionStatus::optimal;
results->objective_value = gmodel->getObjective().getValue();
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBound);
results->objective_bound = value;
}
catch (GRBException) {
}
if (not results->objective_bound.has_value()) {
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBoundC);
results->objective_bound = value;
}
catch (GRBException) {
}
}
// All options are converted to strings for Gurobi
for (auto& it : string_options())
gmodel->set(it.first, it.second);
for (auto& it : boolean_options())
gmodel->set(it.first, std::to_string(it.second));
for (auto& it : integer_options())
gmodel->set(it.first, std::to_string(it.second));
for (auto& it : double_options())
gmodel->set(it.first, std::to_string(it.second));
}

// Collect values of Gurobi variables
for (auto& var : model.repn->variables) {
std::shared_ptr<coek::VariableTerm>& v = var.repn;
if (not v->fixed) {
v->set_value(x[v->index].get(GRB_DoubleAttr_X));
}
}
}
else if (status == GRB_SUBOPTIMAL) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->solution_status = SolutionStatus::feasible;
results->objective_value = gmodel->getObjective().getValue();
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBound);
results->objective_bound = value;
}
catch (GRBException) {
}
if (not results->objective_bound.has_value()) {
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBoundC);
results->objective_bound = value;
}
catch (GRBException) {
}
}
results->error_message
= "Unable to satisfy optimality tolerances; a sub-optimal solution is available";
void GurobiSolver::pre_solve()
{
results = std::make_shared<SolverResults>();
results->solver_name = "gurobi";
results->termination_condition = TerminationCondition::error;
results->tic();

// Collect values of Gurobi variables
for (auto& var : model.repn->variables) {
std::shared_ptr<coek::VariableTerm>& v = var.repn;
if (not v->fixed) {
v->set_value(x[v->index].get(GRB_DoubleAttr_X));
}
}
}
else if (status == GRB_INFEASIBLE) {
results->termination_condition = TerminationCondition::proven_infeasible;
results->solution_status = SolutionStatus::infeasible;
}
else if (status == GRB_UNBOUNDED) {
results->termination_condition = TerminationCondition::unbounded;
}
else if (status == GRB_ITERATION_LIMIT) {
results->termination_condition = TerminationCondition::iteration_limit;
}
else if (status == GRB_TIME_LIMIT) {
results->termination_condition = TerminationCondition::time_limit;
}
else if (status == GRB_INTERRUPTED) {
results->termination_condition = TerminationCondition::interrupted;
}
else if (status == GRB_USER_OBJ_LIMIT) {
results->termination_condition = TerminationCondition::objective_limit;
}
else if (status == GRB_WORK_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the work expended exceeded the value specified in the "
"WorkLimit parameter.";
}
else if (status == GRB_MEM_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the total amount of allocated memory exceeded the "
"value specified in the SoftMemLimit parameter.";
}
else if (status == GRB_NODE_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the total number of branch-and-cut nodes explored "
"exceeded the value specified in the NodeLimit parameter.";
}
else if (status == GRB_SOLUTION_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the number of solutions found reached the value "
"specified in the SolutionLimit parameter.";
}
else if (status == GRB_CUTOFF) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because optimal objective for model was proven to be worse "
"than the value specified in the Cutoff parameter.";
}
else if (status == GRB_NUMERIC) {
results->termination_condition = TerminationCondition::unknown;
results->error_message
= "Gurobi Error: Optimization was terminated due to unrecoverable numerical "
"difficulties.";
}
}
catch (GRBException e) {
results->termination_condition = TerminationCondition::unknown;
results->error_message = "GUROBI Exception: (results) " + e.getMessage();
if (initial_solve()) {
env = new GRBEnv(true);
auto it = integer_options().find("OutputFlag");
if (it != integer_options().end())
env->set(GRB_IntParam_OutputFlag, it->second);
env->start();
gmodel = new GRBModel(*env);
}
}

void GurobiSolver::post_solve()
{
delete gmodel;
gmodel = 0;
delete env;
env = 0;

results->toc();
}

std::shared_ptr<SolverResults> GurobiSolver::solve(Model& model)
{
pre_solve();
Expand Down Expand Up @@ -334,33 +252,6 @@ std::shared_ptr<SolverResults> GurobiSolver::solve(Model& model)
return results;
}

void GurobiSolver::pre_solve()
{
results = std::make_shared<SolverResults>();
results->solver_name = "gurobi";
results->termination_condition = TerminationCondition::error;
results->tic();

if (initial_solve()) {
env = new GRBEnv(true);
auto it = integer_options().find("OutputFlag");
if (it != integer_options().end())
env->set(GRB_IntParam_OutputFlag, it->second);
env->start();
gmodel = new GRBModel(*env);
}
}

void GurobiSolver::post_solve()
{
delete gmodel;
gmodel = 0;
delete env;
env = 0;

results->toc();
}

#ifdef COEK_WITH_COMPACT_MODEL
std::shared_ptr<SolverResults> GurobiSolver::solve(CompactModel& compact_model)
{
Expand Down Expand Up @@ -615,17 +506,126 @@ std::shared_ptr<SolverResults> GurobiSolver::resolve()
return results;
}

void GurobiSolver::set_gurobi_options()
void GurobiSolver::collect_results(Model& model, std::shared_ptr<SolverResults>& results)
{
// All options are converted to strings for Gurobi
for (auto& it : string_options())
gmodel->set(it.first, it.second);
for (auto& it : boolean_options())
gmodel->set(it.first, std::to_string(it.second));
for (auto& it : integer_options())
gmodel->set(it.first, std::to_string(it.second));
for (auto& it : double_options())
gmodel->set(it.first, std::to_string(it.second));
try {
int status = gmodel->get(GRB_IntAttr_Status);
if (status == GRB_OPTIMAL) {
results->termination_condition = TerminationCondition::convergence_criteria_satisfied;
results->solution_status = SolutionStatus::optimal;
results->objective_value = gmodel->getObjective().getValue();
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBound);
results->objective_bound = value;
}
catch (GRBException) {
}
if (not results->objective_bound.has_value()) {
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBoundC);
results->objective_bound = value;
}
catch (GRBException) {
}
}

// Collect values of Gurobi variables
for (auto& var : model.repn->variables) {
std::shared_ptr<coek::VariableTerm>& v = var.repn;
if (not v->fixed) {
v->set_value(x[v->index].get(GRB_DoubleAttr_X));
}
}
}
else if (status == GRB_SUBOPTIMAL) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->solution_status = SolutionStatus::feasible;
results->objective_value = gmodel->getObjective().getValue();
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBound);
results->objective_bound = value;
}
catch (GRBException) {
}
if (not results->objective_bound.has_value()) {
try {
double value = gmodel->get(GRB_DoubleAttr_ObjBoundC);
results->objective_bound = value;
}
catch (GRBException) {
}
}
results->error_message
= "Unable to satisfy optimality tolerances; a sub-optimal solution is available";

// Collect values of Gurobi variables
for (auto& var : model.repn->variables) {
std::shared_ptr<coek::VariableTerm>& v = var.repn;
if (not v->fixed) {
v->set_value(x[v->index].get(GRB_DoubleAttr_X));
}
}
}
else if (status == GRB_INFEASIBLE) {
results->termination_condition = TerminationCondition::proven_infeasible;
results->solution_status = SolutionStatus::infeasible;
}
else if (status == GRB_UNBOUNDED) {
results->termination_condition = TerminationCondition::unbounded;
}
else if (status == GRB_ITERATION_LIMIT) {
results->termination_condition = TerminationCondition::iteration_limit;
}
else if (status == GRB_TIME_LIMIT) {
results->termination_condition = TerminationCondition::time_limit;
}
else if (status == GRB_INTERRUPTED) {
results->termination_condition = TerminationCondition::interrupted;
}
else if (status == GRB_USER_OBJ_LIMIT) {
results->termination_condition = TerminationCondition::objective_limit;
}
else if (status == GRB_WORK_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the work expended exceeded the value specified in the "
"WorkLimit parameter.";
}
else if (status == GRB_MEM_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the total amount of allocated memory exceeded the "
"value specified in the SoftMemLimit parameter.";
}
else if (status == GRB_NODE_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the total number of branch-and-cut nodes explored "
"exceeded the value specified in the NodeLimit parameter.";
}
else if (status == GRB_SOLUTION_LIMIT) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because the number of solutions found reached the value "
"specified in the SolutionLimit parameter.";
}
else if (status == GRB_CUTOFF) {
results->termination_condition = TerminationCondition::other_termination_limit;
results->error_message
= "Gurobi terminated because optimal objective for model was proven to be worse "
"than the value specified in the Cutoff parameter.";
}
else if (status == GRB_NUMERIC) {
results->termination_condition = TerminationCondition::unknown;
results->error_message
= "Gurobi Error: Optimization was terminated due to unrecoverable numerical "
"difficulties.";
}
}
catch (GRBException e) {
results->termination_condition = TerminationCondition::unknown;
results->error_message = "GUROBI Exception: (results) " + e.getMessage();
}
}

} // namespace coek
4 changes: 4 additions & 0 deletions lib/coek/coek/solvers/highs/coek_highs.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <memory>
#include "Highs.h"
#include "coek/solvers/solver_repn.hpp"

Expand All @@ -10,6 +11,7 @@ class HighsSolver : public SolverRepn {
Highs highs;
HighsModel hmodel;
HighsStatus return_status;
std::shared_ptr<SolverResults> results;
std::unordered_map<size_t, size_t> x;

public:
Expand All @@ -26,6 +28,8 @@ class HighsSolver : public SolverRepn {

protected:
void collect_results(Model& model, std::shared_ptr<SolverResults>& results);
void pre_solve();
void post_solve();
};

} // namespace coek
Loading

0 comments on commit 4505ba5

Please sign in to comment.