/* Copyright 2002 Ben Blum, Christian Shelton
 *
 * This file is part of GameTracer.
 *
 * GameTracer 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.
 *
 * GameTracer 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 GameTracer; if not, write to the Free Software Foundation, 
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "cmatrix.h"
#include "ipa.h"
#include "gnmgame.h"

// IPA(A,g,zh,alpha,fuzz,ans)
// --------------------------
// This runs the IPA algorithm on game A.
// Interpretation of parameters:
// g: perturbation ray.
// zh: initial approximation for z.  Can be set to vector of all 1's.
// alpha: stepsize.  Must be a number between 0 and 1, to be interpreted
//        as the fraction of a complete step to take.
// fuzz: the cutoff accuracy for an equilibrium after which the algorithm
//       stops refining it
// ans: a pre-allocated vector in which the equilibrium will be stored

int IPA(gnmgame &A, cvector &g, cvector &zh, double alpha, double fuzz, cvector &ans) {
  int N = A.getNumPlayers(),
    M = A.getNumActions(), // For easy reference
    i,j,n,bestAction,B, // utility vars
    Im[N], // best actions in perturbed game
    firstIteration = 1; 

  double bestPayoff,l; // utility vars

  cmatrix DG(M,M), 
    O(N,N,0), // matrix of zeroes
    S(N,M,0), // 
    I(M+N,M+N,1,1), // identity
    T(M+N,M+N+2,0), // tableau for Lemke-Howson
    T2(M+N,M+N,0); // submatrix of tableau used if Lemke-Howson is unnecessary

  cvector d(M), // diff
    u(M),
    y(M), // old z
    yh(M), // old zh
    s(M), // current strategy
    so(M), // old strategy
    sh(M), // approximate strategy
    sho(M,0), // old approximate strategy
    z(M), // current point in game-space
    zt(M), // next approximating point
    ym1(M), // utility vars
    ym2(M), 
    ymn1(M+N),
    ymn2(M+N);

  // Find the best action for each player when the game is highly perturbed
  for(n = 0; n < N; n++) {
    bestPayoff = g[A.firstAction(n)];
    bestAction = A.firstAction(n);
    for(j = bestAction+1; j < A.lastAction(n); j++) {
      if(g[j] > bestPayoff) {
	bestPayoff = g[j];
	bestAction = j;
      }
    }
    Im[n] = bestAction;
  }


  A.retract(sh, zh);
  so = sh;

  while(1) {
    A.payoffMatrix(DG,sh,0.0);
    DG /= (double)(N-1); // find the Jacobian of the approximating bimatrix game

    // Initialize the Lemke-Howson tableau
    for(n = 0; n < N; n++) {
      for(i = 0; i < M+N; i++) {
	if(i >= A.firstAction(n) && i < A.lastAction(n)) {
	  T[M+n][i] = 1;
	  T[i][M+n] = -1;
	} else {
	  T[M+n][i] = T[i][M+n] = 0;
	}
      }
    }
    for(n = 0; n < M; n++) {
      T[n][M+N] = g[n];
      T[n][M+N+1] = 0.0;
    }
    for(n = 0; n < N; n++) {
      T[M+n][M+N+1] = 1.0;
      T[M+n][M+N] = 0.0;
    }
    for(i = 0; i < M; i++)
      for(j = 0; j < M; j++)
	T[i][j] = DG[i][j];

    // copy the tableau to T2
    for(i = 0; i < M+N; i++)
      for(j = 0; j < M+N; j++)
	T2[i][j] = T[i][j];

    // zero out columns not in the support
    for(i = 0; i < M; i++) {
      if(so[i] <= 0.0) {
	for(j = 0; j < M+N; j++) {
	  if(i == j)
	    T2[j][i] = 1.0;
	  else
	    T2[j][i] = 0.0;
	}
      }
    }
    
    for(i = 0; i < M; i++) {
      ymn1[i] = 0;
    }
    
    for(i = M; i < M+N; i++) {
      ymn1[i] = 1;
    }
    
    // find equilibrium assuming current support
    T2.solve(ymn1, ymn2);
    
    for(i = 0; i < M; i++)
      s[i] = ymn2[i];

    int flag = 0;
    for(i = 0; i < M; i++) {
      if(s[i] < 0.0) {
	flag = 1; // need to update the support
	break;
      }
    }
    if(flag) { // update support and solve
      A.LemkeHowson(s,T,Im);
    } else {
      // limit to current support
      for(i = 0; i < M; i++) {
	if(so[i] <= 0.0)
	  s[i] = 0.0;
      }
    }

    DG.multiply(s,z);
    z += s;
    
    // see if the support has changed
    B = 1;
    for(i = 0; i < M; i++) {
      if((s[i] > 0.0) != (so[i] > 0.0)) {
	B = 0;
	break;
      }
    } 
    
    // see if angle between z-sh and zh-sh is acute; if so, scale zh.

    u = zh;
    u -= sh;

    ym1 = z;
    ym1 -= sh;
    l = (ym1 * u) / u.norm2(); // dot product
    if(l <= 0.0 || B) {
      zh = u;
      zh *= l;
      zh += sh;
      yh -= sho;
      yh *= l;
      yh += sho;
    }
    
    ym1 = z;
    ym1 -= zh;
    ym2 = s;
    ym2 -= sh;
    // if z and zh or s and sh are close enough, 
    // we've got an approximate equilibrium, so we can quit
    if(N <= 2 || (ym1.norm() < fuzz || ym2.norm() < fuzz)) {
      ans = s;
      A.payoffMatrix(DG,s,0.0);
      return 1;
    }
    ym1 = z;
    // do a first-order approximation on the first iteration,
    // and subsequently do a second-order approximation
    if(!firstIteration) {
      d = z;
      d -= zh;
      d -= y;
      d += yh;
      // Rule of false position
      if(B && d.absmax() > fuzz) {
	for(i = 0; i < M; i++) {
	  ym1[i] = (yh[i] * z[i] - zh[i] * y[i])/d[i];
	}
      } else {
      // Only do a first-order approximation
	ym1 = z;
      }
    } else {
      firstIteration = 0;
      ym1 = z;
    }
    zt = ym1;
    zt *= alpha / (1-alpha);
    zt += zh;
    zt *= (1-alpha);
    // zt = ym1*alpha + zh * (1-alpha)

    // Update values
    so = s;
    y = z;
    sho = sh;
    yh = zh;
    zh = zt;
    A.retract(sh,zh);
  }
}


syntax highlighted by Code2HTML, v. 0.9.1