Как наследовать от регулярной матрицы - PullRequest
1 голос
/ 20 апреля 2019
using ShiftedArrays

struct CircularMatrix{T} <: AbstractArray{T,2}
    data::Array{T,2}
    view::CircShiftedArray
    currentIndex::Int
    function CircularMatrix{T}(dims...) where T
        data = zeros(T, dims...)
        CircularMatrix(data, ShiftedArrays.circshift(data, (0, -1)), 1)
    end
end

Base.size(M::CircularMatrix) = size(M.data)
Base.eltype(::Type{CircularMatrix{T}}) where {T} = T

function shift_forward!(M::CircularMatrix)
    M.shift_forward!(1)
end

function shift_forward!(M::CircularMatrix, n)
    # replace the view with a view shifted forwards.
    M.currentIndex += n
    M.view = ShiftedArrays.circshift(M.data, (n, M.currentIndex))
end

@inline Base.@propagate_inbounds function Base.getindex(M::CircularMatrix, i) = M.view[i]
@inline Base.@propagate_inbounds function Base.setindex!(M::CircularMatrix, data, i) = M.view[i] = data

Как заставить CircularMatrix работать как обычная матрица.Так что я могу получить к нему доступ как

m = CircularMatrix{Int}(4,4)
m[1, 1] = 5
x = view(m, 1, :)

1 Ответ

5 голосов
/ 21 апреля 2019

Ваш тип матрицы определен как подтип AbstractArray{T, 2}.Вам нужно реализовать несколько методов в неофициальном интерфейсе массива Julia для вашего типа, чтобы сделать функции и функции, работающие на AbstractArray{T, 2}, также работать с вашим пользовательским типом, то есть сделать ваш CircularMatrix итеративным, индексируемым,полностью функционирующая матрица.

Методы реализации:

  1. size(M::CircularMatrix)
  2. getindex(M::CircularMatrix, i::Int)
  3. getindex(M::CircularMatrix, I::Vararg{Int, N})
  4. setindex!(M::CircularMatrix, v, i::Int)
  5. setindex!(M::CircularMatrix, v, I::Vararg{Int, N})

Вы уже реализовали 1, 2 и 4, но еще не установили свой стиль индексации.Вам может не понадобиться 3 и 5, если вы выберете стиль линейного индексирования .Вам нужно только установить IndexStyle равным IndexLinear() и, возможно, несколько модификаций, тогда все должно работать только для вашей матрицы.

1.size(M::CircularMatrix)

Первый - size.size(A::CircularMatrix) возвращает Tuple размеров A.Я считаю, что для вашей матрицы, вероятно, что-то вроде следующего

Base.size(M::CircularMatrix) = size(M.data)

2getindex(M::CircularMatrix, i::Int)

Этот метод необходим, если вы выберете стиль линейного индексирования .getindex(M, i::Int) должно дать вам значение по линейному индексу i.Вы уже реализовали это в своем коде.Если вы выбираете линейное индексирование, вам нужно установить IndexStyle для вашего типа, а затем просто пропустить 3 и 5. Джулия автоматически преобразует несколько обращений к индексу, например, a[3, 5], в доступ к линейному индексу.

Base.IndexStyle(::Type{<:CircularMatrix}) = IndexLinear()

Base.@propogate_inbounds function Base.getindex(M::CircularMatrix, i::Int)
    @boundscheck checkbounds(M, i)
    @inbounds M.view[i]
end

Возможно, лучше использовать @inbounds здесь во второй строке.Если вызывающая сторона не использует @inbounds, мы сначала проверяем границы, и это , надеюсь, делает последующую проверку границ ненужной.Возможно, вы захотите пропустить это во время разработки.

3.getindex(M::CircularMatrix, I::Vararg{Int, N})

Третий - для декартового стиля индексации.Если вы выбираете этот стиль, вам нужно реализовать этот метод.Vararg{Int, N} в подписи означает "ровно N Int аргументов".Здесь N должно быть равно размерности CircularMatrix.Поскольку это матрица, N должно быть два.Если вы выбираете этот стиль, вам нужно определить что-то вроде следующего

Base.@propogate_inbounds function Base.getindex(A::CircularMatrix, I::Vararg{Int, 2})
    @boundscheck checkbounds(A, I...)
    @inbounds A.view[# convert I[1]` and `I[2]` to a linear index in `view`]
end

или поскольку ваша размерность не параметрическая, а матрица двумерная, просто

 Base.@propogate_inbounds function Base.getindex(A::CircularMatrix, i::Int, j::Int)
    @boundscheck checkbounds(A, i, j)
    @inbounds A.view[# convert i` and `j` to a linear index in `view`]
end

4.setindex!(M::CircularMatrix, v, i::Int)

Четвертый похож на второй.Этот метод должен установить значение на линейный индекс i, если вы выбираете стиль линейного индексирования.

5.setindex!(M::CircularMatrix, v, I::Vararg{Int, N})

Пятый должен быть похож на третий, если вы выбираете декартовой стиль индексации.


После реализаций для 1, 2 и 4 и установки IndexStyle, у вас должен быть собственный тип матрицы, который просто работает.

m[1, 1] = 5
x = view(m, 1, :)

for e in 
  ...
end

for i in eachindex(m)
  ...
end

display(m)
println(m)
length(m)
ndims(m)
map(f, A)
....

Все это должно работать.

Несколько заметок

  • Документация по интерфейсу Abstract Arrays здесь с несколькими примерами.Вы также можете увидеть Необязательные методы для реализации.

  • На GitHub существует организация JuliaArray , которая предоставляет множество полезных реализаций пользовательских массивов, включая StaticArrays, OffsetArrays и т. Д., А также организация JuliaMatrices , предоставляющая пользовательские типы матриц.Возможно, вы захотите взглянуть на их реализации.

  • @inline является избыточным, если вы используете Base.@propogate_inbounds.

@ spreadate_inbounds

Сообщает компилятору встроенную функцию, сохраняя при этом контекст входящих вызовов.

  • Вам не нужно определять eltype дляваша матрица, так как уже есть определение для AbstractArray{T, N}, которое возвращает T.
...