Program Listing for File epec_models.cpp

Return to documentation for file (src/interfaces/epec_models.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 "interfaces/epec_models.h"
#include <iomanip>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#include <rapidjson/prettywriter.h>


std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::prn l) {
  switch (l) {
  case Models::EPEC::prn::label:
     ost << std::left << std::setw(50);
     break;
  case Models::EPEC::prn::val:
     ost << std::right << std::setprecision(2) << std::setw(16) << std::fixed;
     break;
  }
  return ost;
}

std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::FollPar &P) {
  ost << "Follower Parameters: " << '\n';
  ost << "********************" << '\n';
  ost << Models::EPEC::prn::label << "Linear Costs"
        << ":\t";
  for (auto a : P.costs_lin)
     ost << Models::EPEC::prn::val << a;
  ost << '\n'
        << Models::EPEC::prn::label << "Quadratic costs"
        << ":\t";
  for (auto a : P.costs_quad)
     ost << Models::EPEC::prn::val << a;
  ost << '\n'
        << Models::EPEC::prn::label << "Production capacities"
        << ":\t";
  for (auto a : P.capacities)
     ost << Models::EPEC::prn::val << (a < 0 ? std::numeric_limits<double>::infinity() : a);
  ost << '\n'
        << Models::EPEC::prn::label << "Tax Caps"
        << ":\t";
  for (auto a : P.tax_caps)
     ost << Models::EPEC::prn::val << (a < 0 ? std::numeric_limits<double>::infinity() : a);
  ost << '\n';
  return ost;
}

std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::DemPar P) {
  ost << "Demand Parameters: " << '\n';
  ost << "******************" << '\n';
  ost << "Price\t\t =\t\t " << P.alpha << "\t-\t" << P.beta << "  x   Quantity" << '\n';
  return ost;
}

std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::LeadPar P) {
  ost << "Leader Parameters: " << '\n';
  ost << "******************" << '\n';
  ost << std::fixed;
  ost << Models::EPEC::prn::label << "Export Limit"
        << ":" << Models::EPEC::prn::val
        << (P.export_limit < 0 ? std::numeric_limits<double>::infinity() : P.export_limit);
  ost << '\n';
  ost << Models::EPEC::prn::label << "Import Limit"
        << ":" << Models::EPEC::prn::val
        << (P.import_limit < 0 ? std::numeric_limits<double>::infinity() : P.import_limit);
  ost << '\n';
  ost << Models::EPEC::prn::label << "Price limit"
        << ":" << Models::EPEC::prn::val
        << (P.price_limit < 0 ? std::numeric_limits<double>::infinity() : P.price_limit);
  ost << '\n';
  return ost;
}

std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::EPECInstance &I) {
  ost << "EPEC Instance: " << '\n';
  ost << "******************" << '\n';
  for (const auto &a : I.Countries)
     ost << a << '\n';
  ost << "Transportation Costs:" << '\n' << I.TransportationCosts << '\n';
  return ost;
}

std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::LeadAllPar &P) {
  ost << "\n\n";
  ost << "***************************"
        << "\n";
  ost << "Leader Complete Description"
        << "\n";
  ost << "***************************"
        << "\n"
        << "\n";
  ost << Models::EPEC::prn::label << "Number of followers"
        << ":" << Models::EPEC::prn::val << P.n_followers << "\n "
        << "\n";
  ost << '\n' << P.LeaderParam << '\n' << P.FollowerParam << '\n' << P.DemandParam << "\n";
  ost << "***************************"
        << "\n"
        << "\n";
  return ost;
}

std::ostream &Models::EPEC::operator<<(std::ostream &ost, const Models::EPEC::LeaderVars l) {
  switch (l) {
  case Models::EPEC::LeaderVars::FollowerStart:
     ost << "Models::EPEC::LeaderVars::FollowerStart";
     break;
  case Models::EPEC::LeaderVars::NetImport:
     ost << "Models::EPEC::LeaderVars::NetImport";
     break;
  case Models::EPEC::LeaderVars::NetExport:
     ost << "Models::EPEC::LeaderVars::NetExport";
     break;
  case Models::EPEC::LeaderVars::CountryImport:
     ost << "Models::EPEC::LeaderVars::CountryImport";
     break;
  case Models::EPEC::LeaderVars::Caps:
     ost << "Models::EPEC::LeaderVars::Caps";
     break;
  case Models::EPEC::LeaderVars::Tax:
     ost << "Models::EPEC::LeaderVars::Tax";
     break;
  case Models::EPEC::LeaderVars::TaxQuad:
     ost << "Models::EPEC::LeaderVars::TaxQuad";
     break;
  case Models::EPEC::LeaderVars::DualVar:
     ost << "Models::EPEC::LeaderVars::DualVar";
     break;
  case Models::EPEC::LeaderVars::ConvHullDummy:
     ost << "Models::EPEC::LeaderVars::ConvHullDummy";
     break;
  case Models::EPEC::LeaderVars::End:
     ost << "Models::EPEC::LeaderVars::End";
     break;
  };
  return ost;
}

bool Models::EPEC::EPEC::ParamValid(
     const LeadAllPar &Params
) const
{
  if (Params.n_followers == 0)
     throw ZEROException(ZEROErrorCode::Assertion, "There are no followers for a player");
  if (Params.FollowerParam.costs_lin.size() != Params.n_followers ||
        Params.FollowerParam.costs_quad.size() != Params.n_followers ||
        Params.FollowerParam.capacities.size() != Params.n_followers ||
        Params.FollowerParam.tax_caps.size() != Params.n_followers ||
        Params.FollowerParam.emission_costs.size() != Params.n_followers)
     throw ZEROException(ZEROErrorCode::InvalidData, "The input data has a size mismatch");
  if (Params.DemandParam.alpha <= 0 || Params.DemandParam.beta <= 0)
     throw ZEROException(ZEROErrorCode::InvalidData, "Demand curve parameters are negative");
  // Country should have a name!
  if (Params.name.empty())
     throw ZEROException(ZEROErrorCode::InvalidData, "The country has no name");
  // Country should have a unique name
  for (const auto &p : this->AllLeadPars)
     if (Params.name == p.name) // i.e., if the strings are same
        throw ZEROException(ZEROErrorCode::InvalidData, "The country has an already existing name");
  return true;
}

