/*File: rosenbrock1.c

  Purpose: 
		   it is the GA version of rosenbrock1.c to test the class TaoGAApplication. 
  Acknowledgement: 
		   the code was originally developed by Dr.Steve Bensor and others at ANL and modified by Dr. Limin Zhang
  		   and Dr. Jarek Nieplochai from PNNL.
*/
     

/**************************************************************

Author: Limin Zhang, Ph.D.
        Mathematics Department
        Columbia Basin College
        Pasco, WA 99301
        Limin.Zhang@cbc2.org

Mentor: Jarek Naplocha, Ph.D.
        Environmental Molecular Science Laboratory
        Pacific Northwest National Laboratory
        Richland, WA 99352

Date: 4/22/2002

Purpose:
      to design and implement an application for  between TAO and
      global arrays.
**************************************************************/


/*$Id: s.rosenbrock1.c 1.41 02/02/14 14:22:13-06:00 sarich@holmes.mcs.anl.gov $*/

/* Program usage: mpirun -machinefile m -np 1 rosenbrock1 [-help] [all TAO options] */
/* Program usage: mpirun -machinefile m -np 4 rosenbrock1 [-help] [all TAO options] */
/*MPI header*/
#include <mpi.h> 

/*  Include "tao.h" so we can use TAO solvers.  */
#include "tao.h"		//for tao initialization 

/*include "ga.h" so that we can use GA*/
#include "ga.h" 
#include "taovec_ga.h" 
#include "taoapp_ga.h" 
#include "macdecls.h" 



/*some standard C header files */
#include <iostream> 
#include <stdio.h> 
#include <stdlib.h>		//for malloc function 
#include <math.h> 


static char help[] = "This example demonstrates use of the TAO package to \n\
solve an unconstrained minimization problem on a single processor.  We \n\
minimize the extended Rosenbrock function: \n\
   sum_{i=0}^{n/2-1} ( alpha*(x_{2i+1}-x_{2i}^2)^2 + (1-x_{2i})^2 ) \n";

/*T 
   Concepts: TAO - Solving an unconstrained minimization problem
   Routines: TaoInitialize(); TaoFinalize(); TaoSetFromOptions();
   Routines: TaoGAApplicationCreate(); TaoSetApplication();
   Routines: TaoCreate(); TaoSetGAFunctionGradient(); 
   Routines: TaoSetGAHessian(); TaoSetGAInitialVector(); 
   Routines: TaoSolve(); TaoDestroy(); TaoApplicationDestroy();
   Routines: TaoGetTerminationReason();
   Processors: 1
T*/


/* 
   User-defined application context - contains data needed by the 
   application-provided call-back routines that evaluate the function,
   gradient, and hessian.
*/
typedef struct
{
  int n;			/* dimension */
  double alpha;			/* condition parameter */
}
AppCtx;

/* -------------- User-defined routines ---------- */
int FormFunctionGradient (TAO_GA_APPLICATION, GAVec, double *, GAVec, void *);
int SetVariableBounds (TAO_GA_APPLICATION, GAVec, GAVec, void*);

