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

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

问题描述

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

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.

Fortran 模块被编译成一个共享库使用

The Fortran module is compiled into a shared library using

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

然后我试图从交互式 Julia 解释器中调用它(名称从 nm 获得):

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 )

然而,这会立即导致 Julia 向我抛出一个段错误:

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

我是否以错误的方式调用该函数?函数的正确名称是什么?(它似乎不只是 eye,因为那也不起作用.)

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.)

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

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

推荐答案

您的方法存在一些问题.将数组直接返回给 julia 是有问题的,因为除非满足特定条件,否则 Fortran 数组不能与 C 互操作.当您使数组可互操作时(将 bind(C) 添加到您的过程并给数组一个 C 类型),编译器 (gfortran) 会报错:

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

为了解决这个问题,我们可以通过一个虚拟参数返回数组.您需要将此作为 intent(inout) 参数并在 julia 中构造数组,以避免在 Fortran 中创建数组时出现任何内存/范围问题.

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.

其次,可选参数有问题,浏览 Julia 文档我不确定它是否受支持.请注意,即使是 Fortran 也不能在没有显式接口的情况下使用可选参数调用 Fortran,并且由于 Julia 不与 .mod 文件交互,并且似乎期望 C 的做事方式,它可能不会工作(和 Fortran 2008 15.3.7 p2.6 似乎说它不受支持).但是有一些解决方法——您可以创建多个具有不同数量参数的 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.

首先,考虑这个 Fortran 模块,它从您的示例开始,但被缩减为​​演示互操作所需的内容:

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

请注意,我已将 um 移动为 inout 虚拟参数,并且由于我们没有返回值,因此将过程更改为子例程.我还删除了可选参数.我还使用了 C 互操作类型并将 C 名称绑定到该过程.您可以按照您的问题进行编译.

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.

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

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

请注意,我们可以将函数调用为 :eye,因为我们在 Fortran 中使用了 bind(C,name="eye") C 互操作.

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

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

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 函数,返回一个数组:未知函数,段错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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