void Models::EPEC::EPEC::make_LL_QP(
     const LeadAllPar &            Params,
     const unsigned int            follower,
     MathOpt::QP_Param *           Foll,
     const Models::EPEC::LeadLocs &Loc
     ) noexcept
{
  const unsigned int LeadVars = Loc.at(Models::EPEC::LeaderVars::End) - Params.n_followers;
  arma::sp_mat       Q(1, 1), C(1, LeadVars + Params.n_followers - 1);
  // Two constraints. One saying that you should be less than capacity
  // Another saying that you should be less than leader imposed cap!
  arma::sp_mat A(1, Loc.at(Models::EPEC::LeaderVars::End) - 1), B(1, 1);
  arma::vec    c(1), b(1), d(1);
  c.fill(0);
  b.fill(0);
  d.fill(0);
  A.zeros();
  B.zeros();
  C.zeros();
  b.zeros();
  Q.zeros();
  c.zeros();
  // Objective
  Q(0, 0) = Params.FollowerParam.costs_quad.at(follower) + 2 * Params.DemandParam.beta;
  c(0)    = Params.FollowerParam.costs_lin.at(follower) - Params.DemandParam.alpha;

  arma::mat Ctemp(1, Loc.at(Models::EPEC::LeaderVars::End) - 1, arma::fill::zeros);
  Ctemp.cols(0, Params.n_followers - 1)
        .fill(Params.DemandParam.beta); // First n-1 entries and 1 more entry is Beta
  Ctemp(0, Params.n_followers) = -Params.DemandParam.beta; // For q_exp

  // Scroll in Ctemp basing on the taxation paradigm
  if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::StandardTax)
     Ctemp(0, (Params.n_followers - 1) + 2 + Params.n_followers + follower) =
          1; // q_{-i}, then import, export, then tilde q_i, then i-th tax
  else if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::SingleTax)
     Ctemp(0, (Params.n_followers - 1) + 2 + Params.n_followers + 0) =
          1; // q_{-i}, then import, export, then tilde q_i, then only tax var
  else if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::CarbonTax)
     Ctemp(0, (Params.n_followers - 1) + 2 + Params.n_followers + 0) =
          Params.FollowerParam.emission_costs.at(follower); // q_{-i}, then import, export, then tilde
                                                                             // q_i, then only tax var

  C = Ctemp;
  // A(1, (Params.n_followers - 1) + 2 + follower) = 0;
  // Produce positive (zero) quantities and less than the cap
  B(0, 0) = 1;
  b(0)    = Params.FollowerParam.capacities.at(follower);

  Foll->set(std::move(Q), std::move(C), std::move(A), std::move(B), std::move(c), std::move(b), std::move(d));
}

void Models::EPEC::EPEC::make_LL_LeadCons(
     arma::sp_mat &                LeadCons,
     arma::vec &                   LeadRHS,
     const LeadAllPar &            Params,
     const Models::EPEC::LeadLocs &Loc,
     const unsigned int            import_lim_cons,
     const unsigned int export_lim_cons,
     const unsigned int price_lim_cons,
     const unsigned int activeTaxCaps,
     const unsigned int disableTrade
) const noexcept
{
  if (activeTaxCaps > 0) {
     // Tax Caps are active
     // Different tax caps
     // Note that the loop is performed until this->taxVars is hit
     for (unsigned int follower = 0; follower < this->taxVars; follower++) {
        if (Params.FollowerParam.tax_caps.at(follower) >= 0) {
          // Constraints for Tax limits
          LeadCons(follower, Loc.at(LeaderVars::Tax) + follower) = 1;
          LeadRHS(follower) = Params.FollowerParam.tax_caps.at(follower);
        }
     }
  }
  // Export - import <= Local Production
  // (28b)
  for (unsigned int i = 0; i < Params.n_followers; i++)
     LeadCons.at(activeTaxCaps, i) = -1;
  LeadCons.at(activeTaxCaps, Loc.at(LeaderVars::NetExport)) = 1;
  LeadCons.at(activeTaxCaps, Loc.at(LeaderVars::NetImport)) = -1;


  // Import limit - In more precise terms, everything that comes in minus
  // everything that goes out should satisfy this limit (28c)
  if (import_lim_cons) {
     LeadCons(activeTaxCaps + import_lim_cons, Loc.at(LeaderVars::NetImport)) = 1;
     LeadCons(activeTaxCaps + import_lim_cons, Loc.at(LeaderVars::NetExport)) = -1;
     LeadRHS(activeTaxCaps + import_lim_cons) = Params.LeaderParam.import_limit;
  }
  // Export limit - In more precise terms, everything that goes out minus
  // everything that comes in should satisfy this limit (28d)
  if (export_lim_cons) {
     LeadCons(activeTaxCaps + import_lim_cons + export_lim_cons, Loc.at(LeaderVars::NetExport)) = 1;
     LeadCons(activeTaxCaps + import_lim_cons + export_lim_cons, Loc.at(LeaderVars::NetImport)) = -1;
     LeadRHS(activeTaxCaps + import_lim_cons + export_lim_cons) = Params.LeaderParam.export_limit;
  }
  // (28g)
  if (price_lim_cons) {
     for (unsigned int i = 0; i < Params.n_followers; i++)
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons, i) =
             -Params.DemandParam.beta;
     LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons,
                     Loc.at(LeaderVars::NetImport)) = -Params.DemandParam.beta;
     LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons,
                     Loc.at(LeaderVars::NetExport)) = Params.DemandParam.beta;
     LeadRHS.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons) =
          Params.LeaderParam.price_limit - Params.DemandParam.alpha;
  }

  if (disableTrade > 0) {
     LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons + disableTrade,
                     Loc.at(LeaderVars::NetExport)) = 1;
     LeadRHS.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons + disableTrade) =
          0;
  }
  // revenue tax
  if (Params.LeaderParam.tax_revenue) {

     // If taxation paradigm is not standard (0), then just one tax variable is
     // used.
     unsigned int standardTax = 1;
     unsigned int carbonTax   = 0;
     if (Params.LeaderParam.tax_type != TaxType::StandardTax) {
        standardTax = 0;
        // If carbon tax, we should modify McCornick inequalities
        if (Params.LeaderParam.tax_type == TaxType::CarbonTax)
          carbonTax = 1;
     }

     for (unsigned int i = 0; i < Params.n_followers; i++) {
        double t_cap            = (Params.FollowerParam.tax_caps.at(i * standardTax) >= 0
                                                 ? Params.FollowerParam.tax_caps.at(i * standardTax)
                                                 : 0);
        double carbonCorrection = (carbonTax == 1) ? Params.FollowerParam.emission_costs.at(i) : 1;
        // -u_i + \bar{q}_it_i + \bar{t}_iq_i \le \bar{t}_i \bar{q}_i
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 1,
                        Loc.at(LeaderVars::TaxQuad) + i) = -1;
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 1,
                        Loc.at(LeaderVars::Tax) + i * standardTax) =
             Params.FollowerParam.capacities.at(i) * carbonCorrection;
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 1,
                        Loc.at(LeaderVars::FollowerStart) + i) = t_cap * carbonCorrection;
        LeadRHS.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons + disableTrade +
                      i * 3 + 1) = t_cap * Params.FollowerParam.capacities.at(i) * carbonCorrection;

        // -u_i + \bar{q}_it_i  \le 0
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 2,
                        Loc.at(LeaderVars::TaxQuad) + i) = -1;
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 2,
                        Loc.at(LeaderVars::Tax) + i * standardTax) =
             Params.FollowerParam.capacities.at(i) * carbonCorrection;
        LeadRHS.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons + disableTrade +
                      i * 3 + 2) = 0;

        // -u_i + \bar{t}_iq_i  \le 0
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 3,
                        Loc.at(LeaderVars::TaxQuad) + i)       = -1;
        LeadCons.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons +
                             disableTrade + i * 3 + 3,
                        Loc.at(LeaderVars::FollowerStart) + i) = t_cap * carbonCorrection;
        LeadRHS.at(activeTaxCaps + price_lim_cons + import_lim_cons + export_lim_cons + disableTrade +
                      i * 3 + 3)                              = 0;
     }
  }
  LOG_S(1) << "********** Price Limit constraint: " << price_lim_cons;
  LOG_S(1) << "********** Import Limit constraint: " << import_lim_cons;
  LOG_S(1) << "********** Export Limit constraint: " << export_lim_cons;
  LOG_S(1) << "********** Tax Limit constraints: " << activeTaxCaps << "\n\t";
  LOG_S(1) << "********** Trade disabled: " << ((disableTrade > 0) ? "True" : "False") << "\n\t";
}


