3

I have been writing a Fortran and C interface, but there always appears errors. I'm not sure why there is the problem. The original C code is following:

#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multiroots.h>

void gsl_vector_setvalue(gsl_vector* v, size_t index, double value)
{
   gsl_vector_set(v, index-1, value);
}

double gsl_vector_getvalue(gsl_vector* v, size_t index)
{
   return gsl_vector_get(v, index-1);
}

int rosenbrock_f (const gsl_vector * x, void * params, gsl_vector * f)
{
   double x0 = gsl_vector_get (x, 0);
   double x1 = gsl_vector_get (x, 1);
   double y0 = 1. * (1 - x0);
   double y1 = 10. * (x1 - x0 * x0);
   gsl_vector_set (f, 0, y0);
   gsl_vector_set (f, 1, y1);
   return GSL_SUCCESS;
}

int print_state (size_t iter, gsl_multiroot_fsolver * s)
{
    printf ("iter = %3zu x = % .3f % .3f " "f(x) = % .3e % .3e\n", iter, gsl_vector_get (s->x, 0), gsl_vector_get (s->x, 1), gsl_vector_get (s->f, 0), gsl_vector_get (s->f, 1));
}

void gsl_multiroots(int rosenbrock())
{
   const gsl_multiroot_fsolver_type *T;
   gsl_multiroot_fsolver *s;
   int status;
   size_t i, iter = 0;
   const size_t n = 2;
   //struct rparams p = {1.0, 10.0};
   struct rparams *p=NULL;
   gsl_multiroot_function f = {rosenbrock, n, p};
   double x_init[2] = {-10.0, -5.0};
   gsl_vector *x = gsl_vector_alloc (n);
   gsl_vector_set (x, 0, x_init[0]);
   gsl_vector_set (x, 1, x_init[1]);
   T = gsl_multiroot_fsolver_hybrids;
   s = gsl_multiroot_fsolver_alloc (T, 2);
   gsl_multiroot_fsolver_set (s, &f, x);
   print_state (iter, s);
   do
   {
      iter++;
      status = gsl_multiroot_fsolver_iterate (s);
      print_state (iter, s);
      if (status) break;
/* check if solver is stuck */
      status = gsl_multiroot_test_residual (s->f, 1e-7);
   }
   while (status == GSL_CONTINUE && iter < 1000);
   printf ("status = %s\n", gsl_strerror (status));
   gsl_multiroot_fsolver_free (s);
   gsl_vector_free (x);
}

int main()
{
   gsl_multiroots(rosenbrock_f);
   return 0;
}

And when I compile and link it, it can run and have the correct result. But when I write rosenbrock_f in a Fortran code and use the intrinsic mode iso_c_binding to make them interoperable, it can't have the result, only say

> Program received signal SIGSEGV: Segmentation fault - invalid memory
> reference.
> 
> Backtrace for this error:
> #0  0x7FCE7F618777
> #1  0x7FCE7F618D7E
> #2  0x7FCE7F270D3F
> #3  0x7FCE7FCF68AD
> #4  0x400BAE in gsl_vector_getvalue
> #5  0x400E45 in rosenbrock.1876 at multiroot.f90:?
> #6  0x7FCE7FC4A923
> #7  0x400D6F in gsl_multiroots
> #8  0x400F0D in MAIN__ at multiroot.f90:? Segmentation fault (core dumped)

here is the C and Fortran code:

#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multiroots.h>

void gsl_vector_setvalue(gsl_vector* v, size_t index, double value)
{
   gsl_vector_set(v, index-1, value);
}

double gsl_vector_getvalue(gsl_vector* v, size_t index)
{
   return gsl_vector_get(v, index-1);
}

int print_state (size_t iter, gsl_multiroot_fsolver * s)
{
    printf ("iter = %3zu x = % .3f % .3f " "f(x) = % .3e % .3e\n", iter, gsl_vector_get (s->x, 0), gsl_vector_get (s->x, 1), gsl_vector_get (s->f, 0), gsl_vector_get (s->f, 1));
}

