Program Listing for File lcp.cpp

Return to documentation for file (src/mathopt/lcp/lcp.cpp)

/* #############################################
 *             This file is part of
 *                    ZERO
 *
 *             Copyright (c) 2020
 *     Released under the Creative Commons
 *         CC BY-NC-SA 4.0 License
 *
 *              Find out more at
 *        https://github.com/ds4dm/ZERO
 * #############################################*/

//
#include "mathopt/lcp/lcp.h"
#include "solvers/PathSolver.h"


class LCP_PATHStart : public GRBCallback {
public:
  bool              done     = false;
  unsigned long int minNodes = 2500;
  GRBVar              *Vars     = {};
  unsigned long int numVars;
  MathOpt::LCP     *LCP;
  LCP_PATHStart(MathOpt::LCP *LCPin, GRBVar *vars, unsigned long int numvars) {
     this->LCP     = LCPin;
     this->numVars = numvars;
     this->Vars    = vars;
  }

protected:
  void callback() override {
     try {
        // In MIP
        if (this->where == GRB_CB_MIPNODE) {
          // Trigger just one time
          if (!this->done) {
             // Statistics
             double numExploredNodes = getDoubleInfo(GRB_CB_MIPNODE_NODCNT);
             int    numSols          = getIntInfo(GRB_CB_MIPNODE_SOLCNT);
             // No solutions found. Check the minimum explored node threshold

             if (numExploredNodes >= minNodes) {
                arma::vec x, z;
                double    obj = -GRB_INFINITY;
                this->done    = true;
                this->LCP->solve(Data::LCP::Algorithms::PATH, x, z, 15, 0, obj, 1);

                long len = std::min(long(x.size() + z.size()), long(this->numVars));
                if (len == this->numVars) {
                  double sol[len];
                  // Fill vector
                  unsigned long int count = 0;
                  for (unsigned long int i = 0; i < x.size(); ++i) {
                     // this->setSolution(this->Vars[i],x.at(i));
                     sol[i] = Utils::isEqual(x.at(i), 0, 1e-6, 1 - 1e-4) ? 0 : x.at(i);
                     // std::cout << this->Vars[i].get(GRB_StringAttr_VarName) << std::endl;
                     // this->setSolution(this->Vars[i],sol[i]);
                     // GRBLinExpr expr = {this->Vars[i]};
                     // this->addCut(expr,GRB_EQUAL,sol[i]);
                     count++;
                  }
                  for (unsigned long int i = 0; i < z.size() && x.size() + i < len; ++i) {
                     sol[i + count] = Utils::isEqual(z.at(i), 0, 1e-6, 1 - 1e-4) ? 0 : z.at(i);
                     // GRBLinExpr expr = {this->Vars[i+count]};
                     // this->addCut(expr,GRB_EQUAL,sol[i+count]);
                  }

                  // Set the solution
                  this->setSolution(this->Vars, sol, len);
                  double objtest = this->useSolution();
                  this->done     = true;
                  if (objtest != GRB_INFINITY)
                     LOG_S(INFO) << "LCP_PATHStart::callback: Generated a feasible improving solution "
                                         "with PATH.";
                  else {
                     for (unsigned long int i = 0; i < x.size(); ++i)
                        this->setSolution(this->Vars[i], x.at(i));
                     objtest = this->useSolution();
                     if (objtest == GRB_INFINITY)
                        LOG_S(INFO)
                             << "LCP_PATHStart::callback: Failed to generate a feasible (improving) "
                                 "solution with PATH.";
                  }
                }
             }
          }
        }
     } catch (GRBException &e) {
        throw ZEROException(e);
     } catch (...) {
        throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in LCP_PATHStart::callback");
     }
  }
};

void MathOpt::LCP::defConst(GRBEnv *env)

{
  this->Env = env;
  this->nR  = this->M.n_rows;
  this->nC  = this->M.n_cols;
  int diff  = this->nC - this->BoundsX.size();
  ZEROAssert(diff >= 0);
  if (diff > 0)
     for (int i = 0; i < diff; ++i)
        this->BoundsX.push_back({-GRB_INFINITY, GRB_INFINITY});

  this->processBounds();
}