Models::EPEC::EPEC &Models::EPEC::EPEC::addCountry(Models::EPEC::LeadAllPar Params,
                                                                    const unsigned int       addnlLeadVars)
{
  if (this->Finalized)
     throw ZEROException(ZEROErrorCode::Assertion,
                                "EPEC object Finalized. Call EPEC::unlock() to unlock "
                                "this object first and then edit");

  bool noError = false;
  try {
     noError = this->ParamValid(Params);
  } catch (const char *e) {
     std::cerr << "Error in Models::EPEC::EPEC::addCountry: " << e << '\n';
  } catch (std::string &e) {
     std::cerr << "String: Error in Models::EPEC::EPEC::addCountry: " << e << '\n';
  } catch (std::exception &e) {
     std::cerr << "Exception: Error in Models::EPEC::EPEC::addCountry: " << e.what() << '\n';
  }
  if (!noError)
     return *this;

  // Basing on the taxation paradigm, allocate the right number of taxVars in
  // the class
  unsigned int activeTaxCaps = 0;
  if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::StandardTax) {
     LOG_S(1) << "Country " << Params.name << " has a standard tax paradigm.";
     this->taxVars = Params.n_followers;
     // Since we have a standard taxation paradigm, we have to consider all
     // different tax caps
     activeTaxCaps = count_if(Params.FollowerParam.tax_caps.begin(),
                                      Params.FollowerParam.tax_caps.end(),
                                      [](double i) { return i >= 0; });
  } else {
     if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::SingleTax) {
        LOG_S(1) << "Country " << Params.name << " has a single tax paradigm.";
     } else if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::CarbonTax) {
        LOG_S(1) << "Country " << Params.name << " has a carbon tax paradigm.";
     }
     this->taxVars = 1;
     // There is no standard taxation paradigm (so we have carbon or single).
     // Hence we want to consider just one caps, arbitrary the first
     activeTaxCaps = count_if(Params.FollowerParam.tax_caps.begin(),
                                      Params.FollowerParam.tax_caps.end(),
                                      [](double i) { return i >= 0; });
     if (activeTaxCaps >= 0) {
        if (!std::equal(Params.FollowerParam.tax_caps.begin() + 1,
                             Params.FollowerParam.tax_caps.end(),
                             Params.FollowerParam.tax_caps.begin())) {
          LOG_S(WARNING) << "Tax caps are not equal within a non-standard tax framework. "
                                  "Using the first value as tax limit.";
        }
        activeTaxCaps = 1;
     }
  }

  const unsigned int LeadVars =
        2 + (1 + Params.LeaderParam.tax_revenue) * Params.n_followers + taxVars + addnlLeadVars;
  // 2 for quantity imported and exported, n for imposed cap, taxVars for taxes
  // and n for bilinear taxes.

  LeadLocs Loc;
  Models::EPEC::init(Loc);

  // Allocate so much space for each of these types of variables
  Models::EPEC::increaseVal(Loc, LeaderVars::FollowerStart, Params.n_followers);
  Models::EPEC::increaseVal(Loc, LeaderVars::NetImport, 1);
  Models::EPEC::increaseVal(Loc, LeaderVars::NetExport, 1);
  Models::EPEC::increaseVal(Loc, LeaderVars::Caps, Params.n_followers);
  Models::EPEC::increaseVal(Loc, LeaderVars::Tax, this->taxVars);
  if (Params.LeaderParam.tax_revenue) {
     LOG_S(INFO) << "Country " << Params.name << " has tax revenue in the objective.";
     Models::EPEC::increaseVal(Loc, LeaderVars::TaxQuad, Params.n_followers);
  }

  // Leader Constraints
  short int import_lim_cons{0}, export_lim_cons{0}, price_lim_cons{0}, trade_allow_cons{0};
  ;
  if (Params.LeaderParam.import_limit >= 0)
     import_lim_cons = 1;
  if (Params.LeaderParam.export_limit >= 0)
     export_lim_cons = 1;
  if (Params.LeaderParam.price_limit >= 0)
     price_lim_cons = 1;
  if (Params.LeaderParam.tradeAllowed == false)
     trade_allow_cons = 1;

  arma::sp_mat LeadCons(activeTaxCaps +        // Tax limit constraints
                                     1 +                // Export - import <= Domestic production
                                     import_lim_cons +  // Import limit constraint
                                     export_lim_cons +  // Export limit constraint
                                     price_lim_cons +   // Price limit constraint
                                     trade_allow_cons + // Trade Switch constraint
                                     Params.n_followers * 3 * Params.LeaderParam.tax_revenue, // revenue ta
                                Loc[Models::EPEC::LeaderVars::End]);
  arma::vec    LeadRHS(import_lim_cons + export_lim_cons + price_lim_cons + activeTaxCaps +
                        trade_allow_cons + Params.n_followers * 3 * Params.LeaderParam.tax_revenue +
                        1,
                    arma::fill::zeros);

  std::vector<std::shared_ptr<MathOpt::QP_Param>> Players{};
  // Create the QP_Param* for each follower
  try {
     for (unsigned int follower = 0; follower < Params.n_followers; follower++) {
        auto Foll = std::make_shared<MathOpt::QP_Param>(this->Env);
        this->make_LL_QP(Params, follower, Foll.get(), Loc);
        Players.push_back(Foll);
     }
     // Make Leader Constraints
     this->make_LL_LeadCons(LeadCons,
                                    LeadRHS,
                                    Params,
                                    Loc,
                                    import_lim_cons,
                                    export_lim_cons,
                                    price_lim_cons,
                                    activeTaxCaps,
                                    trade_allow_cons);
  } catch (GRBException &e) {
     throw ZEROException(e);
  }

  // Lower level Market clearing constraints - empty
  arma::sp_mat MC(0, LeadVars + Params.n_followers);
  arma::vec    MCRHS(0, arma::fill::zeros);

  std::vector<std::shared_ptr<MathOpt::MP_Param>> MPCasted;
  for (auto &item : Players) {
     auto m = std::dynamic_pointer_cast<MathOpt::MP_Param>(item);
     MPCasted.push_back(m);
  }
  // Convert the country QP to a NashGame
  auto N =
        std::make_shared<Game::NashGame>(this->Env, MPCasted, MC, MCRHS, LeadVars, LeadCons, LeadRHS);
  this->name2nos[Params.name] = this->PlayersLowerLevels.size();
  this->PlayersLowerLevels.push_back(N);
  Models::EPEC::increaseVal(Loc,
                                     Models::EPEC::LeaderVars::DualVar,
                                     N->getNumDualVars()); // N->getNumDualVars() will sum the number of
                                                                  // constraints in each lower level QP and provide
                                                                  // the sum. Indeed, this is the number of dual
                                                                  // variables for the lower level.
  this->Locations.push_back(Loc);

  this->EPEC::LocEnds.push_back(&this->Locations.back().at(LeaderVars::End));
  this->EPEC::ConvexHullVariables.push_back(0);

  this->LeadConses.push_back(N->rewriteLeadCons()); // Not mandatory!
  this->AllLeadPars.push_back(Params);
  this->Game::EPEC::numMCVariables++;
  this->NumPlayers++;
  return *this;
}

