从Julia调用Fortran函数,返回一个数组:未知函数,segfault? [英] Calling a Fortran function from Julia, returning an array: unknown function, segfault?

查看:182
本文介绍了从Julia调用Fortran函数,返回一个数组:未知函数,segfault?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从Julia的Fortran库中调用函数。在这种情况下,我有一个函数 eye ,它接受一个I​​nteger,并返回一个二维整数数组。



< Fortran模块使用

  $ gfortran -shared -fPIC -o matrix_routines.so matrix_routines.f90编译为共享库

之后我试图从交互式的Julia解释器中调用它(名字从 nm ):

  julia> n = 5 
5

julia> ccall((:__ matrix_routines_MOD_eye,/path/to/library/matrix_routines.so),Array {Int64,2},(Ptr {Int64}),& n)



然而,这立即导致Julia在我身上抛出段错误:

 信号(11):分段错误
__matrix_routines_MOD_eye位于/path/to/library/matrix_routines.so(未知行)
无文件匿名:0
未知函数( ip:-1137818532)
/usr/bin/../lib/julia/libjulia.so(未知行)
处的jl_f_top_eval REPL.jl处的eval_user_input:53
jlcall_eval_user_input_19998处于(未知行)
/usr/bin/../lib/julia/libjulia.so上的jl_apply_generic(未知行)
在task.jl上的匿名行为:95
/ usr / bin /下的jl_handle_stack_switch .. /lib/julia/libjulia.so(未知行)
/usr/bin/../lib/julia/libjulia.so中的julia_trampoline(未知行)
未知函数(ip:4199613)
__libc_start_main位于/usr/bin/../lib/libc.so.6(未知行)
未知函数(ip:4199667)
未知n函数(ip:0)
zsh:段错误(核心转储)julia

我以错误的方式调用函数?什么是函数的正确名称? (它看起来不仅仅是 eye ,因为这也行不通。)



作为一个另外一个问题:Julia是否会对结果数组的内存方向做任何事情? Fortran和Julia都是列专业的,但我想知道是由于ccall()Julia可能认为它应该转移它们吗?

 模块matrix_routines 
隐式无

私人

公共::眼睛

包含

纯函数眼睛( n,偏移量)结果(um)!{{{
整数,意图(in):: n
整数,意图(in),可选项::偏移

整数, dimension(n,n):: um

integer :: i,l,u,os

um = 0

l = 1
u = n
os = 0

if(present(offset))then
os = offset
end if

if( abs(os)< n)then
if(os> 0)then
u = n - os
else if(os< 0)then
l = 1 - os
结束,如果

d oi = l,u
um(i,i + os)= 1
end do
end if

end function eye!}}}
结束模块matrix_routines


解决方案