void gsl_multiroots(int rosenbrock())
{
   const gsl_multiroot_fsolver_type *T;
   gsl_multiroot_fsolver *s;
   int status;
   size_t i, iter = 0;
   const size_t n = 2;
   struct rparams *p=NULL;
   gsl_multiroot_function f = {rosenbrock, n, p};
   double x_init[2] = {-10.0, -5.0};
   gsl_vector *x = gsl_vector_alloc (n);
   gsl_vector_set (x, 0, x_init[0]);
   gsl_vector_set (x, 1, x_init[1]);
   T = gsl_multiroot_fsolver_hybrids;
   s = gsl_multiroot_fsolver_alloc (T, 2);
   gsl_multiroot_fsolver_set (s, &f, x);
   print_state (iter, s);
   do
   {
      iter++;
      status = gsl_multiroot_fsolver_iterate (s);
      print_state (iter, s);
      if (status) break;
/* check if solver is stuck */
      status = gsl_multiroot_test_residual (s->f, 1e-7);
   }
   while (status == GSL_CONTINUE && iter < 1000);
   printf ("status = %s\n", gsl_strerror (status));
   gsl_multiroot_fsolver_free (s);
   gsl_vector_free (x);
}

fortran module:

MODULE gsl_interfaces

USE, INTRINSIC :: ISO_C_BINDING

IMPLICIT NONE

TYPE, BIND(C) :: gsl_block
   INTEGER(C_SIZE_T) :: size
   TYPE(C_PTR) :: data
END TYPE gsl_block

TYPE, BIND(C) :: gsl_vector
   INTEGER(C_SIZE_T) :: size
   INTEGER(C_SIZE_T) :: stride
   REAL(C_DOUBLE) :: data
   TYPE(C_PTR) :: block
   INTEGER(C_INT) :: owner
END TYPE gsl_vector

TYPE, BIND(C) :: gsl_matrix
   INTEGER(C_SIZE_T) :: size1
   INTEGER(C_SIZE_T) :: size2
   INTEGER(C_SIZE_T) :: tda
   REAL(C_DOUBLE) :: data
   TYPE(C_PTR) :: block
   INTEGER(C_INT) :: owner
END TYPE gsl_matrix

TYPE, BIND(C) :: rparams
   REAL(C_DOUBLE) :: A
   real(c_double) :: b
END TYPE

INTERFACE
   SUBROUTINE GSL_VECTOR_SETVALUE(V, ROW_INDEX, V_VALUE) BIND(C, NAME='gsl_vector_setvalue')
      USE ISO_C_BINDING
      import gsl_vector
      TYPE(c_ptr), VALUE :: V
      INTEGER :: ROW_INDEX
      REAL(C_DOUBLE), VALUE :: V_VALUE
   END SUBROUTINE GSL_VECTOR_SETVALUE
   REAL(C_DOUBLE) FUNCTION GSL_VECTOR_GETVALUE(V, ROW_INDEX) BIND(C, NAME='gsl_vector_getvalue')
      USE ISO_C_BINDING
      import gsl_vector
      TYPE(c_ptr), VALUE :: V
      INTEGER :: ROW_INDEX
   END FUNCTION GSL_VECTOR_GETVALUE
   SUBROUTINE GSL_MULTIROOTS(ROSENBROCK) BIND(C, NAME='gsl_multiroots')
      USE ISO_C_BINDING
      type(c_funptr), value :: ROSENBROCK
!      TYPE(c_ptr), value :: x
   END SUBROUTINE
!   INTEGER(C_INT) FUNCTION ROSENBROCK(x, params, f) BIND(C, NAME='rosenbrock_f')
!      USE ISO_C_BINDING
!      import gsl_vector
!      IMPORT rparams
!      TYPE(gsl_vector), target :: x, f
!      TYPE(C_PTR), target :: params
!   END FUNCTION ROSENBROCK
END INTERFACE
END MODULE gsl_interfaces

fortran program:

PROGRAM MULTIROOT

USE, INTRINSIC :: ISO_C_BINDING
USE GSL_INTERFACES
IMPLICIT NONE
integer(c_int), parameter :: GSL_SUCCESS=0
type(gsl_vector), target :: x, f

CALL GSL_MULTIROOTS(c_funloc(ROSENBROCK))

contains
   integer(c_int) function rosenbrock(x, params, f) bind(c)
      use iso_c_binding
      use gsl_interfaces
      type(c_ptr) :: x, f
      type(c_ptr) :: params
      real(c_double) :: x0, x1, y0, y1
      !a=params%a
      !b=params%b
      x0 = gsl_vector_getvalue (x, 1)
      x1 = gsl_vector_getvalue (x, 2)
      y0 = 1. * (1 - x0)
      y1 = 10. * (x1 - x0 * x0)
      call gsl_vector_setvalue (f, 1, y0)
      call gsl_vector_setvalue (f, 2, y1)
      rosenbrock=GSL_SUCCESS
   end function rosenbrock