Models::EPEC::EPEC &
Models::EPEC::EPEC::addTranspCosts(const arma::sp_mat &costs
                                              )
{
  if (this->Finalized)
     throw ZEROException(ZEROErrorCode::Assertion,
                                "EPEC object Finalized. Call "
                                "EPEC::unlock() to unlock this object first and then edit.");
  try {
     if (this->getNumPlayers() != costs.n_rows || this->getNumPlayers() != costs.n_cols)
        throw ZEROException(ZEROErrorCode::Assertion, "Mismatch of size in Q");
     else
        this->TranspCosts = arma::sp_mat(costs);
     this->TranspCosts.diag().zeros(); // Doesn't make sense for it to have a nonzero diagonal!

  } catch (GRBException &e) {
     throw ZEROException(e);
  }

  return *this;
}

void Models::EPEC::EPEC::preFinalize() {
  /*
    * Below for loop adds space for each country's quantity imported from
    * variable
    */
  try {
     this->nImportMarkets = std::vector<unsigned int>(this->getNumPlayers());
     for (unsigned int i = 0; i < this->getNumPlayers(); i++)
        this->add_Leaders_tradebalance_constraints(i);
  } catch (GRBException &e) {
     throw ZEROException(e);
  } catch (...) {
     throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in preFinalize()");
  }
}

void Models::EPEC::EPEC::add_Leaders_tradebalance_constraints(const unsigned int i)
{
  if (i >= this->PlayersLowerLevels.size())
     throw ZEROException(ZEROErrorCode::OutOfRange, "Player does not exist");
  int       nImp = 0;
  LeadLocs &Loc  = this->Locations.at(i);
  // Counts the number of countries from which the current country imports
  for (auto val = TranspCosts.begin_col(i); val != TranspCosts.end_col(i); ++val)
     nImp++;
  // substitutes that answer to nImportMarkets at the current position
  this->nImportMarkets.at(i) = (nImp);
  if (nImp > 0) {
     Models::EPEC::increaseVal(Loc, LeaderVars::CountryImport, nImp);

     Game::NashGame &LL_Nash = *this->PlayersLowerLevels.at(i).get();

     // Adding the constraint that the sum of imports from all countries equals
     // total imports
     arma::vec a(Loc.at(Models::EPEC::LeaderVars::End) - LL_Nash.getNumDualVars(),
                     arma::fill::zeros);
     a.at(Loc.at(Models::EPEC::LeaderVars::NetImport)) = -1;
     a.subvec(Loc.at(LeaderVars::CountryImport), Loc.at(LeaderVars::CountryImport + 1) - 1).ones();

     LL_Nash.addDummy(nImp, Loc.at(Models::EPEC::LeaderVars::CountryImport));
     LL_Nash.addLeadCons(a, 0).addLeadCons(-a, 0);
  } else {
     Game::NashGame &LL_Nash = *this->PlayersLowerLevels.at(i).get();

     // Set imports and exports to zero
     arma::vec a(Loc.at(Models::EPEC::LeaderVars::End) - LL_Nash.getNumDualVars(),
                     arma::fill::zeros);
     a.at(Loc.at(Models::EPEC::LeaderVars::NetImport)) = 1;
     LL_Nash.addLeadCons(a, 0); // Export <= 0
     a.at(Loc.at(Models::EPEC::LeaderVars::NetImport)) = 0;
     a.at(Loc.at(Models::EPEC::LeaderVars::NetExport)) = 1;
     LL_Nash.addLeadCons(a, 0); // Import <= 0
  }
}

void Models::EPEC::EPEC::makeMCConstraints(arma::sp_mat &MCLHS, arma::vec &MCRHS) const
{
  if (!this->Finalized)
     throw ZEROException(ZEROErrorCode::Assertion,
                                "makeMCConstraints can be called after finalize()");
  // Transportation matrix
  const arma::sp_mat &TrCo = this->TranspCosts;
  // Output matrices
  MCRHS.zeros(this->getNumPlayers());
  MCLHS.zeros(this->getNumPlayers(), this->getNumVar());
  // The MC constraint for each leader country
  if (this->getNumPlayers() > 1) {
     for (unsigned int i = 0; i < this->getNumPlayers(); ++i) {
        MCLHS(i, this->getPosition(i, LeaderVars::NetExport)) = 1;
        for (auto val = TrCo.begin_row(i); val != TrCo.end_row(i); ++val) {
          const unsigned int j = val.col(); // This is the country which is importing from "i"
          unsigned int       count{0};

          for (auto val2 = TrCo.begin_col(j); val2 != TrCo.end_col(j); ++val2)
          // What position in the list of j's importing from countries  does i
          // fall in?
          {
             if (val2.row() == i)
                break;
             else
                count++;
          }
          MCLHS(i, this->getPosition(j, Models::EPEC::LeaderVars::CountryImport) + count) = -1;
        }
     }
  }
}

void Models::EPEC::EPEC::make_MC_leader(const unsigned int i)
{
  if (i >= this->getNumPlayers())
     throw ZEROException(ZEROErrorCode::OutOfRange, "Player does not exist");
  try {
     const arma::sp_mat &TrCo        = this->TranspCosts;
     const unsigned int  nEPECvars   = this->getNumVar();
     const unsigned int  nThisMCvars = 1;
     arma::sp_mat        C(nThisMCvars, nEPECvars - nThisMCvars);

     C.at(0, this->getPosition(i, Models::EPEC::LeaderVars::NetExport)) = 1;

     for (auto val = TrCo.begin_row(i); val != TrCo.end_row(i); ++val) {
        const unsigned int j = val.col(); // This is the country which the
                                                     // country "i" is importing from
        unsigned int count{0};

        for (auto val2 = TrCo.begin_col(j); val2 != TrCo.end_col(j); ++val2)
        // What position in the list of j's impoting from countries  does i fall
        // in?
        {
          if (val2.row() == i)
             break;
          else
             count++;
        }

        C.at(0,
              this->getPosition(j, Models::EPEC::LeaderVars::CountryImport) + count -
                    (j >= i ? nThisMCvars : 0)) = 1;
     }

     this->MC_QP.at(i) = std::make_shared<MathOpt::QP_Param>(this->Env);
     // Note Q = {{0}}, c={0}, the MC problem has no constraints. So A=B={{}},
     // b={}.
     this->MC_QP.at(i).get()->set(arma::sp_mat{1, 1},                       // Q
                                            std::move(C),                             // C
                                            arma::sp_mat{0, nEPECvars - nThisMCvars}, // A
                                            arma::sp_mat{0, nThisMCvars},             // B
                                            arma::vec{0},                             // c
                                            arma::vec{},                               // b
                                  arma::vec{}
     );
  } catch (GRBException &e) {
     throw ZEROException(e);
  } catch (...) {
     throw ZEROException(ZEROErrorCode::Unknown, "Unknown exception in make_MC_leader()");
  }
}

