Program Listing for File epec.cpp
↰ Return to documentation for file (src/games/epec.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 "games/epec.h"
#include "games/algorithms/EPEC/epec_polybase.h"
#include <memory>
void Game::EPEC::finalize() {
if (this->Finalized)
std::cerr << "Warning in Game::EPEC::finalize: Model already Finalized\n";
this->NumPlayers = static_cast<int>(this->PlayersLowerLevels.size());
this->preFinalize();
// Vector with the number of variables of the convex hull
this->ConvexHullVariables = std::vector<unsigned int>(this->NumPlayers, 0);
// Reset the statistic of feasible polyhedra for any player
this->Stats.AlgorithmData.FeasiblePolyhedra.set(std::vector<unsigned int>(this->NumPlayers, 0));
// Assign locations to the variables
this->computeLeaderLocations(this->numMCVariables);
// Initialize leader objective and PlayersQP
this->LeaderObjective = std::vector<std::shared_ptr<MathOpt::QP_Objective>>(NumPlayers);
this->LeaderObjectiveConvexHull = std::vector<std::shared_ptr<MathOpt::QP_Objective>>(NumPlayers);
this->PlayersQP = std::vector<std::shared_ptr<MathOpt::QP_Param>>(NumPlayers);
this->PlayersLCP = std::vector<std::shared_ptr<MathOpt::LCP>>(NumPlayers);
this->SizesWithoutHull = std::vector<unsigned int>(NumPlayers, 0);
// For each player
for (unsigned int i = 0; i < this->NumPlayers; i++) {
// Add a trivial variables to the player's lower level
this->addDummyLead(i);
// Leader objective and its dummy-enlarged version with convex hull variables
this->LeaderObjective.at(i) = std::make_shared<MathOpt::QP_Objective>();
this->LeaderObjectiveConvexHull.at(i) = std::make_shared<MathOpt::QP_Objective>();
// Fill the original objectives
this->makeObjectivePlayer(i, *this->LeaderObjective.at(i).get());
// Compute sizes
this->SizesWithoutHull.at(i) = *this->LocEnds.at(i);
}
// Finalize
this->Finalized = true;
this->postFinalize();
}
void Game::EPEC::addDummyLead(const unsigned int i) {
ZEROAssert(i < this->NumPlayers);
const unsigned int nEPECvars = this->NumVariables;
const unsigned int nThisCountryvars = *this->LocEnds.at(i);
ZEROAssert(nEPECvars >= nThisCountryvars);
// Add dummy vars associated with "everything else"
this->PlayersLowerLevels.at(i).get()->addDummy(nEPECvars - nThisCountryvars);
}
void Game::EPEC::computeLeaderLocations(const unsigned int addSpaceForMC) {
this->LeaderLocations = std::vector<unsigned int>(this->NumPlayers);
this->LeaderLocations.at(0) = 0;
for (unsigned int i = 1; i < this->NumPlayers; i++) {
this->LeaderLocations.at(i) = this->LeaderLocations.at(i - 1) + *this->LocEnds.at(i - 1);
}
this->NumVariables = this->LeaderLocations.back() + *this->LocEnds.back() + addSpaceForMC;
}
void Game::EPEC::getXMinusI(const arma::vec &x, const unsigned int &i, arma::vec &xMinusI) const {
ZEROAssert(i < this->NumPlayers);
const unsigned int nEPECvars = this->NumVariables;
const unsigned int nThisCountryvars = *this->LocEnds.at(i);
const unsigned int nThisCountryHullVars = this->ConvexHullVariables.at(i);
const auto nConvexHullVars = static_cast<const unsigned int>(
std::accumulate(this->ConvexHullVariables.rbegin(), this->ConvexHullVariables.rend(), 0));
xMinusI.zeros(nEPECvars - // All variables in EPEC
nThisCountryvars - // Subtracting this country's variables
nConvexHullVars + // We don't want any convex hull variables
nThisCountryHullVars); // We subtract the hull variables
// associated to the ith player
// convex hull vars, since we double subtracted
for (unsigned int j = 0, count = 0, current = 0; j < this->NumPlayers; ++j) {
if (i != j) {
current = *this->LocEnds.at(j) - this->ConvexHullVariables.at(j);
xMinusI.subvec(count, count + current - 1) =
x.subvec(this->LeaderLocations.at(j), this->LeaderLocations.at(j) + current - 1);
count += current;
}
}
// We need to keep track of MC_vars also for this country
for (unsigned int j = 0; j < this->numMCVariables; j++)
xMinusI.at(xMinusI.n_rows - this->numMCVariables + j) =
x.at(this->NumVariables - this->numMCVariables + j);
}
void Game::EPEC::getXofI(const arma::vec &x,
const unsigned int &i,
arma::vec &xOfI,
bool hull) const {
ZEROAssert(i < this->NumPlayers);
const unsigned int nThisCountryvars = *this->LocEnds.at(i);
const unsigned int nThisCountryHullVars = this->ConvexHullVariables.at(i);
unsigned int vars, current = 0;
if (hull) {
vars = nThisCountryvars;
current = *this->LocEnds.at(i);
} else {
vars = nThisCountryvars - nThisCountryHullVars;
current = *this->LocEnds.at(i) - this->ConvexHullVariables.at(i);
}
xOfI.zeros(vars);
xOfI.subvec(0, vars - 1) =
x.subvec(this->LeaderLocations.at(i), this->LeaderLocations.at(i) + current - 1);
}
void Game::EPEC::getXWithoutHull(const arma::vec &x, arma::vec &xWithoutHull) const {
const unsigned int nEPECvars = this->NumVariables;
const auto nConvexHullVars = static_cast<const unsigned int>(
std::accumulate(this->ConvexHullVariables.rbegin(), this->ConvexHullVariables.rend(), 0));
xWithoutHull.zeros(nEPECvars - // All variables in EPEC
nConvexHullVars); // We subtract the hull variables
// associated to the convex hull
// convex hull vars
for (unsigned int j = 0, count = 0, current; j < this->NumPlayers; ++j) {
current = *this->LocEnds.at(j) - this->ConvexHullVariables.at(j);
xWithoutHull.subvec(count, count + current - 1) =
x.subvec(this->LeaderLocations.at(j), this->LeaderLocations.at(j) + current - 1);
count += current;
}
for (unsigned int j = 0; j < this->numMCVariables; j++)
xWithoutHull.at(xWithoutHull.n_rows - this->numMCVariables + j) =
x.at(this->NumVariables - this->numMCVariables + j);
}
std::unique_ptr<GRBModel> Game::EPEC::bestResponseProgram(const unsigned int i,
const arma::vec &x,
MathOpt::PolyLCP *customLCP) const {
ZEROAssert(this->Finalized);
ZEROAssert(i < this->NumPlayers);
arma::vec solOther;
this->getXMinusI(x, i, solOther);
auto LCP = (customLCP == nullptr) ? this->PlayersLCP.at(i).get() : customLCP;
if (this->LeaderObjective.at(i)->Q.n_nonzero > 0)
return LCP->LCPasMIQP(this->LeaderObjective.at(i)->Q,
this->LeaderObjective.at(i)->C,
this->LeaderObjective.at(i)->c,
solOther,
true);
else
return LCP->LCPasMILP(
this->LeaderObjective.at(i)->C, this->LeaderObjective.at(i)->c, solOther, true);
}
double Game::EPEC::bestResponse(arma::vec &sol,
unsigned int player,
const arma::vec &x,
const arma::vec &prevDev,
MathOpt::PolyLCP *customLCP) const {
ZEROAssert(this->Finalized);
ZEROAssert(player < this->NumPlayers);
auto model = this->bestResponseProgram(player, x, customLCP);
const int status = model->get(GRB_IntAttr_Status);
if (status == GRB_UNBOUNDED || status == GRB_OPTIMAL) {
//If unbounded or optimal either solution or extreme ray
unsigned int Nx = this->PlayersLCP.at(player)->getNumCols();
sol.zeros(Nx);
for (unsigned int i = 0; i < Nx; ++i)
sol.at(i) = model->getVarByName("x_" + std::to_string(i)).get(GRB_DoubleAttr_X);
if (status == GRB_UNBOUNDED) {
LOG_S(WARNING) << "Game::EPEC::bestResponse: deviation is "
"unbounded.";
GRBLinExpr obj = 0;
model->setObjective(obj);
model->optimize();
if (!prevDev.empty()) {
LOG_S(1) << "Generating an improvement basing on the extreme ray.";
// Fetch objective function coefficients
GRBQuadExpr QuadObj = model->getObjective();
arma::vec objcoeff;
for (int i = 0; i < QuadObj.size(); ++i)
objcoeff.at(i) = QuadObj.getCoeff(i);
// Create objective function objects
arma::vec objvalue = prevDev * objcoeff;
arma::vec newobjvalue{0};
bool improved{false};
// improve following the unbounded ray
while (!improved) {
for (unsigned int i = 0; i < Nx; ++i)
sol.at(i) = sol.at(i) +
model->getVarByName("x_" + std::to_string(i)).get(GRB_DoubleAttr_UnbdRay);
newobjvalue = sol * objcoeff;
if (newobjvalue.at(0) < objvalue.at(0))
improved = true;
}
return newobjvalue.at(0);
} else {
return model->get(GRB_DoubleAttr_ObjVal);
}
}
return model->get(GRB_DoubleAttr_ObjVal);
} else {
return GRB_INFINITY;
}
}
void Game::EPEC::makePlayerQP(const unsigned int i) {
ZEROAssert(this->Finalized);
ZEROAssert(i < this->NumPlayers);
this->PlayersQP.at(i) = std::make_shared<MathOpt::QP_Param>(this->Env);
const auto &origLeadObjec = *this->LeaderObjective.at(i).get();
this->LeaderObjectiveConvexHull.at(i) = std::make_shared<MathOpt::QP_Objective>(
MathOpt::QP_Objective{origLeadObjec.Q, origLeadObjec.C, origLeadObjec.c});
this->PlayersLCP.at(i)->makeQP(*this->LeaderObjectiveConvexHull.at(i).get(),
*this->PlayersQP.at(i).get());
}
void Game::EPEC::makePlayersQPs() {
for (unsigned int i = 0; i < this->NumPlayers; ++i) {
this->Game::EPEC::makePlayerQP(i);
}
for (unsigned int i = 0; i < this->NumPlayers; ++i) {
// Adjusting "stuff" because we now have new convHull variables
unsigned long int originalSizeWithoutHull = this->LeaderObjective.at(i)->Q.n_rows;
unsigned long int convHullVarCount =
this->LeaderObjectiveConvexHull.at(i)->Q.n_rows - originalSizeWithoutHull;
LOG_S(1) << "Game::EPEC::makePlayerQP: Added " << convHullVarCount
<< " convex hull variables to QP #" << i;
// Location details
this->ConvexHullVariables.at(i) = convHullVarCount;
// All other players' QP
if (this->NumPlayers > 1) {
for (int j = 0; j < this->NumPlayers; j++) {
if (i != j) {
this->PlayersQP.at(j)->addDummy(
convHullVarCount,
0,
this->PlayersQP.at(j)->getNumParams() -
this->numMCVariables); // The position to add parameters is
// towards the end of all parameters,
// giving space only for the
// numMCVariables number of market
// clearing variables
}
}
}
}
this->updateLocations();
this->computeLeaderLocations(this->numMCVariables);
}
void Game::EPEC::makeTheLCP() {
ZEROAssert(this->PlayersQP.front() != nullptr);
// Preliminary set up to get the LCP ready
unsigned long int Nvar =
this->PlayersQP.front()->getNumParams() + this->PlayersQP.front()->getNumVars();
arma::sp_mat MC(0, Nvar), dumA(0, Nvar);
arma::vec MCRHS, empty;
MCRHS.zeros(0);
empty.zeros(0);
this->makeMCConstraints(MC, MCRHS);
LOG_S(1) << "Game::EPEC::makeTheLCP(): Market Clearing "
"constraints are ready";
std::vector<std::shared_ptr<MathOpt::MP_Param>> MPCasted;
for (auto &item : this->PlayersQP) {
auto m = std::dynamic_pointer_cast<MathOpt::MP_Param>(item);
MPCasted.push_back(m);
}
this->TheNashGame =
std::make_unique<Game::NashGame>(this->Env, MPCasted, MC, MCRHS, 0, dumA, empty);
LOG_S(1) << "Game::EPEC::makeTheLCP(): NashGame is ready";
this->TheLCP = std::make_unique<MathOpt::LCP>(this->Env, *TheNashGame);
LOG_S(1) << "Game::EPEC::makeTheLCP(): LCP is ready";
LOG_S(2) << *TheNashGame;
}
void Game::EPEC::setWelfareObjective(bool linear = true, bool quadratic = true) {
if (!linear && !quadratic) {
this->LCPModel->setObjective(GRBLinExpr{0}, GRB_MAXIMIZE);
return;
}
std::vector<std::vector<unsigned int>> xOfIs; // indexes of variables for each player
std::vector<std::vector<unsigned int>>
xMinusIs; // indexes of variables for each "other" player (xminusi)
GRBLinExpr linearWelfare = 0;
GRBQuadExpr quadrWelfare = 0;
// Linear part + initialization
for (unsigned int p = 0; p < this->getNumPlayers(); ++p) {
unsigned int playerVars = this->LeaderObjective.at(p)->c.size();
std::vector<unsigned int> xOfI;
for (unsigned int i = this->LeaderLocations.at(p), v = 0;
i < this->LeaderLocations.at(p) + playerVars;
++i, ++v) {
//
xOfI.push_back(i);
linearWelfare += this->LCPModel->getVarByName("x_" + std::to_string(i)) *
this->LeaderObjective.at(p)->c.at(v);
}
xOfIs.push_back(xOfI);
}
// Account for market clearing variables
for (unsigned int p = 0; p < this->getNumPlayers(); ++p) {
// For each player, we build the xMinusI vector
std::vector<unsigned int> xMinusI;
for (unsigned int o = 0; o < this->getNumPlayers(); ++o) {
if (p != o) {
for (unsigned int i = 0; i < this->LeaderObjective.at(o)->c.size(); ++i)
xMinusI.push_back(xOfIs.at(o).at(i));
}
}
// Get the MC variables
for (unsigned int j = 0; j < this->numMCVariables; j++)
xMinusI.push_back(this->NumVariables - this->numMCVariables + j);
xMinusIs.push_back(xMinusI);
}
// Now we can build the objective
for (unsigned int p = 0; p < this->getNumPlayers(); ++p) {
GRBQuadExpr interact = 0;
for (arma::sp_mat::const_iterator it = this->LeaderObjective.at(p)->C.begin();
it != this->LeaderObjective.at(p)->C.end();
++it) {
unsigned int xPlayer = xOfIs.at(p).at(it.row());
unsigned int xOther = xMinusIs.at(p).at(it.col());
interact += *it * this->LCPModel->getVarByName("x_" + std::to_string(xPlayer)) *
this->LCPModel->getVarByName("x_" + std::to_string(xOther));
}
quadrWelfare += interact;
}
if (quadratic) {
if (linear) { // both linear and quadratic
LOG_S(INFO) << "Game::EPEC::setWelfareObjective: Setting linear+quadratic objective.";
this->LCPModel->setObjective(linearWelfare + quadrWelfare, GRB_MINIMIZE);
} else { // just quadratic
LOG_S(INFO) << "Game::EPEC::setWelfareObjective: Setting quadratic objective.";
this->LCPModel->setObjective(quadrWelfare, GRB_MINIMIZE);
}
this->LCPModel->set(GRB_IntParam_NonConvex, 2);
} else {
// Then just linear
LOG_S(INFO) << "Game::EPEC::setWelfareObjective: Setting linear objective.";
this->LCPModel->setObjective(linearWelfare, GRB_MINIMIZE);
}
}
bool Game::EPEC::computeNashEq(bool pureNE,
double localTimeLimit = -1,
bool check = false,
bool linearWelfare = false,
bool quadraticWelfare = false) {
// Make the Nash Game between countries
this->NashEquilibrium = false;
LOG_S(1) << "Game::EPEC::computeNashEq: Making the Master LCP";
this->makeTheLCP();
LOG_S(1) << "Game::EPEC::computeNashEq: Made the Master LCP";
auto solver = this->Stats.AlgorithmData.LCPSolver.get();
if (solver == Data::LCP::Algorithms::PATH) {
LOG_S(WARNING) << "Game::EPEC::computeNashEq: Cannot use PATH fallback (EPEC Market Clearings "
"cannot be handeled). Using MIP";
solver = Data::LCP::Algorithms::MIP;
}
/*
* In these cases, we can only use a MIP solver to get multiple solutions or PNEs
*/
unsigned int threads = 1;
if (solver == Data::LCP::Algorithms::MIP || solver == Data::LCP::Algorithms::MINLP) {
if (this->Stats.AlgorithmData.Threads.get() >= 8) {
int wrk = static_cast<int>(std::round(std::floor(this->Stats.AlgorithmData.Threads.get() / 4)));
threads = std::max(wrk, 1);
LOG_S(INFO) << "Game::EPEC::computeNashEq: Threads set to " << threads << ".";
}
}
bool multipleNE = check;
if (check &&
this->Stats.AlgorithmData.Algorithm.get() == Data::EPEC::Algorithms::OuterApproximation) {
LOG_S(WARNING) << "Game::EPEC::computeNashEq: (check flag is "
"true) Cannot search fore multiple NE with the CutAndPlay.";
multipleNE = false;
}
if (pureNE) {
LOG_S(INFO) << "Game::EPEC::computeNashEq: (PureNashEquilibrium flag is "
"true) Searching for a pure NE.";
if (this->Stats.AlgorithmData.Algorithm.get() != Data::EPEC::Algorithms::OuterApproximation)
this->Algorithm->makeThePureLCP();
else
LOG_S(WARNING) << "Game::EPEC::computeNashEq: (PureNashEquilibrium flag is "
"true) Cannot search fore pure NE with the CutAndPlay.";
}
if (this->TheLCP->getNumRows() > 250000) {
LOG_S(WARNING) << "Game::EPEC::computeNashEq: Too many complementarities. Aborting";
this->Stats.Status.set(ZEROStatus::Numerical);
return false;
}
this->LCPModel =
this->TheLCP->LCPasMIP(false, localTimeLimit, threads, multipleNE ? GRB_MAXINT : 1);
this->setWelfareObjective(linearWelfare, quadraticWelfare);
try {
this->LCPModel->set(GRB_IntParam_OutputFlag, 1);
this->LCPModel->set(GRB_IntParam_NumericFocus, 1);
this->LCPModel->optimize();
} catch (GRBException &e) {
throw ZEROException(e);
}
try { // Try finding a Nash equilibrium for the approximation
this->NashEquilibrium =
this->TheLCP->extractSols(this->LCPModel.get(), SolutionZ, SolutionX, true);
} catch (GRBException &e) {
throw ZEROException(e);
}
if (this->NashEquilibrium) { // If a Nash equilibrium is found, then update
// appropriately
if (multipleNE) {
int scount = this->LCPModel->get(GRB_IntAttr_SolCount);
LOG_S(INFO) << "Game::EPEC::computeNashEq: number of equilibria is " << scount;
for (int k = 0, stop = 0; k < scount && stop == 0; ++k) {
this->LCPModel->set(GRB_IntParam_SolutionNumber, k);
this->NashEquilibrium =
this->TheLCP->extractSols(this->LCPModel.get(), this->SolutionZ, this->SolutionX, true);
if (this->Algorithm->isSolved()) {
LOG_S(INFO) << "Game::EPEC::computeNashEq: an "
"Equilibrium has been found";
stop = 1;
}
}
} else {
this->NashEquilibrium = true;
LOG_S(INFO) << "Game::EPEC::computeNashEq: An Equilibrium has been found (Status: "
<< this->LCPModel->get(GRB_IntAttr_Status) << ")";
}
} else {
LOG_S(INFO) << "Game::EPEC::computeNashEq: no equilibrium has been found.";
int status = this->LCPModel->get(GRB_IntAttr_Status);
if (status == GRB_TIME_LIMIT)
this->Stats.Status.set(ZEROStatus::TimeLimit);
else
this->Stats.Status.set(ZEROStatus::NashEqNotFound);
}
return this->NashEquilibrium;
}
bool Game::EPEC::warmstart(const arma::vec &x) {
ZEROAssert(x.size() >= this->getNumVar());
ZEROAssert(this->Finalized);
ZEROAssert(this->PlayersQP.front() == nullptr);
this->SolutionX = x;
std::vector<arma::vec> devns = std::vector<arma::vec>(this->NumPlayers);
std::vector<arma::vec> prevDevns = std::vector<arma::vec>(this->NumPlayers);
this->makePlayersQPs();
arma::vec devn;
if (this->Algorithm->isSolved())
LOG_S(WARNING) << "Game::EPEC::warmstart: "
"The loaded solution is optimal.";
else
LOG_S(WARNING) << "Game::EPEC::warmstart: "
"The loaded solution is NOT optimal. Trying to repair.";
return true;
}
bool Game::EPEC::isPureStrategy(double tol) const { return this->Algorithm->isPureStrategy(tol); }
bool Game::EPEC::isSolved(double tol) const { return this->Algorithm->isSolved(); }
void Game::EPEC::findNashEq() {
ZEROAssert(this->Finalized);
if (this->Stats.Status.get() != ZEROStatus::Uninitialized) {
LOG_S(ERROR) << "Game::EPEC::findNashEq: a Nash Eq was "
"already found. Calling this findNashEq might lead to errors!";
}
std::stringstream final_msg;
switch (this->Stats.AlgorithmData.Algorithm.get()) {
case Data::EPEC::Algorithms::InnerApproximation: {
final_msg << "Inner approximation: run completed. ";
this->Algorithm = std::shared_ptr<Algorithms::EPEC::PolyBase>(
new class Algorithms::EPEC::InnerApproximation(this->Env, this));
this->Algorithm->solve();
} break;
case Data::EPEC::Algorithms::CombinatorialPne: {
final_msg << "CombinatorialPNE: run completed. ";
this->Algorithm = std::shared_ptr<Algorithms::EPEC::PolyBase>(
new class Algorithms::EPEC::CombinatorialPNE(this->Env, this));
this->Algorithm->solve();
} break;
case Data::EPEC::Algorithms::OuterApproximation: {
final_msg << "Cut-and-Play: run completed. ";
this->Algorithm = std::shared_ptr<Algorithms::EPEC::PolyBase>(
new class Algorithms::EPEC::CutAndPlay(this->Env, this));
this->Algorithm->solve();
} break;
case Data::EPEC::Algorithms::FullEnumeration: {
final_msg << "Full enumeration: run completed. ";
this->Algorithm = std::shared_ptr<Algorithms::EPEC::PolyBase>(
new class Algorithms::EPEC::FullEnumeration(this->Env, this));
this->Algorithm->solve();
} break;
}
const std::chrono::duration<double> timeElapsed =
std::chrono::high_resolution_clock::now() - this->InitTime;
this->Stats.WallClockTime.set(timeElapsed.count() * std::chrono::milliseconds::period::num /
std::chrono::milliseconds::period::den);
// Handing EPECStatistics object to track performance of algorithm
if (this->LCPModel) {
this->Stats.NumVar = this->LCPModel->get(GRB_IntAttr_NumVars);
this->Stats.NumConstraints = this->LCPModel->get(GRB_IntAttr_NumConstrs);
this->Stats.NumNonZero = this->LCPModel->get(GRB_IntAttr_NumNZs);
} // Assigning appropriate Status messages after solving
switch (this->Stats.Status.get()) {
case ZEROStatus::NashEqNotFound:
final_msg << "No Nash equilibrium exists.";
break;
case ZEROStatus::NashEqFound: {
final_msg << "Found a Nash equilibrium ("
<< (this->Stats.PureNashEquilibrium.get() == 0 ? "MNE" : "PNE") << ").";
} break;
case ZEROStatus::TimeLimit:
final_msg << "Nash equilibrium not found. The time limit was hit.";
break;
case ZEROStatus::Numerical:
final_msg << "Nash equilibrium not found. The Numerical issues flag was triggered.";
break;
default:
final_msg << "Nash equilibrium not found. Unknown status.";
break;
}
LOG_S(INFO) << "Game::EPEC::findNashEq: " << final_msg.str();
}
void Game::EPEC::setAlgorithm(Data::EPEC::Algorithms algorithm) {
this->Stats.AlgorithmData.Algorithm.set(algorithm);
}
void Game::EPEC::setRecoverStrategy(Data::EPEC::RecoverStrategy strategy) {
this->Stats.AlgorithmData.RecoverStrategy.set(strategy);
}
unsigned int Game::EPEC::getPositionLeadFoll(const unsigned int i, const unsigned int j) const {
ZEROAssert(i < this->NumPlayers);
const auto LeaderStart = this->TheNashGame->getPrimalLoc(i);
return LeaderStart + j;
}
unsigned int Game::EPEC::getPositionLeadLead(const unsigned int i, const unsigned int j) const {
ZEROAssert(i < this->NumPlayers);
const auto LeaderStart = this->TheNashGame->getPrimalLoc(i);
return LeaderStart + this->PlayersLCP.at(i)->getLStart() + j;
}
double Game::EPEC::getValLeadFoll(const unsigned int i, const unsigned int j) const {
ZEROAssert(i < this->NumPlayers);
ZEROAssert(this->LCPModel != nullptr);
return this->LCPModel->getVarByName("x_" + std::to_string(this->getPositionLeadFoll(i, j)))
.get(GRB_DoubleAttr_X);
}
std::vector<unsigned int> Game::EPEC::mixedStrategyPoly(unsigned int i, double tol) const {
ZEROAssert(i < this->NumPlayers);
ZEROAssert(this->LCPModel != nullptr);
return this->Algorithm->mixedStrategyPoly(i, tol);
}
double Game::EPEC::getValProbab(unsigned int i, unsigned int k) {
ZEROAssert(i < this->NumPlayers);
ZEROAssert(this->LCPModel != nullptr);
return this->Algorithm->getValProbab(i, k);
}
double Game::EPEC::getValLeadFollPoly(const unsigned int i,
const unsigned int j,
const unsigned int k,
const double tol) const {
ZEROAssert(i < this->NumPlayers);
ZEROAssert(this->LCPModel != nullptr);
return this->Algorithm->getValLeadFollPoly(i, j, k, tol);
}
double Game::EPEC::getValLeadLeadPoly(const unsigned int i,
const unsigned int j,
const unsigned int k,
const double tol) const {
ZEROAssert(i < this->NumPlayers);
ZEROAssert(this->LCPModel != nullptr);
return this->Algorithm->getValLeadLeadPoly(i, j, k, tol);
}
double Game::EPEC::getValLeadLead(const unsigned int i, const unsigned int j) const {
ZEROAssert(i < this->NumPlayers);
ZEROAssert(this->LCPModel != nullptr);
return this->LCPModel->getVarByName("x_" + std::to_string(this->getPositionLeadLead(i, j)))
.get(GRB_DoubleAttr_X);
}
void Game::EPEC::setBranchingStrategy(Data::EPEC::BranchingStrategy strategy) {
this->Stats.AlgorithmData.BranchingStrategy.set(strategy);
}
std::string std::to_string(const Data::EPEC::Algorithms al) {
switch (al) {
case Data::EPEC::Algorithms::FullEnumeration:
return std::string("FullEnumeration");
case Data::EPEC::Algorithms::InnerApproximation:
return std::string("InnerApproximation");
case Data::EPEC::Algorithms::CombinatorialPne:
return std::string("CombinatorialPNE");
case Data::EPEC::Algorithms::OuterApproximation:
return std::string("CutAndPlay");
}
return "";
}
std::string std::to_string(const Data::EPEC::RecoverStrategy strategy) {
switch (strategy) {
case Data::EPEC::RecoverStrategy::IncrementalEnumeration:
return std::string("IncrementalEnumeration");
case Data::EPEC::RecoverStrategy::Combinatorial:
return std::string("Combinatorial");
}
return "";
}
std::string std::to_string(const Data::EPEC::BranchingStrategy strategy) {
switch (strategy) {
case Data::EPEC::BranchingStrategy::HybridBranching:
return std::string("HybridBranching");
case Data::EPEC::BranchingStrategy::DeviationBranching:
return std::string("DeviationBranching");
}
return "";
}