Object Oriented Optimization



Hi,

I have the following problem with re-using a fortran 77 optimization
method in the context of an object-oriented (more precisely object-
based) fortran 90
software. The goal of my message is to submit the solution that I
found
and the problem that I did not solve to the fortran gurus of the
forum,
especially fortran 90 object-based gurus. The problem that I have is
on how to design a re-usable optimization method.

I have an existing fortran 77 optimization method in which the user
must
provide a function which role is to compute the objective and the
constraints.
This function must follow a given template with a pre-defined name :

double precision function fonc ( x , co , ifonc )
implicit none
double precision, dimension (:), intent(in) :: x
double precision, dimension (:), intent(out) :: co
integer, intent(inout) :: ifonc
end function fonc

The array "x" is the parameter, the value of "fonc" is the objective,
the
array "co" is the constraints and ifonc is an error flag. This an
overview of the call tree :

client code
|
+- f77_solver ()
|
+ < ... bla ...>
|
+ fonc

The problem is that we have to use the same optimization method with
several objectives and constraints. But if we put specific code in the
function "fonc", we can only have one optimization problem available
for
one executable because all this is defined in a static way.

One solution would have been to modify the optimization fortran 77
source
code, so that the "solve" subroutine takes as an argument the "fonc"
function. This would have been solved at compile-time to call the
specific function. A template "solve" subroutine would be this one :

subroutine f77_solver ( fonc_function )
implicit none
interface
double precision function fonc_function ( x , co , ifonc )
implicit none
double precision, dimension (:), intent(in) :: x
double precision, dimension (:), intent(out) :: co
integer, intent(inout) :: ifonc
end function fonc_function
end interface
! Sometime later :
obj = fonc_function ( x , co , ifonc )
end subroutine f77_solver

In the client code, one pass the function to the optim_solve
subroutine :

call optim_solve ( myfonc1 )
call optim_solve ( myfonc2 )
etc...

This method must not be confused with the function pointers, as they
are
defined in the C language. All this fortran source is defined in a
static way, which is pre-defined at compile-time. It is not defined at
run-time, as in can be in C.

The problem is that the "optim_solve" subroutine does not do the
resolution by itself. Instead, it uses a complex call tree in which
the
"fonc" function is called several times by several subroutines at
different levels. This solution would imply to modify the complex
fortran 77 source code, which would have been time-consuming and which
could have created (additional) bugs.

Another solution to that problem is the "reverse communication"
pattern.
In that type of method, each time that the solver needs to compute the
objective, the solver sets an output flag to a particular value, then
returns. See Dongarra's article :
http://citeseer.ist.psu.edu/dongarra95reverse.html
A sample use of reverse communication is NLPQL :
http://www.old.uni-bayreuth.de/departments/math/~kschittkowski/nlpqlp22.htm
A discussion on reverse-communication has allready been done on
comp.lang.fortran :
http://groups.google.fr/group/sci.math.num-analysis/browse_frm/thread/23453d99f75a5f27/5c17ca0ddc199e33?lnk=gst&q=reverse+communication#5c17ca0ddc199e33
Depending on the returning value of flag of the solver, the
client code knows the current situation :
- the algorithm is finished (and, may be, converged),
- one has to compute the objective.
If one has to compute the objective, one does so and calls the solver
again. This is an elegant method to create an optimization method
which
has no static link to the computation method. The optimization solver
is,
with reverse communication, really re-usable, without any modification
by
the client code. But the problem is for us that we should modify the
existing fortran 77 source code. The algorithm which is behind reverse
communication seems to imply a lot of modifications, which is time-
consuming etc...

I also thought about a pre-processing trick, based on macro
definitions.
But I haven't found anything simple with that idea.

In fact, what we really need is a function pointer similar to what
exist in C.
This would allow to set the pointer before calling the solver, then
call the solver which can evaluate the objective whenever and wherever
it has to.
But function pointers do not exist in fortran 77, 90, 95 (2000 ?) so I
had to
find another solution.

I got to the following solution, where the function pointer is
emulated
by an integer flag. The idea is to define a unique external function
"fonc", which then calls a function "optdrive_fonc" which is located
in a
fortran 90 module. The function "optdrive_fonc" changes its behaviour
depending on the value of the integer flag (=the function pointer).

See for example the function fonc calling the function optdrive_fonc :

double precision function fonc ( x , co , ifonc )
use m_driver_optim, only : optdrive_fonc
implicit none
double precision, dimension (:), intent(in) :: x
double precision, dimension (:), intent(out) :: co
integer, intent(inout) :: ifonc
fonc = optdrive_fonc ( x , co , ifonc )
end function fonc