bool Models::EPEC::EPEC::dataCheck(
     const bool chkAllLeadPars,
     const bool chkcountries_LL,
     const bool chkMC_QP,
     const bool chkLeadConses,
     const bool chkLeadRHSes,
     const bool chknImportMarkets,
     const bool chkLocations,
     const bool chkLeaderLocations,
     const bool chkLeadObjec
) const
{
  if (!chkAllLeadPars && AllLeadPars.size() != this->getNumPlayers())
     return false;
  if (!chkcountries_LL && PlayersLowerLevels.size() != this->getNumPlayers())
     return false;
  if (!chkMC_QP && MC_QP.size() != this->getNumPlayers())
     return false;
  if (!chkLeadConses && LeadConses.size() != this->getNumPlayers())
     return false;
  if (!chkLeadRHSes && LeadRHSes.size() != this->getNumPlayers())
     return false;
  if (!chknImportMarkets && nImportMarkets.size() != this->getNumPlayers())
     return false;
  if (!chkLocations && Locations.size() != this->getNumPlayers())
     return false;
  if (!chkLeaderLocations && LeaderLocations.size() != this->getNumPlayers())
     return false;
  if (!chkLeaderLocations && this->getNumVar() == 0)
     return false;
  if (!chkLeadObjec && LeaderObjective.size() != this->getNumPlayers())
     return false;
  return true;
}

unsigned int Models::EPEC::EPEC::getPosition(const unsigned int             countryCount,
                                                            const Models::EPEC::LeaderVars var) const
{
  if (countryCount >= this->getNumPlayers())
     throw ZEROException(ZEROErrorCode::OutOfRange, "Player object is out of range");
  return this->LeaderLocations.at(countryCount) + this->Locations.at(countryCount).at(var);
}

unsigned int Models::EPEC::EPEC::getPosition(const std::string &            countryName,
                                                            const Models::EPEC::LeaderVars var) const
{
  return this->getPosition(name2nos.at(countryName), var);
}

Game::NashGame *Models::EPEC::EPEC::get_LowerLevelNash(const unsigned int i) const
{
  return this->PlayersLowerLevels.at(i).get();
}

Models::EPEC::EPEC &Models::EPEC::EPEC::unlock()
{
  this->Finalized = false;
  return *this;
}

void Models::EPEC::EPEC::makeObjectivePlayer(
     const unsigned int     i,
     MathOpt::QP_Objective &QP_obj
     )
{
  const unsigned int  nEPECvars        = this->getNumVar();
  const unsigned int  nThisCountryvars = this->Locations.at(i).at(Models::EPEC::LeaderVars::End);
  const LeadAllPar &  Params           = this->AllLeadPars.at(i);
  const arma::sp_mat &TrCo             = this->TranspCosts;
  const LeadLocs &    Loc              = this->Locations.at(i);

  QP_obj.Q.zeros(nThisCountryvars, nThisCountryvars);
  QP_obj.c.zeros(nThisCountryvars);
  QP_obj.C.zeros(nThisCountryvars, nEPECvars - nThisCountryvars);
  // emission term
  for (unsigned int j = Loc.at(Models::EPEC::LeaderVars::FollowerStart), count = 0;
         count < Params.n_followers;
         j++, count++)
     QP_obj.c.at(j) = Params.FollowerParam.emission_costs.at(count);

  // revenue tax
  if (Params.LeaderParam.tax_revenue) {
     for (unsigned int j = Loc.at(Models::EPEC::LeaderVars::TaxQuad), count = 0;
            count < this->taxVars;
            j++, count++)
        QP_obj.c.at(j) = 1;
  }

  if (this->getNumPlayers() > 1) {
     // export revenue term

     QP_obj.C(Loc.at(Models::EPEC::LeaderVars::NetExport),
                 // this->getPosition(i, Models::EPEC::LeaderVars::End) -
                 // nThisCountryvars) = -1;
                 this->getPosition(this->getNumPlayers() - 1, Models::EPEC::LeaderVars::End) -
                      nThisCountryvars + i) = -1;

     // Import cost term.
     unsigned int count{0};
     for (auto val = TrCo.begin_col(i); val != TrCo.end_col(i); ++val, ++count) {
        // C^{tr}_{IA}*q^{I\to A}_{imp} term
        QP_obj.c.at(Loc.at(Models::EPEC::LeaderVars::CountryImport) + count) = (*val);
        // \pi^I*q^{I\to A}_{imp} term
        QP_obj.C.at(Loc.at(Models::EPEC::LeaderVars::CountryImport) + count,
                        this->getPosition(this->getNumPlayers() - 1, Models::EPEC::LeaderVars::End) -
                             nThisCountryvars + val.row()) = 1;
        // this->Locations.at(val.row()).at(Models::EPEC::LeaderVars::End)) = 1;
        // this->getPosition(val.row(), Models::EPEC::LeaderVars::End)) = 1;
     }
  }
}

std::unique_ptr<GRBModel> Models::EPEC::EPEC::Respond(const std::string &name,
                                                                        const arma::vec &  x) const {
  return this->Game::EPEC::bestResponseProgram(this->name2nos.at(name), x, nullptr);
}

void Models::EPEC::EPEC::updateLocations()
{
  for (unsigned int i = 0; i < this->getNumPlayers(); ++i) {
     LeadLocs &Loc = this->Locations.at(i);
     Models::EPEC::decreaseVal(Loc,
                                        Models::EPEC::LeaderVars::ConvHullDummy,
                                        Loc[Models::EPEC::LeaderVars::ConvHullDummy + 1] -
                                             Loc[Models::EPEC::LeaderVars::ConvHullDummy]);
     Models::EPEC::increaseVal(
          Loc, Models::EPEC::LeaderVars::ConvHullDummy, this->ConvexHullVariables.at(i));
  }
}

void Models::EPEC::increaseVal(LeadLocs &         L,
                                         const LeaderVars   start,
                                         const unsigned int val,
                                         const bool         startnext)

{
  auto start_rl = (LeaderVars)(startnext ? start + 1 : start);
  for (LeaderVars l = start_rl; l != Models::EPEC::LeaderVars::End; l = l + 1)
     L[l] += val;
  L[Models::EPEC::LeaderVars::End] += val;
  // LOG_S(ERROR)<<"End location changed to:
  // "<<L[Models::EPEC::LeaderVars::End];
}

void Models::EPEC::decreaseVal(LeadLocs &         L,
                                         const LeaderVars   start,
                                         const unsigned int val,
                                         const bool         startnext) {
  auto start_rl = (LeaderVars)(startnext ? start + 1 : start);
  for (LeaderVars l = start_rl; l != Models::EPEC::LeaderVars::End; l = l + 1)
     L[l] -= val;
  L[Models::EPEC::LeaderVars::End] -= val;
  // LOG_S(ERROR)<<"End location changed to:
  // "<<L[Models::EPEC::LeaderVars::End];
}

void Models::EPEC::init(LeadLocs &L) {
  for (LeaderVars l = Models::EPEC::LeaderVars::FollowerStart; l != Models::EPEC::LeaderVars::End;
         l            = l + 1)
    L[l] = 0;
  L[Models::EPEC::LeaderVars::End] = 0;
}