int main (int argc, char **argv)
{
  int info;			/* used to check for functions returning nonzeros */
  GAVec ga_x;			/* solution, gradient vectors */
  TAO_SOLVER tao;		/* TAO_SOLVER solver context */
  TAO_GA_APPLICATION taoapp;	/* TAO application context */
  TaoTerminateReason reason;
  AppCtx user;			/* user-defined application context */

  /*initialize GA and MPI */
  int heap = 20000, stack = 20000;
  int me, nproc;
  MPI_Init (&argc, &argv);	/* initialize MPI */
  GA_Initialize ();		/* initialize GA */
  me = GA_Nodeid ();
  nproc = GA_Nnodes ();
  if (me == 0) {
    if (GA_Uses_fapi ())
      GA_Error ("Program runs with C array API only", 0);
    printf ("Using %ld processes\n", (long) nproc);
    fflush (stdout);
  }
  heap /= nproc;
  stack /= nproc;
  if (!MA_init (MT_F_DBL, stack, heap))
    GA_Error ("MA_init failed", stack + heap);	/* initialize memory allocator */

  /* Initialize TAO */
  TaoInitialize (&argc, &argv, (char *) 0, help);

  /* Initialize problem parameters */
  user.n = 2;
  user.alpha = 99.0;

  /* Allocate vectors for the solution and gradient */
  int ndim = 1, dims[2], type = C_DBL;
  dims[0] = user.n;
  ga_x = NGA_Create (type, ndim, dims, "GA_X", NULL);
  if (!ga_x) GA_Error ("rosenbrock1.main::NGA_Create ga_x", ga_x);
  /*  ga_g = GA_Duplicate (ga_x, "GA_G");
      if (!ga_g) GA_Error ("rosenbrock1.main::NGA_Create ga_g", ga_g); */

  /* The TAO code begins here */
  /* Create TAO solver with desired solution method */
  info = TaoCreate (MPI_COMM_WORLD, "tao_blmvm", &tao); CHKERRQ(info);
  info = TaoGAApplicationCreate (MPI_COMM_WORLD, &taoapp); CHKERRQ(info);

  info = TaoGAAppSetInitialSolutionVec(taoapp, ga_x); CHKERRQ(info);

  info = TaoGAAppSetVariableBoundsRoutine(taoapp, SetVariableBounds, (void*)&user); CHKERRQ(info);

  /* Set routines for function, gradient */
  info = TaoGAAppSetObjectiveAndGradientRoutine (taoapp, FormFunctionGradient, (void *) &user);	 CHKERRQ(info);



  /* Check for TAO command line options */
  info = TaoSetFromOptions (tao); CHKERRQ(info);

  /* SOLVE THE APPLICATION */
  info = TaoSolveGAApplication (taoapp,tao); CHKERRQ(info);


  /*  To View TAO solver information use */
  info = TaoView(tao); CHKERRQ(info);

  

  /* Get termination information */
  info = TaoGetTerminationReason (tao, &reason); CHKERRQ(info);

  if (reason <= 0)
    printf
      ("Try a different TAO method, adjust some parameters, or check the function evaluation routines\n");

  /*output the solutions */ 
  if (me == 0)
    printf ("The solution is :\n");
  GA_Print (ga_x);


  /* Free TAO data structures */
  info = TaoDestroy (tao); CHKERRQ(info);
  info = TaoGAAppDestroy (taoapp); CHKERRQ(info);


  /* Free GA data structures */
  GA_Destroy (ga_x);

  /* Finalize TAO, GA, and MPI */
  TaoFinalize ();
  GA_Terminate ();
  MPI_Finalize ();

  if (me == 0)
    printf ("Normal exit...\n");

  return 0;
}

/* -------------------------------------------------------------------- */
/*  
    FormFunctionGradient - Evaluates the function, f(X), and gradient, G(X). 

    Input Parameters:
.   tao  - the TAO_SOLVER context
.   ga_X    - input vector
.   ptr  - optional user-defined context, as set by TaoSetFunctionGradient()
    
    Output Parameters:
.   ga_G - vector containing the newly evaluated gradient
.   f - function value

    Note:
    Some optimization methods ask for the function and the gradient evaluation
    at the same time.  Evaluating both at once may be more efficient that
    evaluating each separately. 
*/


int
FormFunctionGradient (TAO_GA_APPLICATION gaapp, GAVec ga_X, double *f, GAVec ga_G, void *ptr)
{
  AppCtx *user = (AppCtx *) ptr;
  int i, nn = user->n / 2;
  double ff = 0., t1, t2, alpha = user->alpha;
  double glocal[2],xlocal[2];

  int me = GA_Nodeid ();

  GA_Sync();

  if(me ==0){
    
     /* given the size of the problem we will execute this part on process 0 only */

     int lo=0,hi=1; /* range of array indices */
     NGA_Get (ga_X, &lo, &hi, xlocal, &hi);
     /* Compute G(X) */

     for (i=0; i<nn; i++){
       t1 = xlocal[2*i+1]-xlocal[2*i]*xlocal[2*i]; t2= 1-xlocal[2*i];
       ff += alpha*t1*t1 + t2*t2;
       glocal[2*i] = -4*alpha*t1*xlocal[2*i]-2.0*t2;
       glocal[2*i+1] = 2*alpha*t1;
     }
   
     GA_Init_fence();
     NGA_Put(ga_G, &lo, &hi, glocal, &hi);
     GA_Fence();
  }   

  /* broadcast the result to everybody */
  GA_Brdcst(&ff, sizeof(double), 0);

  *f = ff;
  return 0;
}


int SetVariableBounds(TAO_GA_APPLICATION gaapp, GAVec ga_xl, GAVec ga_xu, void *ctx)
{
  double lb = 0.0, ub = 2.0;
  
  GA_Fill(ga_xl, (void*)&lb);
  GA_Fill(ga_xu, (void*)&ub);
  return 0;
}