!  Program usage: mpiexec -n 1 rosenbrock1f [-help] [all TAO options]
!
!  Description:  This example demonstrates use of the TAO package to solve an
!  unconstrained minimization problem on a single processor.  We minimize the
!  extended Rosenbrock function:
!       sum_{i=0}^{n/2-1} (alpha*(x_{2i+1}-x_{2i}^2)^2 + (1-x_{2i})^2)
!
!  The C version of this code is rosenbrock1.c
!

!

! ----------------------------------------------------------------------
!
#include "petsc/finclude/petsctao.h"
module rosenbrock1fmodule
  use petsctao
  implicit none
contains
! --------------------------------------------------------------------
!  FormFunctionGradient - Evaluates the function f(X) and gradient G(X)
!
!  Input Parameters:
!  tao - the Tao context
!  X   - input vector
!  dummy - not used
!
!  Output Parameters:
!  G - vector containing the newly evaluated gradient
!  f - function value

  subroutine FormFunctionGradient(ta, X, f, G, dummy, ierr)
    type(tTao) ta
    type(tVec) X, G
    PetscReal f
    PetscErrorCode ierr
    PetscInt dummy

    PetscReal ff, t1, t2
    PetscInt i, nn
    PetscReal, pointer :: g_v(:), x_v(:)
    PetscReal alpha
    PetscInt n
    common/params/alpha, n

    ierr = 0
    nn = n/2
    ff = 0

!     Get pointers to vector data
    PetscCall(VecGetArrayRead(X, x_v, ierr))
    PetscCall(VecGetArray(G, g_v, ierr))

!     Compute G(X)
    do i = 0, nn - 1
      t1 = x_v(1 + 2*i + 1) - x_v(1 + 2*i)*x_v(1 + 2*i)
      t2 = 1.0 - x_v(1 + 2*i)
      ff = ff + alpha*t1*t1 + t2*t2
      g_v(1 + 2*i) = -4*alpha*t1*x_v(1 + 2*i) - 2.0*t2
      g_v(1 + 2*i + 1) = 2.0*alpha*t1
    end do

!     Restore vectors
    PetscCall(VecRestoreArrayRead(X, x_v, ierr))
    PetscCall(VecRestoreArray(G, g_v, ierr))

    f = ff
    PetscCall(PetscLogFlops(15.0d0*nn, ierr))

  end

!
! ---------------------------------------------------------------------
!
!  FormHessian - Evaluates Hessian matrix.
!
!  Input Parameters:
!  tao     - the Tao context
!  X       - input vector
!  dummy   - optional user-defined context, as set by SNESSetHessian()
!            (not used here)
!
!  Output Parameters:
!  H      - Hessian matrix
!  PrecH  - optionally different matrix used to compute the preconditioner (not used here)
!  ierr   - error code
!
!  Note: Providing the Hessian may not be necessary.  Only some solvers
!  require this matrix.

  subroutine FormHessian(ta, X, H, PrecH, dummy, ierr)

!  Input/output variables:
    type(tTao) ta
    type(tVec) X
    type(tMat) H, PrecH
    PetscErrorCode ierr
    PetscInt dummy

    PetscReal v(0:1, 0:1)
    PetscBool assembled

! PETSc's VecGetArray acts differently in Fortran than it does in C.
! Calling VecGetArray((Vec) X, (PetscReal) x_array(0:1), (PetscOffset) x_index, ierr)
! will return an array of doubles referenced by x_array offset by x_index.
!  i.e.,  to reference the kth element of X, use x_array(k + x_index).
! Notice that by declaring the arrays with range (0:1), we are using the C 0-indexing practice.
    PetscReal, pointer :: x_v(:)
    PetscInt i, nn, ind(0:1), i2
    PetscReal alpha
    PetscInt n
    common/params/alpha, n

    ierr = 0
    nn = n/2
    i2 = 2

!  Zero existing matrix entries
    PetscCall(MatAssembled(H, assembled, ierr))
    if (assembled .eqv. PETSC_TRUE) PetscCall(MatZeroEntries(H, ierr))

!  Get a pointer to vector data

    PetscCall(VecGetArrayRead(X, x_v, ierr))