Models::EPEC::FollPar operator+(const Models::EPEC::FollPar &F1, const Models::EPEC::FollPar &F2) {
  std::vector<double>      cq, cl, cap, ec, tc;
  std::vector<std::string> nm;

  cq.insert(cq.end(), F1.costs_quad.begin(), F1.costs_quad.end());
  cq.insert(cq.end(), F2.costs_quad.begin(), F2.costs_quad.end());

  cl.insert(cl.end(), F1.costs_lin.begin(), F1.costs_lin.end());
  cl.insert(cl.end(), F2.costs_lin.begin(), F2.costs_lin.end());

  cap.insert(cap.end(), F1.capacities.begin(), F1.capacities.end());
  cap.insert(cap.end(), F2.capacities.begin(), F2.capacities.end());

  ec.insert(ec.end(), F1.emission_costs.begin(), F1.emission_costs.end());
  ec.insert(ec.end(), F2.emission_costs.begin(), F2.emission_costs.end());

  tc.insert(tc.end(), F1.tax_caps.begin(), F1.tax_caps.end());
  tc.insert(tc.end(), F2.tax_caps.begin(), F2.tax_caps.end());

  nm.insert(nm.end(), F1.names.begin(), F1.names.end());
  nm.insert(nm.end(), F2.names.begin(), F2.names.end());

  return Models::EPEC::FollPar(cq, cl, cap, ec, tc, nm);
}
Models::EPEC::LeaderVars Models::EPEC::operator+(Models::EPEC::LeaderVars a, int b) {
  return static_cast<LeaderVars>(static_cast<int>(a) + b);
}

std::string to_string(const GRBConstr &cons, const GRBModel &model) {
  const GRBVar *     vars  = model.getVars();
  const int          nVars = model.get(GRB_IntAttr_NumVars);
  std::ostringstream oss;
  oss << cons.get(GRB_StringAttr_ConstrName) << ":\t\t";
  constexpr double eps = 1e-5;
  // LHS
  for (int i = 0; i < nVars; ++i) {
     double coeff = model.getCoeff(cons, vars[i]);
     if (std::abs(coeff) > eps) {
        char sign = (coeff > eps) ? '+' : ' ';
        oss << sign << coeff << to_string(vars[i]) << "\t";
     }
  }
  // Inequality/Equality and RHS
  oss << cons.get(GRB_CharAttr_Sense) << "\t" << cons.get(GRB_DoubleAttr_RHS);
  return oss.str();
}

std::string to_string(const GRBVar &var) {
  std::string name = var.get(GRB_StringAttr_VarName);
  return name.empty() ? "unNamedvar" : name;
}

void Models::EPEC::EPEC::writeLeadParams(const std::string &filename,
                                                      const unsigned int i,
                                                      bool               append) const {
  std::ofstream file;
  file.open(filename, append ? std::ios::app : std::ios::out);
  const LeadAllPar &Params = this->AllLeadPars.at(i);
  file << "**************************************************\n";
  file << "COUNTRY: " << Params.name << '\n';
  file << "- - - - - - - - - - - - - - - - - - - - - - - - - \n";
  file << Params;
  file << "**************************************************\n\n\n\n\n";
  file.close();
}

void Models::EPEC::EPEC::writeSolutionJSON(const std::string &filename,
                                                         const arma::vec &  x,
                                                         const arma::vec &  z) const {

  rapidjson::StringBuffer                          s;
  rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(s);
  writer.StartObject();
  writer.Key("Meta");
  writer.StartObject();
  writer.Key("isPureEquilibrium");
  writer.Bool(this->isPureStrategy());
  writer.Key("nCountries");
  writer.Uint(this->getNumPlayers());
  writer.Key("nFollowers");
  writer.StartArray();
  for (unsigned i = 0; i < this->getNumPlayers(); i++)
     writer.Uint(this->AllLeadPars.at(i).n_followers);
  writer.EndArray();
  writer.Key("Countries");
  writer.StartArray();
  for (unsigned i = 0; i < this->getNumPlayers(); i++) {
     writer.StartObject();
     writer.Key("FollowerStart");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::FollowerStart));
     writer.Key("NetImport");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::NetImport));
     writer.Key("NetExport");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::NetExport));
     writer.Key("CountryImport");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::CountryImport));
     writer.Key("Caps");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::Caps));
     writer.Key("Tax");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::Tax));
     if (this->AllLeadPars.at(i).LeaderParam.tax_revenue) {
        writer.Key("QuadraticTax");
        writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::TaxQuad));
     }
     writer.Key("DualVar");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::DualVar));
     writer.Key("ConvHullDummy");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::ConvHullDummy));
     writer.Key("End");
     writer.Uint(this->getPosition(i, Models::EPEC::LeaderVars::End));
     writer.Key("ShadowPrice");
     writer.Uint(this->getPosition(this->getNumPlayers() - 1, Models::EPEC::LeaderVars::End) + i);
     writer.EndObject();
  }
  writer.EndArray();
  writer.EndObject();
  writer.Key("Solution");
  writer.StartObject();
  writer.Key("x");
  writer.StartArray();
  for (unsigned i = 0; i < x.size(); i++)
     writer.Double(x.at(i));
  writer.EndArray();
  writer.Key("z");
  writer.StartArray();
  for (unsigned i = 0; i < z.size(); i++)
     writer.Double(z.at(i));
  writer.EndArray();
  writer.EndObject();
  writer.EndObject();
  std::ofstream file(filename + ".json");
  file << s.GetString();
}

void Models::EPEC::EPEC::readSolutionJSON(const std::string &filename) {
  std::ifstream ifs(filename + ".json");
  if (ifs.good()) {
     rapidjson::IStreamWrapper isw(ifs);
     rapidjson::Document       d;
     try {
        d.ParseStream(isw);
        const rapidjson::Value &x = d["Solution"].GetObject()["x"];
        // const Value &z = d["Solution"].GetObject()["z"];
        arma::vec new_x;
        // arma::vec new_z;
        new_x.zeros(x.GetArray().Size());
        // new_z.zeros(z.GetArray().Size());

        for (rapidjson::SizeType i = 0; i < this->getNumVar(); i++)
          new_x.at(i) = x[i].GetDouble();

        // for (SizeType i = 0; i < this->getNumVar(); i++)
        // new_z.at(i) = z[i].GetDouble();
        ifs.close();
        this->warmstart(new_x);
     } catch (std::exception &e) {
        throw ZEROException(ZEROErrorCode::IOError, e.what());
     } catch (...) {
        throw ZEROException(ZEROErrorCode::Unknown, "Unknown error in readSolutionJSON()");
     }
  } else {
     throw ZEROException(ZEROErrorCode::IOError, "File not found");
  }
}

