Форраноподобные массивы, такие как FArray (Float64, -1:1, -7: 7, -128: 512) в Julia

Как правило, наличие 1-основанного массива для Julia является хорошим решением, но иногда желательно иметь Fortran-подобный массив с индексами, которые охватывают некоторые поддиапазоны ℤ:

julia> x = FArray(Float64, -1:1,-7:7,-128:512)

где это было бы полезно:

в кодах, сопровождающих книгу Численное решение гиперболических дифференциальных уравнений с частными производными проф. Джон А. Трангенштейн эти отрицательные индексы интенсивно используются для призрачных клеток для граничных условий. То же самое относится и к Clawpack (означает "пакет законов о сохранении" ) проф. Randall J. LeVeque http://depts.washington.edu/clawpack/, и есть много других кодов, где такие индексы были бы естественными. Поэтому такой вспомогательный класс был бы полезен для быстрого перевода таких кодов.

Я только начал реализовывать такой вспомогательный тип, но поскольку я совершенно новый для Julia, вам будет очень благодарна ваша помощь.

Я начал с:

type FArray
    ranges
    array::Array
    function FArray(T, r::Range1{Int}...)
        dims = map((x) -> length(x), r)
        array = Array(T, dims)
        new(r, array)
    end
end

Вывод:

julia> include ("FortranArray.jl")
julia> x = FArray(Float64, -1:1,-7:7,-128:512)
FArray((-1:1,-7:7,-128:512),3x15x641 Array{Float64,3}:
[:, :, 1] =
6.90321e-310  2.6821e-316   1.96042e-316  0.0  0.0  0.0  9.84474e-317  …  1.83233e-316  2.63285e-316  0.0           9.61618e-317  0.0        
6.90321e-310  6.32404e-322  2.63285e-316  0.0  0.0  0.0  2.63292e-316     2.67975e-316
...
[:, :, 2] =
...

Поскольку я совершенно новичок в Джулии, мне бы очень понравились любые рекомендации, которые особенно важны для повышения эффективности.

[изменить]

Тема обсуждалась здесь:

https://groups.google.com/forum/#!topic/julia-dev/NOF6MA6tb9Y

В ходе обсуждения были разработаны два способа создания массивов Юлии с произвольной базой: На основе SubArray используется выборка со вспомогательной функцией:

function farray(T, r::Range1{Int64}...)
    dims = map((x) -> length(x), r)
    array = Array(T, dims)
    sub_indices = map((x) -> -minimum(x) + 2 : maximum(x), r)
    sub(array, sub_indices)
end

julia> y[-1,-7,-128] = 777
777

julia> y[-1,-7,-128] + 33
810.0

julia> y[-2,-7,-128]
ERROR: BoundsError()
 in getindex at subarray.jl:200

julia> y[2,-7,-128]
2.3977385e-316

Обратите внимание, что ограничения не проверяются полностью. Подробности здесь: https://github.com/JuliaLang/julia/issues/4044

В настоящее время SubArray имеет проблемы с производительностью, но в конечном итоге его производительность может быть улучшена, см. также:

https://github.com/JuliaLang/julia/issues/5117

https://github.com/JuliaLang/julia/issues/3496

Другой подход, который имеет лучшую производительность на данный момент, помимо проверок обеих границ:

type FArray{T<:Number, N, A<:AbstractArray} <: AbstractArray

    ranges
    offsets::NTuple{N,Int}
    array::A

    function FArray(r::Range1{Int}...)
        dims = map((x) -> length(x), r)
        array = Array(T, dims)
        offs = map((x) -> 1 - minimum(x), r)
        new(r, offs, array)
    end
end

FArray(T, r::Range1{Int}...) = FArray{T, length(r,), Array{T, length(r,)}}(r...)

getindex{T<:Number}(FA::FArray{T}, i1::Int) = FA.array[i1+FA.offsets[1]]
getindex{T<:Number}(FA::FArray{T}, i1::Int, i2::Int) = FA.array[i1+FA.offsets[1], i2+FA.offsets[2]]
getindex{T<:Number}(FA::FArray{T}, i1::Int, i2::Int, i3::Int) = FA.array[i1+FA.offsets[1], i2+FA.offsets[2], i3+FA.offsets[3]]

setindex!{T}(FA::FArray{T}, x, i1::Int) = arrayset(FA.array, convert(T,x), i1+FA.offsets[1])
setindex!{T}(FA::FArray{T}, x, i1::Int, i2::Int) = arrayset(FA.array, convert(T,x), i1+FA.offsets[1], i2+FA.offsets[2])
setindex!{T}(FA::FArray{T}, x, i1::Int, i2::Int, i3::Int) = arrayset(FA.array, convert(T,x), i1+FA.offsets[1], i2+FA.offsets[2], i3+FA.offsets[3])

getindex и setindex! методы для FArray были вдохновлены кодом base/array.jl.

Варианты использования:

julia> y = FArray(Float64, -1:1,-7:7,-128:512);

julia> y[-1,-7,-128] = 777
777

julia> y[-1,-7,-128] + 33
810.0

julia> y[-1,2,3]
0.0

julia> y[-2,-7,-128]
ERROR: BoundsError()
 in getindex at FortranArray.jl:27

julia> y[2,-7,-128]
ERROR: BoundsError()
 in getindex at FortranArray.jl:27

Ответ 1

В настоящее время существует два пакета, которые обеспечивают такую ​​функциональность. Для массивов с произвольными начальными индексами см. https://github.com/alsam/OffsetArrays.jl. Для большей гибкости см. https://github.com/mbauman/AxisArrays.jl, где индексы не должны быть целыми числами.