void MathOpt::LCP::processBounds() {
  unsigned long int              cnt = 0;
  std::vector<unsigned long int> shedded;
  for (auto c : this->Compl) {
     unsigned long int xVar = c.second;

     if (this->BoundsX.at(xVar).first == this->BoundsX.at(xVar).second)
        shedded.push_back(cnt);
     // Then we should remove this! The equation is useless

     ++cnt;
  }

  if (!shedded.empty()) {
     LOG_S(INFO) << "MathOpt::LCP::processBounds: Shedding " << shedded.size()
                     << " trivial complementarities.";
     std::sort(shedded.begin(), shedded.end());

     for (int i = shedded.size() - 1; i >= 0; --i) {
        for (int j = shedded.at(i); j < this->Compl.size(); ++j) {
          this->Compl.at(j).first--;
        }
        this->Compl.erase(this->Compl.begin() + shedded.at(i));
        this->M.shed_row(shedded.at(i));
        this->q.shed_row(shedded.at(i));
     }
     this->MadeRlxdModel = false;
  }

  this->nR = this->nR - shedded.size();
}


MathOpt::LCP::LCP(
     GRBEnv *env, arma::sp_mat &M, arma::vec &q, perps &Compl, arma::sp_mat &A, arma::vec &b)
     : M{M}, q{q}, A{A}, b{b}, RelaxedModel(*env) {

  this->Compl = perps(Compl);
  Utils::sortByKey(this->Compl);
  for (auto p : this->Compl)
     if (p.first != p.second) {
        this->LeadStart    = p.first;
        this->LeadEnd      = p.second - 1;
        this->NumberLeader = this->LeadEnd - this->LeadStart + 1;
        this->NumberLeader = this->NumberLeader > 0 ? this->NumberLeader : 0;
        break;
     }
  this->defConst(env);
}


MathOpt::LCP::LCP(GRBEnv           *env,
                        arma::sp_mat     &M,
                        arma::vec        &q,
                        unsigned long int leadStart,
                        unsigned          leadEnd,
                        arma::sp_mat     &A,
                        arma::vec        &b)
     : M{M}, q{q}, A{A}, b{b}, RelaxedModel(*env)

{
  this->LeadStart    = leadStart;
  this->LeadEnd      = leadEnd;
  this->NumberLeader = this->LeadEnd - this->LeadStart + 1;
  this->NumberLeader = this->NumberLeader > 0 ? this->NumberLeader : 0;
  for (unsigned long int i = 0; i < M.n_rows; i++) {
     unsigned long int count = i < leadStart ? i : i + NumberLeader;
     this->Compl.push_back({i, count});
  }
  Utils::sortByKey(this->Compl);
  this->defConst(env);
}