void Models::EPEC::EPEC::writeSolution(const int writeLevel, const std::string &filename) const {
  if (this->Stats.Status.get() == ZEROStatus::NashEqFound) {
     if (writeLevel == 1 || writeLevel == 2) {
        this->WriteCountrySolution(0, filename + ".txt", this->SolutionX, false);
        for (unsigned int ell = 1; ell < this->getNumPlayers(); ++ell)
          this->WriteCountrySolution(ell, filename + ".txt", this->SolutionX, true);
     }
     if (writeLevel == 2 || writeLevel == 0)
        this->writeSolutionJSON(filename, this->SolutionX, this->SolutionZ);
  } else {
     std::cerr << "Error in Models::EPEC::EPEC::writeSolution: no solution to write." << '\n';
  }
}
void Models::EPEC::EPECInstance::save(const std::string &filename) {

  rapidjson::StringBuffer                          s;
  rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(s);
  writer.StartObject();
  writer.Key("nCountries");
  writer.Uint(this->Countries.size());
  writer.Key("Countries");
  writer.StartArray();
  for (unsigned i = 0; i < this->Countries.size(); i++) {
     writer.StartObject();

     writer.Key("nFollowers");
     writer.Uint(this->Countries.at(i).n_followers);

     writer.Key("Name");
     std::string currName = this->Countries.at(i).name;
     char        nameArray[currName.length() + 1];
     strcpy(nameArray, currName.c_str());
     writer.String(nameArray);

     writer.Key("DemandParam");
     writer.StartObject();
     writer.Key("Alpha");
     writer.Double(this->Countries.at(i).DemandParam.alpha);
     writer.Key("Beta");
     writer.Double(this->Countries.at(i).DemandParam.beta);
     writer.EndObject();

     writer.Key("TransportationCosts");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.size(); j++)
        writer.Double(this->TransportationCosts(i, j));
     writer.EndArray();

     writer.Key("LeaderParam");
     writer.StartObject();
     writer.Key("ImportLimit");
     writer.Double(this->Countries.at(i).LeaderParam.import_limit);
     writer.Key("ExportLimit");
     writer.Double(this->Countries.at(i).LeaderParam.export_limit);
     writer.Key("PriceLimit");
     writer.Double(this->Countries.at(i).LeaderParam.price_limit);
     writer.Key("TaxRevenue");
     writer.Bool(this->Countries.at(i).LeaderParam.tax_revenue);
     writer.Key("TaxationType");
     switch (this->Countries.at(i).LeaderParam.tax_type) {
     case Models::EPEC::TaxType::StandardTax:
        writer.Int(0);
        break;
     case Models::EPEC::TaxType::SingleTax:
        writer.Int(1);
        break;
     default:
        writer.Int(2);
     }
     writer.EndObject();

     writer.Key("Followers");
     writer.StartObject();

     writer.Key("Names");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.at(i).n_followers; j++) {
        currName = this->Countries.at(i).FollowerParam.names.at(j);
        char nameArrayCurrent[currName.length() + 1];
        strcpy(nameArrayCurrent, currName.c_str());
        writer.String(nameArrayCurrent);
     }
     writer.EndArray();

     writer.Key("Capacities");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.at(i).n_followers; j++)
        writer.Double(this->Countries.at(i).FollowerParam.capacities.at(j));
     writer.EndArray();

     writer.Key("LinearCosts");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.at(i).n_followers; j++)
        writer.Double(this->Countries.at(i).FollowerParam.costs_lin.at(j));
     writer.EndArray();

     writer.Key("QuadraticCosts");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.at(i).n_followers; j++)
        writer.Double(this->Countries.at(i).FollowerParam.costs_quad.at(j));
     writer.EndArray();

     writer.Key("EmissionCosts");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.at(i).n_followers; j++)
        writer.Double(this->Countries.at(i).FollowerParam.emission_costs.at(j));
     writer.EndArray();

     writer.Key("TaxCaps");
     writer.StartArray();
     for (unsigned j = 0; j < this->Countries.at(i).n_followers; j++)
        writer.Double(this->Countries.at(i).FollowerParam.tax_caps.at(j));
     writer.EndArray();

     writer.EndObject();

     writer.EndObject();
  }
  writer.EndArray();
  writer.EndObject();
  remove(filename.c_str());
  std::ofstream file(filename + ".json");
  file << s.GetString();
  file.close();
}

void Models::EPEC::EPECInstance::load(const std::string &filename) {
  std::ifstream ifs(filename + ".json");
  if (ifs.good()) {
     rapidjson::IStreamWrapper isw(ifs);
     rapidjson::Document       d;
     try {
        d.ParseStream(isw);
        std::vector<Models::EPEC::LeadAllPar> LAP        = {};
        int                                   nCountries = d["nCountries"].GetInt();
        arma::sp_mat                          TrCo;
        TrCo.zeros(nCountries, nCountries);
        for (int j = 0; j < nCountries; ++j) {
          const rapidjson::Value &c = d["Countries"].GetArray()[j].GetObject();

          Models::EPEC::FollPar   FP;
          const rapidjson::Value &cap = c["Followers"]["Capacities"];
          for (rapidjson::SizeType i = 0; i < cap.GetArray().Size(); i++) {
             FP.capacities.push_back(Utils::round_nplaces(cap[i].GetDouble(), 5));
          }
          const rapidjson::Value &lc = c["Followers"]["LinearCosts"];
          for (rapidjson::SizeType i = 0; i < lc.GetArray().Size(); i++) {
             FP.costs_lin.push_back(Utils::round_nplaces(lc[i].GetDouble(), 5));
          }
          const rapidjson::Value &qc = c["Followers"]["QuadraticCosts"];
          for (rapidjson::SizeType i = 0; i < qc.GetArray().Size(); i++) {
             FP.costs_quad.push_back(Utils::round_nplaces(qc[i].GetDouble(), 5));
          }
          const rapidjson::Value &ec = c["Followers"]["EmissionCosts"];
          for (rapidjson::SizeType i = 0; i < ec.GetArray().Size(); i++) {
             FP.emission_costs.push_back(Utils::round_nplaces(ec[i].GetDouble(), 5));
          }
          const rapidjson::Value &tc = c["Followers"]["TaxCaps"];
          for (rapidjson::SizeType i = 0; i < tc.GetArray().Size(); i++) {
             FP.tax_caps.push_back(Utils::round_nplaces(tc[i].GetDouble(), 5));
          }
          const rapidjson::Value &nm = c["Followers"]["Names"];
          for (rapidjson::SizeType i = 0; i < nm.GetArray().Size(); i++) {
             FP.names.push_back(nm[i].GetString());
          }
          for (rapidjson::SizeType i = 0; i < c["TransportationCosts"].GetArray().Size(); i++) {
             TrCo.at(j, i) =
                  Utils::round_nplaces(c["TransportationCosts"].GetArray()[i].GetDouble(), 5);
          }
          bool tax_revenue = false;
          if (c["LeaderParam"].HasMember("TaxRevenue")) {
             tax_revenue = c["LeaderParam"].GetObject()["TaxRevenue"].GetBool();
          }
          unsigned int tax_type = 0;
          if (c["LeaderParam"].HasMember("TaxationType")) {
             tax_type = c["LeaderParam"].GetObject()["TaxationType"].GetInt();
          }
          LAP.push_back(Models::EPEC::LeadAllPar(
                FP.capacities.size(),
                c["Name"].GetString(),
                FP,
                {Utils::round_nplaces(c["DemandParam"].GetObject()["Alpha"].GetDouble(), 5),
                 Utils::round_nplaces(c["DemandParam"].GetObject()["Beta"].GetDouble(), 5)},
                {Utils::round_nplaces(c["LeaderParam"].GetObject()["ImportLimit"].GetDouble(), 5),
                 Utils::round_nplaces(c["LeaderParam"].GetObject()["ExportLimit"].GetDouble(), 5),
                 Utils::round_nplaces(c["LeaderParam"].GetObject()["PriceLimit"].GetDouble(), 5),
                 tax_revenue,
                 tax_type}));
        }
        ifs.close();
        this->Countries           = LAP;
        this->TransportationCosts = TrCo;
     } catch (std::exception &e) {
        throw ZEROException(ZEROErrorCode::IOError, e.what());
     } catch (...) {
        throw ZEROException(ZEROErrorCode::IOError, "Unknown error in load()");
     }
  } else {
     throw ZEROException(ZEROErrorCode::IOError, "File not found");
  }
}