The function optdrive_fonc let us choose between several optimization
functions where each one corresponds to a specific optimization
problem.

module m_driver_optim
! Objective function to optimize
integer, public :: objective_function
integer, parameter, public :: PB1 = 1
integer, parameter, public :: PB2 = 2
contains
double precision function optdrive_fonc ( x , co , ifonc )
implicit none
double precision, dimension (:), intent(in) :: x
double precision, dimension (:), intent(out) :: co
integer, intent(inout) :: ifonc
external test_fonc
double precision :: test_fonc
external optm_fonc2
double precision :: optm_fonc2
select case ( objective_function )
case ( PB1 )
optdrive_fonc = test_fonc1 ( x , co , ifonc )
case ( PB2 )
optdrive_fonc = test_fonc2 ( x , co , ifonc )
case default
! TODO : generate an exception.
end select
end function optdrive_fonc
end module m_driver_optim

In the client of the optimization method, it is easy to configure the
problem by a simple set of the objective_function variable.

use m_driver_optim, only : objective_function, PB1, PB2
objective_function = PB1
call f77_solver ()

This solution is simple and efficient. But the problem which remains
is
that the evaluation of the objective cannot be done with the parameter
array "x" by itself. Some additional data have to be provided. The
traditional "fortran 77" way is to put additional data in a global
variable, accessed with a COMMON. In Fortran 90, this can be done with
a
derived-type. That derived-type can be defined in the "test_fonc1"
function or in the "m_driver_optim" module itself. For example :

module m_driver_optim
use m_fonc_module, only : test_fonc3, DATATYPE
type ( DATATYPE ) , save :: mydata
contains
double precision function optdrive_fonc ( x , co , ifonc )
implicit none
double precision, dimension (:), intent(in) :: x
double precision, dimension (:), intent(out) :: co
integer, intent(inout) :: ifonc
external test_fonc
double precision :: test_fonc
select case ( objective_function )
case ( FONC1 )
optdrive_fonc = test_fonc1 ( x , co , ifonc )
case ( FONC3 )
optdrive_fonc = test_fonc3 ( mydata , x , co , ifonc )
case default
! TODO : generate an exception.
end select
end function optdrive_fonc
end module m_driver_optim

That solution is used in the context of an optimization fortran 90
module, developed
following the ideas of object-based programming. That module is based
on personal ideas and external influences, but I recently discovered
(!) that
similar ideas have been presented in
http://www.ccs.lanl.gov/CCS/CCS-4/pdf/obf90.pdf
or since at least 10 years :
http://exodus.physics.ucla.edu/Fortran95/PSTIResearchLecSeries1.html
or :
http://www.cs.rpi.edu/~szymansk/oof90.html
and that a discussion on this subject has taken place here :
http://groups.google.fr/group/comp.lang.fortran/browse_frm/thread/9c13f0b670831908/8f6808345655d24c?lnk=gst&q=object+oriented#8f6808345655d24c
While my message is not completely linked to that subject, it is not
independent.

I think that I am not alone to have these kinds of "how to re-use a
fortran 77 code" problems.
Any comments or suggestions are welcomed.

Best regards,
Michaël

.



Relevant Pages

  • Re: help about ARPACK solver
    ... Fortran uses single precision variables by default. ... Fortran program, that is *single* precision. ... I still dont know what kind of criteria Matlab adopts to form ...
    (sci.math.num-analysis)
  • Re: help about ARPACK solver
    ... Fortran uses single precision variables by default. ... Fortran program, that is *single* precision. ...
    (sci.math.num-analysis)
  • Re: help about ARPACK solver
    ... could be as precise as, however, when I used the eigenvector ... Fortran uses single precision variables by default. ... Fortran program, that is *single* precision. ...
    (sci.math.num-analysis)
  • Re: help about ARPACK solver
    ... Matlab's eigsfunction uses ARPACK, so logically your Fortran ... Matlab uses 'double precision' variables throughout by default. ... Fortran uses single precision variables by default. ... Fortran program, that is *single* precision. ...
    (sci.math.num-analysis)
  • Re: Matlab Vectorisation Speed - How is it done in c++?
    ... Beating the performance of vectorized Matlab code is very ... Matlab makes calls to optimized C and Fortran libraries ... Use optimization level 3 on numerical code and level 2 on non- ...
    (comp.soft-sys.matlab)