MathOpt::LCP::LCP(GRBEnv *env, const Game::NashGame &N) : RelaxedModel(*env) {
  arma::sp_mat   M_local;
  arma::vec      q_local;
  perps          Compl_local;
  VariableBounds NashBounds;
  N.formulateLCP(M_local, q_local, Compl_local, NashBounds);

  this->M     = M_local;
  this->q     = q_local;
  this->Compl = Compl_local;
  // Warning for you, user: check that you have anyway the bounds in the Nash game's LCP...
  this->BoundsX = NashBounds;
  if (this->BoundsX.size() < this->M.n_cols)
     for (unsigned long int i = this->BoundsX.size(); i < this->M.n_cols; ++i)
        this->BoundsX.push_back({0, GRB_INFINITY});
  this->A     = N.rewriteLeadCons();
  this->b     = N.getMCLeadRHS();
  this->Compl = perps(Compl);
  Utils::sortByKey(this->Compl);
  // Delete no more!
  for (auto p : this->Compl) {
     if (p.first != p.second) {
        this->LeadStart    = p.first;
        this->LeadEnd      = p.second - 1;
        this->NumberLeader = this->LeadEnd - this->LeadStart + 1;
        this->NumberLeader = this->NumberLeader > 0 ? this->NumberLeader : 0;
        break;
     }
  }
  this->defConst(env);
}
void MathOpt::LCP::makeRelaxed() {
  try {
     if (this->MadeRlxdModel)
        return;
     LOG_S(3) << "MathOpt::LCP::makeRelaxed: Creating the relaxed model";


     GRBVar x[nC], z[nR];
     for (unsigned long int i = 0; i < nC; i++) {
        x[i] = RelaxedModel.addVar(BoundsX.at(i).first,
                                            BoundsX.at(i).second > 0 ? BoundsX.at(i).second : GRB_INFINITY,
                                            // 0, GRB_INFINITY,
                                            1,
                                            GRB_CONTINUOUS,
                                            "x_" + std::to_string(i));
     }
     for (unsigned long int i = 0; i < nR; i++)
        z[i] = RelaxedModel.addVar(
             -GRB_INFINITY, GRB_INFINITY, 1, GRB_CONTINUOUS, "z_" + std::to_string(i));
     LOG_S(3) << "MathOpt::LCP::makeRelaxed: Added variables";

     // Define complementarities
     Utils::addSparseConstraints(M, -q, x, "zdef", &RelaxedModel, GRB_EQUAL, z);
     LOG_S(3) << "MathOpt::LCP::makeRelaxed: Added equation definitions";

     // If Ax<=b constraints are there, they should be included too!
     if (this->A.n_nonzero != 0 && this->b.n_rows != 0) {
        if (A.n_cols != nC || A.n_rows != b.n_rows) {
          LOG_S(1) << "(" << A.n_rows << "," << A.n_cols << ")\t" << b.n_rows << " " << nC;
          throw ZEROException(ZEROErrorCode::InvalidData, "A and b are incompatible");
        }
        Utils::addSparseConstraints(A, b, x, "commonCons", &RelaxedModel, GRB_LESS_EQUAL, nullptr);
        LOG_S(3) << "MathOpt::LCP::makeRelaxed: Added common constraints";
     }

     // Finalize and update.
     RelaxedModel.update();
     RelaxedModel.set(GRB_IntParam_OutputFlag, 0);
     this->MadeRlxdModel = true;

  } catch (GRBException &e) {
     throw ZEROException(e);
  } catch (...) {
     throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in makeRelaxed()");
  }
}


std::unique_ptr<GRBModel> MathOpt::LCP::LCPasMIP(bool              solve,
                                                                 double            timeLimit,
                                                                 unsigned long int threads,
                                                                 unsigned long int solLimit) {
  makeRelaxed();
  std::unique_ptr<GRBModel> model;
  try {
     if (this->PureMIP)
        model = this->getMIP(false);
     else
        model = this->getMINLP();
  } catch (GRBException &e) {
     throw ZEROException(e);
  } catch (...) {
     throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in makeRelaxed()");
  }

  if (timeLimit > 0)
     model->set(GRB_DoubleParam_TimeLimit, timeLimit);
  if (threads > 1)
     model->set(GRB_IntParam_Threads, threads);
  /*model->set(GRB_DoubleParam_IntFeasTol, this->Eps);
  model->set(GRB_DoubleParam_FeasibilityTol, this->Eps);
  model->set(GRB_DoubleParam_OptimalityTol, this->Eps);
    */
  model->set(GRB_IntParam_SolutionLimit, solLimit);
  model->set(GRB_IntParam_OutputFlag, 0);
  model->setObjective(GRBLinExpr{0}, GRB_MINIMIZE);
  this->setMIPObjective(*model);

  if (solve)
     model->optimize();

  return model;
}


bool MathOpt::LCP::extractSols(GRBModel *model, arma::vec &z, arma::vec &x, bool extractZ) const {
  if (model->get(GRB_IntAttr_Status) == GRB_LOADED)
     model->optimize();
  auto status = model->get(GRB_IntAttr_Status);
  if (!(status == GRB_OPTIMAL || status == GRB_SUBOPTIMAL || status == GRB_SOLUTION_LIMIT) ||
        (status == GRB_TIME_LIMIT && model->get(GRB_IntAttr_SolCount) == 0))
     return false;
  x.zeros(nC);
  for (unsigned long int i = 0; i < nC; i++)
     x.at(i) = model->getVarByName("x_" + std::to_string(i)).get(GRB_DoubleAttr_X);
  if (extractZ) {
     z.zeros(nR);
     for (unsigned long int i = 0; i < nR; i++)
        z.at(i) = model->getVarByName("z_" + std::to_string(i)).get(GRB_DoubleAttr_X);
  }
  return true;
}


arma::vec MathOpt::LCP::zFromX(const arma::vec &x) { return (this->M * x + this->q); }


