//
// $Source: /cvsroot/gambit/gambit/sources/tools/liap/nfgliap.cc,v $
// $Date: 2006/02/08 21:12:04 $
// $Revision: 1.14 $
//
// DESCRIPTION:
// Compute Nash equilibria by minimizing Liapunov function
//
// This file is part of Gambit
// Copyright (c) 2002, The Gambit Project
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include <unistd.h>
#include <iostream>
#include <fstream>

#include "libgambit/libgambit.h"
#include "funcmin.h"

extern int m_stopAfter;
extern int m_numTries;
extern int m_maxits1;
extern int m_maxitsN;
extern double m_tol1;
extern double m_tolN;
extern std::string startFile;
extern bool useRandom;
extern int g_numDecimals;
extern bool verbose;

//---------------------------------------------------------------------
//                        class NFLiapFunc
//---------------------------------------------------------------------

class NFLiapFunc : public gC1Function<double>  {
private:
  mutable long _nevals;
  Gambit::Game _nfg;
  mutable Gambit::MixedStrategyProfile<double> _p;

  double Value(const Gambit::Vector<double> &) const;
  bool Gradient(const Gambit::Vector<double> &, Gambit::Vector<double> &) const;

  double LiapDerivValue(int, int, const Gambit::MixedStrategyProfile<double> &) const;
    

public:
  NFLiapFunc(const Gambit::Game &, const Gambit::MixedStrategyProfile<double> &);
  virtual ~NFLiapFunc();
    
  long NumEvals(void) const  { return _nevals; }
};

NFLiapFunc::NFLiapFunc(const Gambit::Game &N,
		       const Gambit::MixedStrategyProfile<double> &start)
  : _nevals(0L), _nfg(N), _p(start)
{ }

NFLiapFunc::~NFLiapFunc()
{ }

double NFLiapFunc::LiapDerivValue(int i1, int j1,
				  const Gambit::MixedStrategyProfile<double> &p) const
{
  int i, j;
  double x, x1, psum;
  
  x = 0.0;
  for (i = 1; i <= _nfg->NumPlayers(); i++)  {
    psum = 0.0;
    for (j = 1; j <= p.GetSupport().NumStrategies(i); j++)  {
      psum += p[p.GetSupport().GetStrategy(i,j)];
      x1 = p.GetStrategyValue(p.GetSupport().GetStrategy(i, j)) - p.GetPayoff(i);
      if (i1 == i) {
	if (x1 > 0.0)
	  x -= x1 * p.GetPayoffDeriv(i, p.GetSupport().GetStrategy(i1, j1));
      }
      else {
	if (x1> 0.0)
	  x += x1 * (p.GetPayoffDeriv(i, 
				      p.GetSupport().GetStrategy(i, j),
				      p.GetSupport().GetStrategy(i1, j1)) - 
		     p.GetPayoffDeriv(i,
				      p.GetSupport().GetStrategy(i1, j1)));
      }
    }
    if (i == i1)  x += 100.0 * (psum - 1.0);
  }
  if (p[p.GetSupport().GetStrategy(i1, j1)] < 0.0) {
    x += p[p.GetSupport().GetStrategy(i1, j1)];
  }
  return 2.0 * x;
}

//
// This function projects a gradient into the plane of the simplex.
// (Actually, it works by computing the projection of 'x' onto the
// vector perpendicular to the plane, then subtracting to compute the
// component parallel to the plane.)
//
static void Project(Gambit::Vector<double> &x, const Gambit::Array<int> &lengths)
{
  int index = 1;
  for (int part = 1; part <= lengths.Length(); part++)  {
    double avg = 0.0;
    int j;
    for (j = 1; j <= lengths[part]; j++, index++)  {
      avg += x[index];
    }
    avg /= (double) lengths[part];
    index -= lengths[part];
    for (j = 1; j <= lengths[part]; j++, index++)  {
      x[index] -= avg;
    }
  }
}

bool NFLiapFunc::Gradient(const Gambit::Vector<double> &v, Gambit::Vector<double> &d) const
{
  ((Gambit::Vector<double> &) _p).operator=(v);
  int i1, j1, ii;
  
  for (i1 = 1, ii = 1; i1 <= _nfg->NumPlayers(); i1++) {
    for (j1 = 1; j1 <= _p.GetSupport().NumStrategies(i1); j1++) {
      d[ii++] = LiapDerivValue(i1, j1, _p);
    }
  }

  Project(d, _p.GetSupport().NumStrategies());
  return true;
}
  
