如何从规则矩阵继承

时间:2019-04-20 19:45:26

标签: julia

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 个答案:

答案 0 :(得分:5)

您的矩阵类型被定义为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,但尚未设置索引样式。如果选择linear indexing style,则可能不需要3和5。您只需要将IndexStyle设置为IndexLinear()并可能需要进行一些修改,然后一切都应适用于您的矩阵。

1。 size(M::CircularMatrix)

第一个是sizesize(A::CircularMatrix)返回尺寸为Tuple的{​​{1}}。我相信您的矩阵可能类似于以下内容

A

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

如果选择线性索引样式,则需要此方法。 getindex(M::CircularMatrix, i::Int)应该为您提供线性索引getindex(M, i::Int)处的值。您已经在代码中实现了它。如果选择线性索引,则需要为类型设置i,然后只需跳过3和5。Julia将自动转换多个索引访问,例如IndexStyle,以访问线性索引。

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,我们将首先检查边界,并且此希望使后续边界检查不再必要。不过,您可能需要在开发过程中忽略此设置。

3。 @inbounds

第三个用于笛卡尔索引风格。如果选择此样式,则需要实现此方法。签名中的getindex(M::CircularMatrix, I::Vararg{Int, N})代表“恰好是Vararg{Int, N} N个参数”。这里的Int应该等于N的维数。由于这是一个矩阵,因此N应该为2。如果选择此样式,则需要定义如下内容

CircularMatrix

或者因为您的尺寸不是参数化的,而矩阵是2D的,那么

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

4。 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

第四个类似于第二个。如果您选择线性索引样式,则此方法应将值设置为线性索引setindex!(M::CircularMatrix, v, i::Int)

5。 i

如果选择笛卡尔索引风格,则第五个应该与第三个相似。


在实现1、2和4并设置setindex!(M::CircularMatrix, v, I::Vararg{Int, N})之后,您应该拥有一个可以正常使用的自定义矩阵类型。

IndexStyle

这些都应该起作用。

一些笔记

  • 有一个有关抽象数组接口here的文档,并带有一些示例。您还可以看到可选方法来实现。

  • GitHub上有一个JuliaArray组织,提供了许多有用的自定义数组实现,包括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) .... StaticArrays等,还有一个JuliaMatrices组织提供自定义矩阵类型。您可能想看看它们的实现。

  • 如果您使用OffsetArrays,则
  • @inline是多余的。

  

@propagate_inbounds

     

告诉编译器内联一个函数,同时保留调用方的     入站上下文。

  • 您不需要为矩阵定义Base.@propogate_inbounds,因为已经有eltype的定义返回AbstractArray{T, N}