I'm still a beginner with Julia, so apologies for the OOP terminology.
Let X
and Y
be two user-defined matrices with an element type of MyType
. Let I
be the identity matrix. MyType
, in this case, happens to be an object with a single attribute and all the operations are done through this attribute.
I'm trying to test if ((X-I)')^-1
is approximately Y
. I want to do this by defining as few functions as possible. But I also don't want MyType
to inherit from anything.
I would expect that I would only need to define:
one
andzero
for the identity matrix. The elements ofI
should know that they are of typeMyType
.+(x::MyType, y::MyType)
,-
,*
,/
for matrix multiplication and inversion.adjoint
for determining(X-I)'
.isapprox
to give≈
some kind of meaning.
However, in practice, I need far more than these. Is there any way that I can reduce the number of implemented methods for this?
Here is what I have so far. It took me some time to finally stop getting MethodError
s.
# Define MyType
struct MyType # Don't inherit from anything
value
end
value(x::MyType) = x.value
# Define its methods
Base.one(::Type{MyType}) = MyType(1)
Base.zero(::Type{MyType}) = MyType(0)
Base.:+(x::MyType, y::MyType) = MyType(value(x)+value(y))
Base.:*(x::MyType, y::MyType) = MyType(value(x)*value(y))
Base.:-(x::MyType, y::MyType) = MyType(value(x)-value(y))
Base.:/(x::MyType, y::MyType) = MyType(value(x)/value(y))
Base.adjoint(x::MyType) = MyType(adjoint(value(x)))
function Base.isapprox(x::MyType, y::MyType; atol, rtol)
return isapprox(value(x), value(y), atol=atol, rtol=rtol)
end
function Base.rtoldefault(::Type{MyType}, ::Type{MyType}, z)
# I don't really want to be restricted to Float64 tolerance.
return Base.rtoldefault(Float64, Float64, z)
end
# Shouldn't have to define these
Base.one(::MyType) = one(MyType)
Base.zero(::MyType) = zero(MyType)
Base.:+(x::MyType, y) = MyType(value(x)+y)
Base.:*(x::MyType, y) = MyType(value(x)*y)
Base.abs(x::MyType) = MyType(abs(value(x)))
Base.:<(x::MyType, y::MyType) = value(x) < value(y)
Base.inv(x::MyType) = MyType(inv(value(x)))
Base.promote_rule(::Type{Any}, ::Type{MyType}) = MyType
Base.convert(::Type{MyType}, x::MyType) = x
Base.convert(::Type{MyType}, x) = MyType(x)
Base.iterate(x::MyType) = (value(x), nothing)
Base.iterate(::MyType, ::Any) = nothing
Base.length(x::MyType) = 1
# Begin check for ((X-I)')^-1 ≈ Y
X = [
0.4 1;
-0.2 0.8;
]
X = map(x -> MyType(x), X)
Y = [
-0.625 0.625;
-3.125 -1.875;
]
Y = map(x -> MyType(x), Y)
using LinearAlgebra
println((X-I)')
println(inv((X-I)'))
println(inv((X-I)') ≈ Y)
I know I can use macros to make this easier. But this is not the point of the exercise.
I'm sure I've made many stupid mistakes particularly with promote_rule
and iterate
. I've tried reading the documentation multiple times, so I must be pretty stupid.
Any help much appreciated.