double NFLiapFunc::Value(const Gambit::Vector<double> &v) const
{
  _nevals++;

  ((Gambit::Vector<double> &) _p).operator=(v);
  return _p.GetLiapValue();
}

static void PickRandomProfile(Gambit::MixedStrategyProfile<double> &p)
{
  double sum, tmp;

  for (int pl = 1; pl <= p.GetGame()->NumPlayers(); pl++)  {
    sum = 0.0;
    int st;
    
    for (st = 1; st < p.GetSupport().NumStrategies(pl); st++)  {
      do
	tmp = ((double) rand()) / ((double) RAND_MAX);
      while (tmp + sum > 1.0);
      p[p.GetSupport().GetStrategy(pl, st)] = tmp;
      sum += tmp;
    }
    p[p.GetSupport().GetStrategy(pl, st)] = 1.0 - sum;
  }
}

void PrintProfile(std::ostream &p_stream,
		  const std::string &p_label,
		  const Gambit::MixedStrategyProfile<double> &p_profile)
{
  p_stream << p_label;
  for (int i = 1; i <= p_profile.Length(); i++) {
    p_stream.setf(std::ios::fixed);
    p_stream << ", " << std::setprecision(g_numDecimals) << p_profile[i];
  }

  p_stream << std::endl;
}

bool ReadProfile(std::istream &p_stream,
		 Gambit::MixedStrategyProfile<double> &p_profile)
{
  for (int i = 1; i <= p_profile.Length(); i++) {
    if (p_stream.eof() || p_stream.bad()) {
      return false;
    }

    p_stream >> p_profile[i];
    if (i < p_profile.Length()) {
      char comma;
      p_stream >> comma;
    }
  }

  // Read in the rest of the line and discard
  std::string foo;
  std::getline(p_stream, foo);
  return true;
}

extern std::string startFile;

void SolveStrategic(const Gambit::Game &p_game)
{
  Gambit::List<Gambit::MixedStrategyProfile<double> > starts;

  if (startFile != "") {
    std::ifstream startPoints(startFile.c_str());

    while (!startPoints.eof() && !startPoints.bad()) {
      Gambit::MixedStrategyProfile<double> start(p_game);
      if (ReadProfile(startPoints, start)) {
	starts.Append(start);
      }
    }
  }
  else {
    // Generate the desired number of points randomly
    for (int i = 1; i <= m_numTries; i++) {
      Gambit::MixedStrategyProfile<double> start(p_game);
      PickRandomProfile(start);
      starts.Append(start);
    }
  }

  static const double ALPHA = .00000001;

  for (int i = 1; i <= starts.Length(); i++) {
    Gambit::MixedStrategyProfile<double> p(starts[i]);

    if (verbose) {
      PrintProfile(std::cout, "start", p);
    }

    NFLiapFunc F(p.GetGame(), p);

    // if starting vector not interior, perturb it towards centroid
    int kk;
    for (kk = 1; kk <= p.Length() && p[kk] > ALPHA; kk++);
    if (kk <= p.Length()) {
      Gambit::MixedStrategyProfile<double> centroid(p.GetSupport());
      for (int k = 1; k <= p.Length(); k++) {
	p[k] = centroid[k] * ALPHA + p[k] * (1.0-ALPHA);
      }
    }

    gConjugatePR minimizer(p.Length());
    Gambit::Vector<double> gradient(p.Length()), dx(p.Length());
    double fval;
    minimizer.Set(F, p, fval, gradient, .01, .0001);

    try {
      for (int iter = 1; iter <= m_maxitsN; iter++) {
	if (!minimizer.Iterate(F, p, fval, gradient, dx)) {
	  break;
	}

	if (sqrt(gradient.NormSquared()) < .001) {
	  PrintProfile(std::cout, "NE", p);
	  break;
	}
      }

      if (verbose && sqrt(gradient.NormSquared()) >= .001) {
	PrintProfile(std::cout, "end", p);
      }
    }
    catch (gFuncMinException &) { }
  }
}






syntax highlighted by Code2HTML, v. 0.9.1