void Models::EPEC::EPEC::WriteCountrySolution(const unsigned int i,
                                                             const std::string &filename,
                                                             const arma::vec &  x,
                                                             const bool         append) const {
  // if (!TheLCP) return;
  // const LeadLocs& Loc = this->Locations.at(i);

  std::ofstream file;
  file.open(filename, append ? std::ios::app : std::ios::out);
  // FILE OPERATIONS START
  const LeadAllPar &Params = this->AllLeadPars.at(i);
  file << "**************************************************\n";
  file << "COUNTRY: " << Params.name << '\n';
  file << "**************************************************\n\n";
  // Country Variables
  unsigned int foll_prod;
  foll_prod = this->getPosition(i, Models::EPEC::LeaderVars::FollowerStart);
  // Domestic production
  double prod{0};
  for (unsigned int j = 0; j < Params.n_followers; ++j)
     prod += x.at(foll_prod + j);
  // Trade
  double Export{x.at(this->getPosition(i, Models::EPEC::LeaderVars::NetExport))};
  double exportPrice{
        x.at(this->getPosition(this->getNumPlayers() - 1, Models::EPEC::LeaderVars::End) + i)};
  double import{0};
  for (unsigned int j = this->getPosition(i, Models::EPEC::LeaderVars::CountryImport);
         j < this->getPosition(i, Models::EPEC::LeaderVars::CountryImport + 1);
         ++j)
     import += x.at(j);
  // Writing national level details
  file << "PureStrategy:" << this->isPureStrategy(i) << "\n";
  file << Models::EPEC::prn::label << "Domestic production"
         << ":" << Models::EPEC::prn::val << prod << "\n";
  if (Export >= import)
     file << Models::EPEC::prn::label << "Net exports"
            << ":" << Models::EPEC::prn::val << Export - import << "\n";
  else
     file << Models::EPEC::prn::label << "Net imports"
            << ":" << Models::EPEC::prn::val << import - Export << "\n";
  file << Models::EPEC::prn::label << "Export price"
         << ":" << Models::EPEC::prn::val << exportPrice << "\n";
  file << Models::EPEC::prn::label << " -> Total Export"
         << ":" << Models::EPEC::prn::val << Export << "\n";
  file << Models::EPEC::prn::label << " -> Total Import"
         << ":" << Models::EPEC::prn::val << import << '\n';
  file << Models::EPEC::prn::label << "Domestic consumed quantity"
         << ":" << Models::EPEC::prn::val << import - Export + prod << "\n";
  file << Models::EPEC::prn::label << "Domestic price"
         << ":" << Models::EPEC::prn::val
         << Params.DemandParam.alpha - Params.DemandParam.beta * (import - Export + prod) << "\n";

  file.close();

  // Follower productions
  file << "- - - - - - - - - - - - - - - - - - - - - - - - - \n";
  file << "FOLLOWER DETAILS:\n";
  for (unsigned int j = 0; j < Params.n_followers; ++j)
     this->WriteFollower(i, j, filename, x, false);

  file << "\n\n\n";
  // FILE OPERATIONS END
}

void Models::EPEC::EPEC::WriteFollower(const unsigned int i,
                                                    const unsigned int j,
                                                    const std::string &filename,
                                                    const arma::vec &  x,
                                                    bool               append) const {
  std::ofstream file;
  file.open(filename, append ? std::ios::app : std::ios::out);

  // Country Variables
  const LeadAllPar &Params = this->AllLeadPars.at(i);
  unsigned int      foll_prod, foll_tax, foll_lim, foll_taxQ = 0;
  foll_prod = this->getPosition(i, Models::EPEC::LeaderVars::FollowerStart);
  foll_tax  = this->getPosition(i, Models::EPEC::LeaderVars::Tax);
  foll_lim  = this->getPosition(i, Models::EPEC::LeaderVars::Caps);
  if (Params.LeaderParam.tax_revenue)
     foll_taxQ = this->getPosition(i, Models::EPEC::LeaderVars::TaxQuad);

  std::string name;
  try {
     name = Params.name + " --- " + Params.FollowerParam.names.at(j);
  } catch (...) {
     name = "Follower " + std::to_string(j) + " of leader " + std::to_string(i);
  }

  file << "\n" << name << "\n\n"; //<<" named "<<Params.FollowerParam.names.at(j)<<"\n";
  double tax;
  if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::StandardTax)
     tax = x.at(foll_tax + j);
  else
     tax = x.at(foll_tax);
  const double q    = x.at(foll_prod + j);
  double       taxQ = 0;
  if (Params.LeaderParam.tax_revenue)
     taxQ = q > 0 ? x.at(foll_taxQ + j) / q : x.at(foll_taxQ + j);
  const double lim  = x.at(foll_lim + j);
  const double lin  = Params.FollowerParam.costs_lin.at(j);
  const double quad = Params.FollowerParam.costs_quad.at(j);

  file << Models::EPEC::prn::label << "Quantity produced"
         << ":" << Models::EPEC::prn::val << q << '\n';
  // file << "x(): " << foll_prod + j << '\n';
  file << Models::EPEC::prn::label << "Capacity of production"
         << ":" << Models::EPEC::prn::val << Params.FollowerParam.capacities.at(j) << "\n";
  file << Models::EPEC::prn::label << "Limit on production"
         << ":" << Models::EPEC::prn::val << lim << "\n";
  // file << "x(): " << foll_lim + j << '\n';
  file << Models::EPEC::prn::label << "Tax imposed"
         << ":" << Models::EPEC::prn::val << tax;
  if (Params.LeaderParam.tax_type == Models::EPEC::TaxType::CarbonTax) {
     tax = tax * Params.FollowerParam.emission_costs.at(j);
     file << " per unit emission; " << tax << " per unit energy";
  }
  file << "\n";
  if (Params.LeaderParam.tax_revenue)
     file << Models::EPEC::prn::label << "Tax imposed (Q)"
            << ":" << Models::EPEC::prn::val << taxQ << "\n";
  // file << Models::EPEC::prn::label << "Tax cap" << ":" <<
  // Params.FollowerParam.tax_caps.at(j) << tax << "\n";
  // file << "x(): " << foll_tax + j << '\n';
  file << Models::EPEC::prn::label << "  -Production cost function"
         << ":"
         << "\t C(q) = (" << lin << " + " << tax << ")*q + 0.5*" << quad << "*q^2\n"
         << Models::EPEC::prn::label << " "
         << "=" << Models::EPEC::prn::val << (lin + tax) * q + 0.5 * quad * q * q << "\n";
  file << Models::EPEC::prn::label << "  -Marginal cost of production"
         << ":" << Models::EPEC::prn::val << quad * q + lin + tax << "\n";
  file << Models::EPEC::prn::label << "Emission cost"
         << ":" << Models::EPEC::prn::val << Params.FollowerParam.emission_costs.at(j) << '\n';

  file.close();
}