std::unique_ptr<GRBModel> MathOpt::LCP::LCPasMILP(const arma::sp_mat &C,
                                                                  const arma::vec    &c,
                                                                  const arma::vec    &x_minus_i,
                                                                  bool                solve) {

  if (!this->PureMIP)
     LOG_S(1) << "MathOpt::LCP::LCPasMILP: Note that complementarities are bi-linearly modeled!";
  std::unique_ptr<GRBModel> model = this->LCPasMIP(true, -1, 1, 1);
  // Reset the solution limit. We need to solve to optimality
  model->set(GRB_IntParam_SolutionLimit, GRB_MAXINT);
  ZEROAssert(C.n_cols == x_minus_i.n_rows);
  ZEROAssert(c.n_rows == C.n_rows);
  arma::vec Cx(c.n_rows, arma::fill::zeros);
  try {
     Cx = C * x_minus_i;
  } catch (std::exception &e) {
     throw ZEROException(ZEROErrorCode::Numeric, e.what());
  } catch (std::string &e) {
     throw ZEROException(ZEROErrorCode::Numeric, e);
  }
  arma::vec  obj = c + Cx;
  GRBLinExpr expr{0};
  for (unsigned long int i = 0; i < obj.n_rows; i++)
     expr += obj.at(i) * model->getVarByName("x_" + std::to_string(i));
  model->setObjective(expr, GRB_MINIMIZE);
  model->set(GRB_IntParam_OutputFlag, 0);
  model->update();
  if (solve)
     model->optimize();
  return model;
}

std::unique_ptr<GRBModel> MathOpt::LCP::LCPasMIQP(const arma::sp_mat &Q,
                                                                  const arma::sp_mat &C,
                                                                  const arma::vec    &c,
                                                                  const arma::vec    &x_minus_i,
                                                                  bool                solve)

{
  auto model = this->LCPasMILP(C, c, x_minus_i, false);
  if (Q.n_nonzero != 0) // If Q is zero, then just solve MIP as opposed to MIQP!
  {
     GRBQuadExpr expr{model->getObjective()};
     for (auto it = Q.begin(); it != Q.end(); ++it)
        expr += 0.5 * (*it) * model->getVarByName("x_" + std::to_string(it.row())) *
                  model->getVarByName("x_" + std::to_string(it.col()));
     model->setObjective(expr, GRB_MINIMIZE);
  }
  model->update();
  if (solve)
     model->optimize();
  return model;
}

void MathOpt::LCP::save(const std::string &filename, bool erase) const {

  Utils::appendSave(std::string("LCP"), filename, erase);
  Utils::appendSave(this->M, filename, std::string("LCP::M"), false);
  Utils::appendSave(this->q, filename, std::string("LCP::q"), false);

  Utils::appendSave(
        static_cast<long int>(this->LeadStart), filename, std::string("LCP::LeadStart"), false);
  Utils::appendSave(
        static_cast<long int>(this->LeadEnd), filename, std::string("LCP::LeadEnd"), false);

  Utils::appendSave(this->A, filename, std::string("LCP::A"), false);
  Utils::appendSave(this->b, filename, std::string("LCP::b"), false);

  arma::sp_mat B(this->nC, 2);
  for (unsigned long int i = 0; i < this->nC; ++i) {
     B.at(i, 0) = this->BoundsX.at(i).first;
     B.at(i, 1) = this->BoundsX.at(i).second;
  }
  Utils::appendSave(B, filename, std::string("LCP::Bounds"), false);

  LOG_S(1) << "Saved LCP to file " << filename;
}



