3
votes

I'm struggling to understand how exactly modules can be extended in Julia. Specifically, I'd like to create my own LinearAlgebra matrix whose parent class is AbstractMatrix{T} and implement its functionality similar to how the Diagonal or UpperTriangular matrices are implemented in the actual LA package. If I could literally add my matrix to the original package, then I would, but for now I am content creating my own MyLinearAlgebra package that simply imports the original and extends it. Here's what I've got so far in MyLinearAlgebra.jl:

module MyLinearAlgebra

import LinearAlgebra
import Base: getindex, setindex!, size

export
# Types
LocalMatrix,
SolutionVector,
# Functions
issymmetric,
isdiag
# Operators
# Constants

include("SolutionVector.jl")
include("LocalMatrix.jl")

end

Focusing solely on LocalMatrix.jl now, I have:

"""
    struct LocalMatrix{T} <: AbstractMatrix{T}
Block diagonal structure for local matrix. `A[:,:,s,iK]` is a block matrix for
state s and element iK
"""
struct LocalMatrix{T} <: AbstractMatrix{T}
    data::Array{T,4}

    function LocalMatrix{T}(data) where {T}
        new{T}(data)
    end
end
[... implement size, getindex, setindex! ... all working perfectly]

"""
    issymmetric(A::LocalMatrix)
Tests whether a LocalMatrix is symmetric
"""
function issymmetric(A::LocalMatrix)
    println("my issymmetric")
    all(LinearAlgebra.issymmetric, [@view A.data[:,:,i,j] for i=1:size(A.data,3), j=1:size(A.data,4)])
end

"""
    isdiag(A::LocalMatrix)
Tests whether a LocalMatrix is diagonal
"""
function isdiag(A::LocalMatrix)
    println("my isdiag")
    all(LinearAlgebra.isdiag, [@view A.data[:,:,i,j] for i=1:size(A.data,3), j=1:size(A.data,4)])
end

When I try and run this however, I get

error in method definition: function LinearAlgebra.isdiag must be explicitly imported to be extended

OK not a problem, I can change the definition to function LinearAlgebra.isdiag() instead and it works. But if I also change the definition of the other function to function LinearAlgebra.issymmetric() and run a simple test I now get the error

ERROR: MethodError: no method matching issymmetric(::MyLinearAlgebra.LocalMatrix{Float64})

So I'm stumped. Obviously I have a workaround that lets me continue working for now, but I must be simply misunderstanding how Julia modules work because I can't seem to distinguish between the two functions. Why does one needs to be explicitly extended? Why can the other not? What even is the difference between them in this situation? What is the correct way here to extend a package's module? Thanks for any help.

1

1 Answers

2
votes

You need to explicitly state that you are adding new methods to existing functions so it should be:

function LinearAlgebra.issymmetric(A::LocalMatrix)
...
end

function LinearAlgebra.isdiag(A::LocalMatrix)
...
end

The reason that you are getting the error most likely is because you forgot to import LinearAlgebra in the code that is testing your package.

Note that your constructor also should be corrected:

struct LocalMatrix{T} <: AbstractMatrix{T}
    data::Array{T,4}
    function LocalMatrix(data::Array{T,4}) where {T}
        new{T}(data)
    end
end

With the current constructor you need to write LocalMatrix{Float64}(some_arr) instead of simply LocalMatrix(some_arr). Even worse, if you provide your constructor with a 3-d array you will get a type conversion error, while when you used the syntax that I am proposing one gets no method matching LocalMatrix(::Array{Int64,3}) which is much more readable for users of your library.