Hey guys,
I'm trying to implement a nonlinear constrained optimization with C++
using NLopt to compute one nash equilibrium of an n-person game. The
implementation is based on the function "npg" of the matlab optimization
toolbox, which solves an n-person finite non-co-operative game
(DOI-Link: http://dx.doi.org/10.1109/ICM2CS.2009.5397970). It uses SQP
based method to get a solution of the minimization problem.
The attached code represents my implementation, strictly oriented on the
matlab implementation referred above. Each call of the optimization
function returns "nlopt roundoff-limited" exception.
Does anybody know this exception or can figure out the fault? I'm not
very familiar with the mathematical background of different optimization
formulations. Maybe the calls of add_*_constraint or the formulation of
the objective function causes the exception.
You will need the Eigen library to compile the source code. It is
available at http://bitbucket.org/eigen/eigen/get/3.2.0.tar.bz2 .
Thanks in advance,
Tobias Schmidt
/*
* npg.cpp
*
* The function npg solves an n-person finite non-co-operative game to
* compute one sample Nash Equilibrium. It uses an optimization formulation
* of n-person non-co-operative games as described in the adjoining paper
* "An Optimization Formulation to Compute Nash Equilibrium in finite
* Games" presented by the author.
*
* The inputs to the function are:
* a) M : The row vector containing number of strategies of each player.
* b) U : The matrix containing the pay-offs to the players at various
* pure strategy combinations.
*
* The outputs are:
* a) A : The matrix whose columns are mixed strategies to players at Nash
* equilibrium.
* b) payoff : The row vector containing the pay-offs to the players at Nash
* equilibrium.
* c) iterations : Number of iterations performed by the optimization
* method.
* d) err : The absolute error in the objective function of the minimization
* problem.
*
* For theory of the method the adjoining paper may be consulted. Here an
* example is given for explanantion. Consider a 3-person game where each
* player has 2 strategies each. So M = [2 2 2]. Suppose the pay-offs at the
* pure strategy combinations (1-1-1), (1-1-2) and so on, as described by
* the ranking in the theory, are given as the matrix U =
* [2,7.5,0;3,.2,.3;6,3.6,1.5;2,3.5,5;0,3.2,9;2,3.2,5;0,2,3.2;2.1,0,1]. Then
* after supplying M and U call [A,payoff,iterations,err] = npg(M,U).
*
* The method is capable of giving one sample Nash equilibrium out of
* probably many present in a given game. The screenshot showing GUI has
* been developed on the code using it as dll and VB.Net. The GUI software
* may be made available on request.
*
* Any error in the code may be reported to bhaskerchatter...@gmail.com. Any
* suggestion/comment is greatly appreciated.
*
* Author: tschmidt
* See: n-person game for Optimization Toolbox on Matlab
*/
#include <string.h>
#include <cmath>
#include <iostream>
#include <nlopt.hpp>
#include "Eigen/Core"
using namespace Eigen;
typedef struct
{
const int s, p;
const MatrixXi& I;
const VectorXd& Us;
unsigned int myfun_calls;
} myfun_data_t;
typedef struct
{
const int s, p;
const VectorXi& pay;
const MatrixXi& I;
const MatrixXd& U;
} confun_data_t;
typedef struct
{
const MatrixXd& Aeq;
const VectorXd& beq;
const int s;
} linconfun_data_t;
double myfun(unsigned int n, const double* x, double* grad, void* f_data)
{
myfun_data_t* data = (myfun_data_t*) f_data;
double res = 0, prod = 1;
int i;
unsigned int j, k;
++data->myfun_calls;
for (k = 0; k < n - data->s; ++k)
res += x[data->s + k];
for (i = 0; i < data->p; ++i)
{
for (j = 0; j < n - data->s; ++j)
{
prod *= x[data->I(i,j)-1];
}
res -= data->Us(i) * prod;
prod = 1;
}
return res;
}
void confun(unsigned int m, double* result, unsigned int n, const double* x,
double* grad, void* f_data)
{
confun_data_t* data = (confun_data_t*) (f_data);
// Result vector
VectorXd C = VectorXd::Zero(m);
int i, j;
unsigned int t, k;
double add, prd;
for (i = 0; i < data->s; ++i)
{
C(i) = -x[data->pay(i)-1];
for (t = 0; t < n - data->s; ++t)
{
add = 0;
for (j = 0; j < data->p; ++j)
{
prd = 1;
for (k = 0; k < n - data->s; ++k)
if (i+1 == data->I(j,k))
prd *= data->U(j,k);
else
prd *= x[data->I(j,k)-1];
if (data->I(j,t) != i+1)
prd = 0;
add += prd;
}
C(i) += add;
}
}
// return ...
memcpy(result, C.data(), m * sizeof(double));
}
void linconfun(unsigned int m, double* result, unsigned int n, const double* x,
double* grad, void* f_data)
{
linconfun_data_t* data = (linconfun_data_t*) (f_data);
unsigned int i,j;
for (i = 0; i < n - data->s; ++i)
{
result[i] = -data->beq(i);
for (j = 0; j < m; ++j)
result[i] += data->Aeq(i,j) * x[j];
}
}
int npg(RowVectorXi& iM, MatrixXd& iU, MatrixXd& oA, RowVectorXd& oPayoff,
int* oIterations, double* oErr) throw(const std::string)
{
#if 1 // editor folding of npg matrices initialization code
int i = 0, j = 0, k = 0, p = 1;
double V = 1;
int n = iM.cols();
int s = iM.sum();
oA = MatrixXd::Zero(iM.maxCoeff(), n);
oPayoff = RowVectorXd::Zero(n);
for(i = 0; i < n; ++i)
p = p * iM(i);
if(p != iU.rows() || n != iU.cols())
throw std::string("Error: Dimension mismatch!"); //
ERR_DIM_MISMATCH;
RowVectorXd P = RowVectorXd::Zero(n);
RowVectorXd N = RowVectorXd::Zero(n);
P(n-1) = 1;
for(i = n-2; i >= 0; --i)
P(i) = P(i+1) * iM(i+1);
for(i = 0; i < n; ++i)
N(i) = p / P(i);
VectorXd x0 = VectorXd::Zero(s + n);
for(i = 0; i < n; ++i)
for(j=0; j < iM(i); ++j, ++k)
x0(k) = 1.0 / iM(i);
VectorXd Us = iU.rowwise().sum();
for(i = 0; i < n; ++i)
V = V * std::pow((1.0 / iM(i)), (double) iM(i));
x0.tail(n) << V * (iU.colwise().sum().transpose());
MatrixXd Aeq = MatrixXd::Zero(n, s+n);
int cnt = 0;
for (i = 0; i < n; ++i)
{
if (i != 0)
cnt += iM(i);
for (j = 0; j < s; ++j)
if ((j+1) <= iM.head(i+1).sum() && (j+1) > cnt)
Aeq(i,j) = 1;
}
VectorXd beq = VectorXd::Ones(n);
MatrixXi I = MatrixXi::Ones(p,n);
int counter = 0, count = 0;
for (i = 0; i < n; ++i)
for (j = 0; j < N(i); ++j)
{
++counter;
if (i != 0 && counter > iM.head(i+1).sum())
counter -= iM(i);
for (k = 0; k < P(i); ++k)
I(count++) = counter;
}
VectorXd lb = VectorXd::Zero(s+n);
VectorXd ub = VectorXd::Ones(s+n);
VectorXi pay = VectorXi::Zero(s);
counter = 0;
for (i = 0; i < n; ++i)
for (j = 0; j < iM(i); ++j)
pay(counter++) = i + 1 + s;
lb.tail(n).setConstant(-HUGE_VAL);
ub.tail(n).setConstant(HUGE_VAL);
#endif
// Construct optimization problem
nlopt::opt opt(nlopt::LD_SLSQP, s+n);
myfun_data_t fun_data = {s, p, I, Us, 0};
opt.set_min_objective(&myfun, &fun_data);
confun_data_t confun_data = {s, p, pay, I, iU};
opt.add_inequality_mconstraint(&confun, (void*) &confun_data,
std::vector<double>(s+n,1e-6));
linconfun_data_t linconfun_data = {Aeq, beq, s};
opt.add_equality_mconstraint(&linconfun, (void*) &linconfun_data,
std::vector<double>(s+n,1e-6));
std::vector<double> lbv(lb.data(), lb.data() + lb.rows());
std::vector<double> ubv(ub.data(), ub.data() + ub.rows());
opt.set_lower_bounds(lbv);
opt.set_upper_bounds(ubv);
opt.set_xtol_rel(1e-6);
opt.set_xtol_abs(1e-6);
opt.set_ftol_rel(1e-6);
opt.set_ftol_abs(1e-6);
opt.set_maxeval(1e4);
opt.set_maxtime(10.0);
// Call optimization function and map output
std::vector<double> x(x0.data(), x0.data() + x0.rows());
try
{
opt.optimize(x);
} catch (nlopt::roundoff_limited err)
{
throw std::string("Halted because roundoff errors limited
progress! ") + err.what();
} catch (nlopt::forced_stop err)
{
throw std::string("Halted because of a forced termination! ") +
err.what();
} catch (std::invalid_argument err )
{
throw std::string("Invalid arguments. ") + err.what();
} catch (std::bad_alloc err)
{
throw std::string("Ran out of memory. ") + err.what();
} catch (std::runtime_error err)
{
throw std::string("Generic failure. ") + err.what();
}
count = 0;
for (i = 0; i < n; ++i)
{
for (j = 0; j > iM(i); ++j)
oA(j,i) = std::abs(x[count++]);
oPayoff(i) = x[s+i];
}
*oErr = std::abs(opt.last_optimum_value());
*oIterations = fun_data.myfun_calls;
return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
RowVectorXi m(2);
m << 3, 3;
MatrixXd u(9,2);
u << 0.1324, -0.1324,
0.1238, -0.1238,
0.1155, -0.1155,
0.1470, -0.1470,
0.1384, -0.1384,
0.1301, -0.1301,
0.1610, -0.1610,
0.1524, -0.1524,
0.1442, -0.1442;
MatrixXd a;
RowVectorXd payoff;
int iter = 0;
double err = 0.0;
try
{
npg(m, u, a, payoff, &iter, &err);
} catch(const std::string message)
{
std::cout << message << std::endl;
return EXIT_FAILURE;
}
std::cout << "Iterations: " << iter << "\nFunction value: " << err <<
std::endl;
return EXIT_SUCCESS;
}
_______________________________________________
NLopt-discuss mailing list
NLopt-discuss@ab-initio.mit.edu
http://ab-initio.mit.edu/cgi-bin/mailman/listinfo/nlopt-discuss