long int MathOpt::LCP::load(const std::string &filename, long int pos) {
  ZEROAssert(this->Env);

  std::string headercheck;
  pos = Utils::appendRead(headercheck, filename, pos);
  if (headercheck != "LCP")
     throw ZEROException(ZEROErrorCode::IOError, "Invalid header");

  arma::sp_mat M_t, A, Bounds;
  arma::vec    q_t, b;
  long int     LeadStart_t, LeadEnd_t;
  pos = Utils::appendRead(M_t, filename, pos, std::string("LCP::M"));
  pos = Utils::appendRead(q_t, filename, pos, std::string("LCP::q"));
  pos = Utils::appendRead(LeadStart_t, filename, pos, std::string("LCP::LeadStart"));
  pos = Utils::appendRead(LeadEnd_t, filename, pos, std::string("LCP::LeadEnd"));
  pos = Utils::appendRead(A, filename, pos, std::string("LCP::A"));
  pos = Utils::appendRead(b, filename, pos, std::string("LCP::b"));
  pos = Utils::appendRead(Bounds, filename, pos, std::string("LCP::Bounds"));

  this->M = M_t;
  this->q = q_t;
  this->A = A;
  this->b = b;

  if (Bounds.n_rows > 0) {
     if (Bounds.n_cols != 2)
        throw ZEROException(ZEROErrorCode::IOError, "Invalid bounds object in loaded file");

     for (unsigned long int i = 0; i < this->M.n_cols; ++i)
        this->BoundsX.push_back(
             {abs(Bounds.at(i, 0)) < 1e20 ? Bounds.at(i, 0)
                                                    : Utils::getSign(Bounds.at(i, 0)) * GRB_INFINITY,


              abs(Bounds.at(i, 1)) < 1e20 ? Bounds.at(i, 1)
                                                    : Utils::getSign(Bounds.at(i, 1)) * GRB_INFINITY});
  }


  this->defConst(Env);
  this->LeadStart = LeadStart_t;
  this->LeadEnd   = LeadEnd_t;

  this->NumberLeader = this->LeadEnd - this->LeadStart + 1;
  this->NumberLeader = this->NumberLeader > 0 ? this->NumberLeader : 0;
  for (unsigned long int i = 0; i < M.n_rows; i++) {
     unsigned long int count = i < LeadStart ? i : i + NumberLeader;
     Compl.push_back({i, count});
  }
  Utils::sortByKey(this->Compl);
  return pos;
}



unsigned long int MathOpt::LCP::convexHull(arma::sp_mat &A, arma::vec &b) {
  const std::vector<arma::sp_mat *> tempAi = [](spmat_Vec &uv) {
     std::vector<arma::sp_mat *> v{};
     for (const auto &x : uv)
        v.push_back(x.get());
     return v;
  }(*this->Ai);
  const auto temp_bi = [](vec_Vec &uv) {
     std::vector<arma::vec *> v{};
     std::for_each(uv.begin(), uv.end(), [&v](const std::unique_ptr<arma::vec> &ptr) {
        v.push_back(ptr.get());
     });
     return v;
  }(*this->bi);
  arma::sp_mat A_common = arma::join_cols(this->A, -this->M);
  arma::vec    bCommon  = arma::join_cols(this->b, this->q);

  if (Ai->size() == 1) {
     A.zeros(Ai->at(0)->n_rows + A_common.n_rows, Ai->at(0)->n_cols + A_common.n_cols);
     b.zeros(bi->at(0)->n_rows + bCommon.n_rows);
     A = arma::join_cols(*Ai->at(0), A_common);
     b = arma::join_cols(*bi->at(0), bCommon);
     return 1;
  } else
     return MathOpt::convexHull(&tempAi, &temp_bi, A, b, A_common, bCommon);
}


void MathOpt::LCP::makeQP(MathOpt::QP_Objective &QP_obj, MathOpt::QP_Param &QP) {
  if (this->Ai->empty())
     return;
  const unsigned long int oldNumVariablesX{static_cast<unsigned long int>(QP_obj.C.n_cols)};

  MathOpt::QP_Constraints QP_cons;
  int                     components = this->convexHull(QP_cons.B, QP_cons.b);
  LOG_S(1) << "LCP::makeQP: No. components: " << components;
  // Updated size after convex hull has been computed.
  const unsigned long int numConstraints{static_cast<unsigned long int>(QP_cons.B.n_rows)};
  const unsigned long int oldNumVariablesY{static_cast<unsigned long int>(QP_cons.B.n_cols)};
  // Resizing entities.
  QP_cons.A.zeros(numConstraints, oldNumVariablesX);
  QP_obj.c = Utils::resizePatch(QP_obj.c, oldNumVariablesY, 1);
  QP_obj.C = Utils::resizePatch(QP_obj.C, oldNumVariablesY, oldNumVariablesX);
  QP_obj.Q = Utils::resizePatch(QP_obj.Q, oldNumVariablesY, oldNumVariablesY);
  // Setting the QP_Param object
  QP.set(QP_obj, QP_cons);

  // Now we have to merge the bounds
  QP.setBounds(Utils::intersectBounds(QP.getBounds(), this->BoundsX));
}