!  Compute Hessian entries

    do i = 0, nn - 1
      v(1, 1) = 2.0*alpha
      v(0, 0) = -4.0*alpha*(x_v(1 + 2*i + 1) - 3*x_v(1 + 2*i)*x_v(1 + 2*i)) + 2
      v(1, 0) = -4.0*alpha*x_v(1 + 2*i)
      v(0, 1) = v(1, 0)
      ind(0) = 2*i
      ind(1) = 2*i + 1
      PetscCall(MatSetValues(H, i2, ind, i2, ind, reshape(v, [i2*i2]), INSERT_VALUES, ierr))
    end do

!  Restore vector

    PetscCall(VecRestoreArrayRead(X, x_v, ierr))

!  Assemble matrix

    PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY, ierr))
    PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY, ierr))

    PetscCall(PetscLogFlops(9.0d0*nn, ierr))

  end
end module rosenbrock1fmodule

program rosenbrock1f
  use petsctao
  use rosenbrock1fmodule
  implicit none

! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!                   Variable declarations
! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!
!  See additional variable declarations in the file rosenbrock1f.h

  PetscErrorCode ierr    ! used to check for functions returning nonzeros
  type(tVec) x       ! solution vector
  type(tMat) H       ! hessian matrix
  type(tTao) ta     ! TAO_SOVER context
  PetscBool flg
  PetscInt i2, i1
  PetscMPIInt size
  PetscReal zero
  PetscReal alpha
  PetscInt n
  common/params/alpha, n

  zero = 0.0d0
  i2 = 2
  i1 = 1

!  Initialize TAO and PETSc
  PetscCallA(PetscInitialize(ierr))

  PetscCallMPIA(MPI_Comm_size(PETSC_COMM_WORLD, size, ierr))
  PetscCheckA(size == 1, PETSC_COMM_SELF, PETSC_ERR_WRONG_MPI_SIZE, 'This is a uniprocessor example only')

!  Initialize problem parameters
  n = 2
  alpha = 99.0d0

! Check for command line arguments to override defaults
  PetscCallA(PetscOptionsGetInt(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-n', n, flg, ierr))
  PetscCallA(PetscOptionsGetReal(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, '-alpha', alpha, flg, ierr))

!  Allocate vectors for the solution and gradient
  PetscCallA(VecCreateSeq(PETSC_COMM_SELF, n, x, ierr))

!  Allocate storage space for Hessian
  PetscCallA(MatCreateSeqBAIJ(PETSC_COMM_SELF, i2, n, n, i1, PETSC_NULL_INTEGER_ARRAY, H, ierr))

  PetscCallA(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE, ierr))

!  The TAO code begins here

!  Create TAO solver
  PetscCallA(TaoCreate(PETSC_COMM_SELF, ta, ierr))
  PetscCallA(TaoSetType(ta, TAOLMVM, ierr))

!  Set routines for function, gradient, and hessian evaluation
  PetscCallA(TaoSetObjectiveAndGradient(ta, PETSC_NULL_VEC, FormFunctionGradient, 0, ierr))
  PetscCallA(TaoSetHessian(ta, H, H, FormHessian, 0, ierr))

!  Optional: Set initial guess
  PetscCallA(VecSet(x, zero, ierr))
  PetscCallA(TaoSetSolution(ta, x, ierr))

!  Check for TAO command line options
  PetscCallA(TaoSetFromOptions(ta, ierr))

!  SOLVE THE APPLICATION
  PetscCallA(TaoSolve(ta, ierr))

!  TaoView() prints ierr about the TAO solver; the option
!      -tao_view
!  can alternatively be used to activate this at runtime.
!      PetscCallA(TaoView(ta,PETSC_VIEWER_STDOUT_SELF,ierr))

!  Free TAO data structures
  PetscCallA(TaoDestroy(ta, ierr))

!  Free PETSc data structures
  PetscCallA(VecDestroy(x, ierr))
  PetscCallA(MatDestroy(H, ierr))

  PetscCallA(PetscFinalize(ierr))
end program rosenbrock1f
!
!/*TEST
!
!   build:
!      requires: !complex
!
!   test:
!      args: -tao_monitor_short -tao_type ntr -tao_gatol 1.e-5
!      requires: !single
!
!TEST*/