END PROGRAM 

Can someone help me out? I don't know why there is memory invalid reference.

zmwang
  • 519
  • 1
  • 7
  • 13
  • Though not very sure, is it no problem to write "void gsl_multiroots( int rosenbrock() )" rather than "void gsl_multiroots(int (* rosenbrock)())"...? – roygvib Sep 03 '15 at 12:40
  • I tried your answer, but it's not work. Thank you for answer my question. I suppose there is some mistake on params in integer(c_int) function rosenbrock. – zmwang Sep 03 '15 at 13:59
  • One more trial: how about attaching the VALUE attribute to all the dummy arguments (x, f, and params) in rosenbrock()....? – roygvib Sep 03 '15 at 14:17
  • 1
    ROW_INDEX in the interface block may also need VALUE... hmm complicated. – roygvib Sep 03 '15 at 14:29
  • well, I have tried VALUE attribute for x, params and f, but it also doesn't work, ROW_INDEX is not an interoperable variable, so I think it doesn't need VALUE attribute. – zmwang Sep 03 '15 at 14:39
  • integer(c_size_t) may be an interoperable type for size_t, though I have never used this before so not sure again (sorry...) – roygvib Sep 03 '15 at 14:52
  • @roygvib c_size_t is interoperatble with size_t. Fortran 2008 15.3.2, table 15.2. – casey Sep 03 '15 at 15:39
  • Row_index definitely would need a value attribute here, as it is not a call by reference in the c interface. But I am missing how this actually is supposed to work. GSL_MULTIROOTS works completely disconnected from the x and f defined in the main program, so there appears to be no reason for the structure declarations in Fortran. To the Fortran caller this could just be some c_ptr handle right now. Do you really need the internal structure in the Fortran caller? – haraldkl Sep 03 '15 at 15:47
  • 3
    There is a public Fortran interface to GSL: http://www.lrz.de/services/software/mathematik/gsl/fortran/. Looking at their implementation may help. – M. S. B. Sep 03 '15 at 16:36
  • @roygvib you are right, if I add value attribute to row_index, x, params and f, it works. I'm really sorry for not listening to your advice, because I really tried to add value attribute to x, params and f, but I forget I never add value to row_index. When I encountered this problem, I tried many method. I forgot what I used and what I didn't. Your comment really work, I just want to say that and apologize to you. – zmwang Sep 04 '15 at 01:48
  • @haraldkl I really don't need the declaration of the derived type variable x & f in main program. I comment the declaration line and it still works. Row_index really need a value attribute. c_ptr is OK for my program, gsl_vector type is of no use. – zmwang Sep 04 '15 at 01:55
  • @zmwang, great, if you merely need this as a handle, stick to the c_ptr. But I would recommend you to consider the suggestion by M. S. B. and giving the wrapper from LRZ a try. If it does not provide the functionality you need, you could still improve that. I didn't have a look at it and didn't know it existed before, but why duplicate the work... – haraldkl Sep 04 '15 at 04:57
  • @zmwang No problem at all, it's nice that your problem has been solved! :) One more point to note is that the concept of VALUE and C interoperability is independent (though they were introduced into Fortran at the same, I guess). The VALUE attribute can be used between Fortran subroutines even without any C functions. Another point is that type(c_ptr) contains the address of another object as its value, so VALUE is needed to send the address of that another object; otherwise, the address of the type(c_ptr) variable itself will be sent! This corresponds to int ** in the C side, for example. – roygvib Sep 05 '15 at 12:57
  • @roygvib you mean that if I miss the VALUE attribute after type(c_ptr), it is interoperable with int ** variable, and if I add the VALUE attribute, it is interoperable with int * variable. Do I misunderstand what you say? – zmwang Sep 07 '15 at 03:09
  • @zmwang Your last comment (about the effect of VALUE) is exactly what I wanted to say :) I feel this is pretty confusing because Fortran sends the address of Fortran variable by default (rather than the value of Fortran variable)... – roygvib Sep 07 '15 at 17:51
  • @zmwang [this](http://stackoverflow.com/questions/9677972/how-to-allocate-an-array-inside-fortran-routine-called-from-c/9680050#9680050) and [this](http://stackoverflow.com/questions/31596166/call-fortran-subroutine-with-allocatables-in-r/32444968#32444968) pages are also useful to see the behavior of type(c_ptr) :) Cheers – roygvib Sep 07 '15 at 21:27

1 Answers1

4

Note: M. S. B. pointed out that there exists a Fortran wrapper for the GNU Scientific Library already.

Here is what works for me with gcc 5.2.

I modified your C code to align with the example given in the gsl documentation, because I was not sure, wether the ordering of your function description matched the interface. Also, I changed the function pointer declaration to match, what the gsl is expecting.

#include <stdlib.h>                                                                                                                                   
#include <stdio.h>                                                                                                                                    
#include <gsl/gsl_vector.h>                                                                                                                           
#include <gsl/gsl_multiroots.h>                                                                                                                       


void gsl_vector_setvalue(gsl_vector* v, size_t index, double value)                                                                                   
{                                                                                                                                                     
  gsl_vector_set(v, index-1, value);                                                                                                                 
}                                                                                                                                                     

double gsl_vector_getvalue(gsl_vector* v, size_t index)                                                                                               
{                                                                                                                                                     
  return gsl_vector_get(v, index-1);                                                                                                                 
}                                                                                                                                                     

int print_state (size_t iter, gsl_multiroot_fsolver * s)                                                                                              
{                                                                                                                                                     
  printf ("iter = %3zu x = % .3f % .3f " "f(x) = % .3e % .3e\n", iter, gsl_vector_get (s->x, 0), gsl_vector_get (s->x, 1), gsl_vector_get (s->f, 0), gsl_vector_get (s->f, 1));                                                                                                                           
}                                                                                                                                                     

void gsl_multiroots(int (*rosenbrock)(gsl_vector *x, void *p, gsl_vector *f))                                                                         
{                                                                                                                                                     
  const gsl_multiroot_fsolver_type *T;                                                                                                               
  gsl_multiroot_fsolver *s;                                                                                                                          
  int status;                                                                                                                                        
  size_t i, iter = 0;                                                                                                                                
  const size_t n = 2;                                                                                                                                
  void *p = NULL;                                                                                                                                    
  gsl_multiroot_function f;                                                                                                                          
  f.f = rosenbrock;                                                                                                                                  
  f.n = n;                                                                                                                                           
  f.params = &p;                                                                                                                                     
  double x_init[2] = {-10.0, -5.0};                                                                                                                  
  gsl_vector *x = gsl_vector_alloc (n);                                                                                                              
  gsl_vector_set (x, 0, x_init[0]);                                                                                                                  
  gsl_vector_set (x, 1, x_init[1]);                                                                                                                  
  T = gsl_multiroot_fsolver_hybrids;                                                                                                                 
  s = gsl_multiroot_fsolver_alloc (T, 2);                                                                                                            
  gsl_multiroot_fsolver_set (s, &f, x);                                                                                                              
  print_state (iter, s);                                                                                                                             
  do                                                                                                                                                 
  {                                                                                                                                                  
    iter++;                                                                                                                                         
    status = gsl_multiroot_fsolver_iterate (s);                                                                                                     
    print_state (iter, s);                                                                                                                          
    if (status) break;                                                                                                                              
    /* check if solver is stuck */                                                                                                                        
    status = gsl_multiroot_test_residual (s->f, 1e-7);                                                                                              
  }                                                                                                                                                  
  while (status == GSL_CONTINUE && iter < 1000);                                                                                                     
  printf ("status = %s\n", gsl_strerror (status));                                                                                                   
  gsl_multiroot_fsolver_free (s);                                                                                                                    
  gsl_vector_free (x);                                                                                                                               
}

In the interfaces I added the value attribute for the integers, that are expected as call by value parameters:

MODULE gsl_interfaces                                                                                                                                 

  USE, INTRINSIC :: ISO_C_BINDING                                                                                                                       

  IMPLICIT NONE                                                                                                                                         

  INTERFACE                                                                                                                                             
    SUBROUTINE GSL_VECTOR_SETVALUE(V, ROW_INDEX, V_VALUE) BIND(C, NAME='gsl_vector_setvalue')                                                          
      USE ISO_C_BINDING                                                                                                                               
      TYPE(c_ptr), VALUE :: V                                                                                                                         
      INTEGER,value :: ROW_INDEX                                                                                                                      
      REAL(C_DOUBLE), VALUE :: V_VALUE                                                                                                                
    END SUBROUTINE GSL_VECTOR_SETVALUE                                                                                                                 

    REAL(C_DOUBLE) FUNCTION GSL_VECTOR_GETVALUE(V, ROW_INDEX) BIND(C, NAME='gsl_vector_getvalue')                                                      
      USE ISO_C_BINDING                                                                                                                               
      TYPE(c_ptr), VALUE :: V                                                                                                                         
      INTEGER,value :: ROW_INDEX                                                                                                                      
    END FUNCTION GSL_VECTOR_GETVALUE                                                                                                                   
    SUBROUTINE GSL_MULTIROOTS(ROSENBROCK) BIND(C, NAME='gsl_multiroots')                                                                               
      USE ISO_C_BINDING                                                                                                                               
      type(c_funptr), value :: ROSENBROCK                                                                                                             
    END SUBROUTINE                                                                                                                                     
  END INTERFACE                                                                                                                                         
END MODULE gsl_interfaces

And in the rosenbrock function, I added missing value attributes for the c_ptr arguments:

PROGRAM MULTIROOT                                                                                                                                     

  USE, INTRINSIC :: ISO_C_BINDING                                                                                                                       
  USE GSL_INTERFACES                                                                                                                                    
  IMPLICIT NONE                                                                                                                                         
  integer(c_int), parameter :: GSL_SUCCESS=0                                                                                                            

  CALL GSL_MULTIROOTS(c_funloc(ROSENBROCK))                                                                                                             

contains                                                                                                                                              
  integer(c_int) function rosenbrock(x, params, f) bind(c)                                                                                           
    use iso_c_binding                                                                                                                               
    use gsl_interfaces                                                                                                                              
    type(c_ptr),value :: x, f                                                                                                                       
    type(c_ptr),value :: params                                                                                                                     
    real(c_double) :: x0, x1, y0, y1                                                                                                                
    !a=params%a                                                                                                                                     
    !b=params%b                                                                                                                                     
    x0 = gsl_vector_getvalue (x, 1)                                                                                                                 
    x1 = gsl_vector_getvalue (x, 2)                                                                                                                 
    y0 = 1. * (1 - x0)                                                                                                                              
    y1 = 10. * (x1 - x0 * x0)                                                                                                                       
    call gsl_vector_setvalue (f, 1, y0)                                                                                                             
    call gsl_vector_setvalue (f, 2, y1)                                                                                                             
    rosenbrock=GSL_SUCCESS                                                                                                                          
  end function rosenbrock                                                                                                                            
END PROGRAM

When running this, I get:

iter =   0 x = -10.000 -5.000 f(x) =  1.100e+01 -1.050e+03
iter =   1 x = -10.000 -5.000 f(x) =  1.100e+01 -1.050e+03
iter =   2 x = -3.976  24.827 f(x) =  4.976e+00  9.020e+01
iter =   3 x = -3.976  24.827 f(x) =  4.976e+00  9.020e+01
iter =   4 x = -3.976  24.827 f(x) =  4.976e+00  9.020e+01
iter =   5 x = -1.274 -5.680 f(x) =  2.274e+00 -7.302e+01
iter =   6 x = -1.274 -5.680 f(x) =  2.274e+00 -7.302e+01
iter =   7 x =  0.249  0.298 f(x) =  7.511e-01  2.359e+00
iter =   8 x =  0.249  0.298 f(x) =  7.511e-01  2.359e+00
iter =   9 x =  1.000  0.878 f(x) = -2.653e-10 -1.218e+00
iter =  10 x =  1.000  0.989 f(x) = -2.353e-11 -1.080e-01
iter =  11 x =  1.000  1.000 f(x) =  0.000e+00  0.000e+00
status = success

I hope this is somewhat useful and helps you to get to what you actually would like to achieve. I would stay away from declaring Fortran types for the C structs if possible. If you can just use the c_ptr as handles in the caller, that should be much less pain.

haraldkl
  • 3,809
  • 26
  • 44
  • This result is exactly what I want to have. But I'm confused why x, params and f should have a value attirbute. They are pointers in c code. and x & f are gsl_vector type, they are not values. – zmwang Sep 04 '15 at 02:00
  • @zmwang in your declaration for the function they are type(c_ptr), therefore they need to have the value attribute, as the c interface also has "just" a c pointer and not a pointer to a pointer. Its the same logic as for v in GSL_VECTOR_{SET|GET}VALUE, where you did this declaration correctly. – haraldkl Sep 04 '15 at 04:50