void MathOpt::LCP::addCustomCuts(const arma::sp_mat &A_in, const arma::vec &b_in) {

  ZEROAssert(this->A.n_cols == A_in.n_cols);
  ZEROAssert(b_in.size() == A_in.n_rows);

  this->A = arma::join_cols(this->A, A_in);
  this->b = arma::join_cols(this->b, b_in);
  if (MadeRlxdModel) {
     GRBVar x[nC];
     for (unsigned long int i = 0; i < nC; i++)
        x[i] = this->RelaxedModel.getVarByName("x_" + std::to_string(i));

     std::string basename = "cutConstr" + std::to_string(std::time(nullptr));
     Utils::addSparseConstraints(A_in, b_in, x, basename, &RelaxedModel, GRB_LESS_EQUAL, nullptr);

     LOG_S(1) << "MathOpt::LCP::addCustomCuts: Added cut constraint";
  }
}


bool MathOpt::LCP::containsCut(const arma::vec &A_in, const double b_in, double tol) {
  return Utils::containsConstraint(this->A, this->b, A_in, b_in, tol);
}



ZEROStatus MathOpt::LCP::solvePATH(double timelimit, arma::vec &z, arma::vec &x, bool verbose) {
  auto Solver =
        new Solvers::PATH(this->M, this->q, this->Compl, this->BoundsX, z, x, timelimit, verbose);
  return Solver->getStatus();
}


ZEROStatus MathOpt::LCP::solve(Data::LCP::Algorithms algo,
                                         arma::vec            &xSol,
                                         arma::vec            &zSol,
                                         double                timeLimit,
                                         unsigned long int     threads,
                                         double               &cutOff,
                                         unsigned long int     solLimit) {


  if (algo == Data::LCP::Algorithms::PATH) {
     if (this->A.n_nonzero != 0) {
        this->A.print_dense("A");
        this->b.print("b");
        throw ZEROException(ZEROErrorCode::SolverError,
                                  "PATH does not support non-complementarity constraints!");
     }
     xSol.zeros(this->M.n_cols);
     zSol.zeros(this->M.n_rows);
     switch (this->solvePATH(timeLimit, xSol, zSol, false)) {
     case ZEROStatus::NashEqFound:
        return ZEROStatus::NashEqFound;
     case ZEROStatus::Solved:
        return ZEROStatus::NashEqFound;
     case ZEROStatus::NotSolved:
        return ZEROStatus::NashEqNotFound;
     case ZEROStatus::Numerical:
        return ZEROStatus::Numerical;
     default:
        return ZEROStatus::NashEqNotFound;
     }
  } else {
     if (algo == Data::LCP::Algorithms::MINLP)
        this->PureMIP = false;
     else
        this->PureMIP = true;

     auto Model = this->LCPasMIP(false, timeLimit, threads, solLimit);

     // MIP Warmstart
     try {
        if (!xSol.empty()) {
          for (unsigned long int i = 0; i < xSol.size(); ++i)
             Model->getVarByName("x_" + std::to_string(i)).set(GRB_DoubleAttr_Start, xSol.at(i));
        }
        if (!zSol.empty()) {
          for (unsigned long int i = 0; i < xSol.size(); ++i)
             Model->getVarByName("z_" + std::to_string(i)).set(GRB_DoubleAttr_Start, zSol.at(i));
        }
     } catch (...) {
        LOG_S(WARNING) << "MathOpt::LCP::solve: Cannot complete warmstart. Skipping.";
     }
     if (cutOff != -GRB_INFINITY) {
        // Add a cutoff
        Model->set(GRB_DoubleParam_Cutoff, cutOff);
        // GRBQuadExpr obj{Model->getObjective()};
        // Model->addQConstr(obj, GRB_LESS_EQUAL, cutOff, "cutOff");
     }


     try {

        LCP_PATHStart Callback =
             LCP_PATHStart(this, Model->getVars(), Model->get(GRB_IntAttr_NumVars));
        Model->setCallback(&Callback);
        // Model->set(GRB_IntParam_Presolve, 2);
        // Model->set(GRB_DoubleParam_Heuristics, 0.15);
        // Model->set(GRB_IntParam_Cuts, 3);
        // Model->write("TheLCP.lp");
        Model->optimize();
     } catch (GRBException &e) {
        throw ZEROException(e);
     }

     if (this->extractSols(Model.get(), zSol, xSol, true)) {
        cutOff = Model->getObjective().getValue();
        return ZEROStatus::NashEqFound;
     } else {
        if (Model->get(GRB_IntAttr_Status) == GRB_TIME_LIMIT)
          return ZEROStatus::TimeLimit;
        else
          return ZEROStatus::NashEqNotFound;
     }
  }
}