将数组直接返回给julia是有问题的,因为除非满足特定的条件,否则Fortran数组不能与C进行互操作。当你让数组可互操作时(向你的过程中添加 bind(C))并给数组一个C类型)编译器( gfortran

 错误:一个数组

为了解决这个问题,我们可以通过一个伪参数返回数组。你需要做一个 intent(inout)参数并在julia中构造数组,以避免在Fortran中创建数组时遇到任何内存/范围问题。



其次,可选参数是有问题的,并跳过Julia文档,我不确定它是否被支持。请注意,即使Fortran没有明确的接口,也可以使用可选参数调用Fortran,因为Julia不会与 .mod 文件进行交互,并且似乎期望C的处理方式,它可能不会工作(并且Fortran 2008 15.3.7 p2.6似乎说它不被支持)。尽管有解决方法 - 您可以使用不同数量的参数创建多个Fortran过程,然后使用它们的可选参数调用过程。首先,考虑一下这个Fortran模块,这个模块从你的例子开始,但是被简化为展示interop所必需的:

$ b b $ b

 模块matrix_routines 
隐式无

私有
public :: eye

包含

纯子程序eye(n,um)bind(C,name =eye)!{{{
use,intrinsic :: iso_c_binding,only:c_int
implicit none
integer(c_int),intent(in):: n
整数(c_int),intent(inout),dimension(n,n):: um

integer :: i ,j

do j = 1,n
do i = 1,n
um(i,j)= i + j
end do
end do

end subroutine eye!}}}
end module matrix_routines

请注意,我将 um 移动为 inout 虚拟参数,并且由于我们没有返回值将程序更改为子程序。我也删除了可选参数。我还使用了C interop类型并为该过程绑定了一个C名称。您可以在您的问题中进行编译。



在Julia中,您现在可以执行以下操作:

 朱莉娅> n = 2 
2

julia> um = zeros(Int32,n,n)
2x2 Array {Int32,2}:
0 0
0 0

julia> ccall((:eye,matrix_routines.so),Void,(Ptr {Int32},Ptr {Array {Int32,2}}),& n,um)

julia> um
2x2 Array {Int32,2}:
2 3
3 4

julia> n = 4
4

julia> um = zeros(Int32,n,n)
4x4 Array {Int32,2}:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

julia> ccall((:eye,matrix_routines.so),Void,(Ptr {Int32},Ptr {Array {Int32,2}}),& n,um)

julia> um
4x4 Array {Int32,2}:
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8

请注意,我们可以将函数调用为:eye ,因为我们在我们的Fortran中使用 bind(C,name =eye) C interop。

最后,如果我将Fortran示例中的do循环更改为 um(i,j)= i * 10 + j ,我们可以看到数组中没有换位:

  julia> um 
3x3 Array {Int32,2}:
11 12 13
21 22 23
31 32 33






您的segfault的特殊原因可能是许多因素 - 数据类型不匹配,返回类型问题,问题可选参数或实际调用和变量传递不匹配。


I want to call functions in my Fortran library from Julia. In this case, I have a function eye that takes an Integer, and returns a two-dimensional array of integers.

The Fortran module is compiled into a shared library using

$ gfortran -shared -fPIC -o matrix_routines.so matrix_routines.f90

And thereafter I am attempting to call it from the interactive Julia interpreter like that (name obtained from nm):

julia> n=5
5

julia> ccall( (:__matrix_routines_MOD_eye, "/path/to/library/matrix_routines.so"), Array{Int64,2} , (Ptr{Int64},), &n )

This, however, immediately results in Julia throwing a segfault at me:

signal (11): Segmentation fault
__matrix_routines_MOD_eye at /path/to/library/matrix_routines.so (unknown line)
anonymous at no file:0
unknown function (ip: -1137818532)
jl_f_top_eval at /usr/bin/../lib/julia/libjulia.so (unknown line)
eval_user_input at REPL.jl:53
jlcall_eval_user_input_19998 at  (unknown line)
jl_apply_generic at /usr/bin/../lib/julia/libjulia.so (unknown line)
anonymous at task.jl:95
jl_handle_stack_switch at /usr/bin/../lib/julia/libjulia.so (unknown line)
julia_trampoline at /usr/bin/../lib/julia/libjulia.so (unknown line)
unknown function (ip: 4199613)
__libc_start_main at /usr/bin/../lib/libc.so.6 (unknown line)
unknown function (ip: 4199667)
unknown function (ip: 0)
zsh: segmentation fault (core dumped)  julia

Am I calling the function the wrong way? What is the correct name of the function? (It doesn't appear to be just eye, as that doesn't work either.)

As an additional question: does Julia do anything with the memory-orientation of the resulting arrays? Fortran and Julia are both column-major, but I wonder if due to ccall() Julia might think it should tranpose them?

module matrix_routines
    implicit none

    private

    public :: eye

    contains

        pure function eye(n,offset) result(um) !{{{
            integer, intent(in) :: n
            integer, intent(in), optional :: offset

            integer, dimension(n,n) :: um

            integer :: i, l, u, os

            um = 0

            l = 1
            u = n
            os = 0

            if (present(offset)) then
                os = offset
            end if

            if (abs(os) < n) then
                if (os > 0) then
                    u = n - os
                else if (os < 0) then
                    l = 1 - os
                end if

                do i=l, u
                    um(i, i+os) = 1
                end do
            end if

        end function eye !}}}
end module matrix_routines

解决方案

There a couple issues with your approach. Returning an array directly to julia is problematic because Fortran arrays are not interoperable with C unless specific conditions are met. When you make the array interoperable (add bind(C) to your procedure and give the array a C type) the compiler (gfortran) will complain:

Error: Return type of BIND(C) function 'um' at (1) cannot be an array

To get around this we can return the array through a dummy argument. You'll want to make this an intent(inout) argument and construct the array in julia to avoid any memory/scope issues with creating the array in Fortran.

Secondly, the optional argument is problematic and skimming the Julia docs I'm not sure it is even supported. Note that not even Fortran can call Fortran with optional arguments without an explicit interface and as Julia doesn't interact with the .mod file and seems to expect the C way of doing things, it probably won't work (and Fortran 2008 15.3.7 p2.6 seems to say it isn't supported). There are workarounds though -- you can create multiple Fortran procedures with varying numbers of arguments and then call the procedure with optional arguments from them.

First, consider this Fortran module, which started with your example but is pared down to just what is necessary to demonstrate the interop:

module matrix_routines
  implicit none

  private
  public :: eye

contains

  pure subroutine eye(n,um) bind(C,name="eye") !{{{
    use, intrinsic :: iso_c_binding, only: c_int
    implicit none
    integer(c_int), intent(in) :: n
    integer(c_int), intent(inout), dimension(n,n) :: um

    integer :: i, j

    do j=1,n
       do i=1,n
          um(i,j) = i+j
       end do
    end do

  end subroutine eye !}}}
end module matrix_routines

Note that I've moved um to being an inout dummy argument and since we're not returning a value changed the procedure to a subroutine. I have also removed the optional argument. I have also used C interop types and bound a C name to the procedure. You can compile this as in your question.

In Julia you can now do the following:

julia> n = 2
2

julia> um = zeros(Int32, n, n)
2x2 Array{Int32,2}:
 0  0
 0  0

julia> ccall((:eye, "matrix_routines.so"), Void, (Ptr{Int32}, Ptr{Array{Int32,2}}), &n, um)

julia> um
2x2 Array{Int32,2}:
 2  3
 3  4

julia> n = 4
4

julia> um = zeros(Int32, n, n)
4x4 Array{Int32,2}:
 0  0  0  0
 0  0  0  0
 0  0  0  0
 0  0  0  0

julia> ccall((:eye, "matrix_routines.so"), Void, (Ptr{Int32}, Ptr{Array{Int32,2}}), &n, um)

julia> um
4x4 Array{Int32,2}:
 2  3  4  5
 3  4  5  6
 4  5  6  7
 5  6  7  8

Note that we can call the function as just :eye since we used the bind(C,name="eye") C interop in our Fortran.

And lastly, if we change the do loop in my Fortran example to be um(i,j) = i*10+j, we can see that no transposition happens in the array:

julia> um
3x3 Array{Int32,2}:
 11  12  13
 21  22  23
 31  32  33


The particular reason for your segfault could have been a number of things -- mismatched data types, issues with the return type, issues with the optional argument or mismatch in the actual call and variables passed.

这篇关于从Julia调用Fortran函数,返回一个数组:未知函数,segfault?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