    /*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;
    }