std::unique_ptr<GRBModel> MathOpt::LCP::getMIP(bool indicators) {
  std::unique_ptr<GRBModel> model{new GRBModel(this->RelaxedModel)};
  // Creating the model
  try {
     GRBVar     x[nC], z[nR], u[this->Compl.size()], l[this->Compl.size()], in[this->Compl.size()];
     GRBLinExpr obj = 0;
     // Get hold of the Variables and Eqn Variables
     for (unsigned long int i = 0; i < nC; i++)
        x[i] = model->getVarByName("x_" + std::to_string(i));

     for (unsigned long int i = 0; i < nR; i++)
        z[i] = model->getVarByName("z_" + std::to_string(i));


     GRBLinExpr        expr    = 0;
     unsigned long int counter = 0;
     for (const auto p : Compl) {


        double LB = this->BoundsX.at(p.second).first;
        double UB = this->BoundsX.at(p.second).second;
        // std::cout << std::to_string(LB) << " - " << std::to_string(UB) << "\n";

        l[counter]  = model->addVar(0, 1, 0, GRB_BINARY, "l_" + std::to_string(p.second));
        in[counter] = model->addVar(0, 1, 0, GRB_BINARY, "in_" + std::to_string(p.second));
        u[counter]  = model->addVar(
          0, UB >= GRB_INFINITY ? 0 : 1, 0, GRB_BINARY, "u_" + std::to_string(p.second));

        model->addGenConstrIndicator(
             in[counter], 1, z[p.first], GRB_EQUAL, 0, "ind_z_" + std::to_string(p.first) + "_zero");

        if (UB < GRB_INFINITY) {
          model->addGenConstrIndicator(u[counter],
                                                 1,
                                                 z[p.first],
                                                 GRB_LESS_EQUAL,
                                                 0,
                                                 "ind_z_" + std::to_string(p.first) + "_negative");

          model->addGenConstrIndicator(
                u[counter], 1, x[p.second], GRB_EQUAL, UB, "ind_x_" + std::to_string(p.first) + "_UB");
        }

        model->addGenConstrIndicator(l[counter],
                                              1,
                                              z[p.first],
                                              GRB_GREATER_EQUAL,
                                              0,
                                              "ind_z_" + std::to_string(p.first) + "_positive");

        model->addGenConstrIndicator(
             l[counter], 1, x[p.second], GRB_EQUAL, LB, "ind_x_" + std::to_string(p.first) + "_LB");

        obj += x[p.second] + l[counter];

        model->addConstr(u[counter] + l[counter] + in[counter] == 1,
                              "MCP_" + std::to_string(p.second));


        counter++;
     }
     //  If any equation or variable is to be fixed to zero, that happens here!
     model->update();
     // Get first Equilibrium
     return model;
  } catch (GRBException &e) {
     throw ZEROException(e);
  } catch (...) {
     throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in  MathOpt::LCP::getMIP");
  }
}

bool MathOpt::LCP::setMIPLinearObjective(const arma::vec &c) {
  ZEROAssert(c.size() <= this->nC);
  this->c_Obj.zeros(this->nC);
  this->c_Obj.subvec(0, c.size() - 1) = c;
  this->ObjType                       = 1;
  LOG_S(2) << "MathOpt::LCP::setMIPLinearObjective: Set LINEAR objective";
  this->MadeObjective = false;
  return true;
}

bool MathOpt::LCP::setMIPQuadraticObjective(const arma::vec &c, const arma::sp_mat &Q) {
  ZEROAssert(c.size() <= this->nC);
  ZEROAssert(c.size() == Q.n_cols);
  ZEROAssert(Q.is_square());

  this->c_Obj.zeros(this->nC);
  this->c_Obj.subvec(0, c.size() - 1) = c;
  this->Q_Obj.zeros(this->nC, this->nC);
  this->Q_Obj.submat(0, 0, c.size() - 1, c.size() - 1) = Q;
  this->ObjType                                        = 2;
  LOG_S(2) << "MathOpt::LCP::setMIPLinearObjective: Set QUADRATIC objective";
  this->MadeObjective = false;
  return true;
}

void MathOpt::LCP::setMIPObjective(GRBModel &MIP) {

  if (this->MadeObjective)
     return;
  if (this->ObjType != 0) {

     // Linear part of the objective
     GRBQuadExpr obj = 0;
     // Get hold of the Variables and Eqn Variables
     for (unsigned long int i = 0; i < this->c_Obj.size(); i++) {
        GRBVar vars[]  = {MIP.getVarByName("x_" + std::to_string(i))};
        double coeff[] = {this->c_Obj.at(i)};
        obj.addTerms(coeff, vars, 1);
     }

     if (this->ObjType == 2) {
        MIP.set(GRB_IntParam_NonConvex, 2);
        // Add a quadratic part
        for (arma::sp_mat::const_iterator it = this->Q_Obj.begin(); it != this->Q_Obj.end(); ++it) {
          obj.addTerm(*it,
                          MIP.getVarByName("x_" + std::to_string(it.col())),
                          MIP.getVarByName("x_" + std::to_string(it.row())));
        }
     }

     MIP.setObjective(obj, GRB_MINIMIZE);
     MIP.set(GRB_IntParam_MIPFocus, 0);

  } else {
     // Feasibility MIP
     GRBLinExpr obj = 0;
     // Get hold of the Variables and Eqn Variables

     for (unsigned long int i = 0; i < nC; i++) {
        GRBVar vars[]  = {MIP.getVarByName("x_" + std::to_string(i))};
        double coeff[] = {1};
        obj.addTerms(coeff, vars, 1);
     }

     for (unsigned long int i = 0; i < nR; i++) {
        GRBVar vars[]  = {MIP.getVarByName("z_" + std::to_string(i))};
        double coeff[] = {1};
        obj.addTerms(coeff, vars, 1);
     }

     MIP.setObjective(obj, GRB_MINIMIZE);
     MIP.set(GRB_IntParam_MIPFocus, 1);
  }
  this->MadeObjective = true;
  MIP.update();
}



std::unique_ptr<GRBModel> MathOpt::LCP::getMINLP() {
  //@todo Not working with lower bounds other than 0
  makeRelaxed();
  LOG_S(0) << "MathOpt::LCP::getMINLP: may not work if UB and LB defined for x.";
  std::unique_ptr<GRBModel> model{new GRBModel(this->RelaxedModel)};
  // Creating the model
  try {
     GRBVar x[nC], z[nR];
     // Get hold of the Variables and Eqn Variables
     for (unsigned long int i = 0; i < nC; i++)
        x[i] = model->getVarByName("x_" + std::to_string(i));

     for (unsigned long int i = 0; i < nR; i++)
        z[i] = model->getVarByName("z_" + std::to_string(i));
     // Define binary variables for BigM

     GRBLinExpr expr = 0;
     for (const auto p : Compl) {

        //  Otherwise, no bounds and we simplify the first expression for LB
        model->addQConstr(x[p.second] * z[p.first],
                                GRB_EQUAL,
                                0,
                                "compl_z_" + std::to_string(p.first) + "_x_" + std::to_string(p.second));
     }

     model->set(GRB_IntParam_NonConvex, 2);
     model->update();
     return model;
  } catch (GRBException &e) {
     throw ZEROException(e);
  } catch (...) {
     throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in  MathOpt::LCP::getMINLP");
  }
}
bool MathOpt::LCP::setMIPFeasibilityObjective() {
  this->ObjType = 0;
  LOG_S(1) << "MathOpt::LCP::setMIPLinearObjective: Set Feasibility objective.";
  return true;
}

std::string std::to_string(Data::LCP::Algorithms al) {
  switch (al) {
  case Data::LCP::Algorithms::MIP:
     return std::string("MIP");
  case Data::LCP::Algorithms::MINLP:
     return std::string("MINLP");
  case Data::LCP::Algorithms::PATH:
     return std::string("PATH");
  }
  return "";
}