Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add elementary matrices constructors #21

Closed
wants to merge 1 commit into from

Conversation

rlschuller
Copy link
Contributor

As defined by Aitchison 1986.

A couple of observations regarding the choices of types:

  1. Use Integer for i and j, following LinearAlgebra.jl conventions
  2. Use Int for the variable inside the structure to avoid (re)allocation problems: it is used in F and G for the method getindex

The documentation was simplified to make it cleaner, but we can put the examples back.

Copy link
Member

@juliohm juliohm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @rlschuller for the updated PR. It is very organized. I think we can do something more clever regarding the dimension of the matrices. Let's use I from LinearAlgebra as an example again:

julia> using LinearAlgebra

julia> I * [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

It works even though we did not specify the dimension (=3). If you read their source you will see that they create a functor object that is dimensionless, and then when users type I(3) this creates a Diagonal instance. We can do something similar for our matrices so that when we use them in complicated expressions we don't need to type d everywhere.

Take a look at equations 4.26 -- 4.30 after the table in the book and imagine these expressions full of d, not very pleasant to read. We will use these equations to convert between the various types of covariance structure.

How to implement something similar to I without reinventing the wheel? Take a look at FillArrays.jl and BlockArrays.jl. For example I would export the functor J = JMatrix() and would write (J::JMatrix{T})(d) where {T} = Fill(one(T), d, d).

After we have this working with general dimension, we can check if the BlockArrays.jl approach introduces performance issues.

src/CoDa.jl Outdated
@@ -26,6 +27,13 @@ export
norm, dot, ⋅,
distance,

# matrices
JColumn,
JMatrix,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why you didn't export J and j as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the conflicting styles - structures should start w/ upper case letters according to the manual.

There're is also the problem of j beeing a common indexation variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any suggestions for j alternatives? Maybe Jc?

Copy link
Contributor Author

@rlschuller rlschuller Sep 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe for Julia this doesn't apply, but the variables I use most frequently in nested for loops are i, j and k. Wouldn't j for the column vector create confusion?

test/matrices.jl Outdated
@test JColumn(d) == ones(d)
@test JMatrix(d) == ones(d, d)
@test F(d) == [I(d) -ones(d)]
@test maximum(abs.(G(D) - I(D) + JMatrix(D)/ D)) < 1e-5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about norm(G(D) - I(D) + J(D) / D, Inf) < 1e-5? The function norm is available in LinearAlgebra.

test/runtests.jl Outdated
@@ -3,6 +3,7 @@ using DataDeps
using RData
using CSV
using Test
import LinearAlgebra: I
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import LinearAlgebra: I
using LinearAlgebra

test/matrices.jl Outdated
Comment on lines 5 to 6
@test JColumn(d) == ones(d)
@test JMatrix(d) == ones(d, d)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rewrite these in terms of the shortcuts J and j after exporting these names.

src/matrices.jl Outdated
Base.IndexStyle(::Type{<:JColumn}) = IndexLinear()
Base.getindex(A::JColumn{T}, i::Integer) where {T} = one(T)


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

src/matrices.jl Outdated
struct FMatrix{T} <: AbstractMatrix{T}
d::Int
end
FMatrix(d::Int) = FMatrix{Int}(d)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that you are not consistent regarding the parameter type. For the Jmatrix you used Bool and here you used Int.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The elements in FMatrix can assume negative values.

The elements in GMatrix are fractional, so Float64 was chosen as the default. According to the Performance Tips section of the manual, the use of Real types should be avoided in containers, but for in my tests Real defaulted to Float64 and performed w/ the same speed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thank you for clarifying it 💯

src/matrices.jl Outdated
end
FMatrix(d::Int) = FMatrix{Int}(d)
F(d::Int) = FMatrix(d)
Base.size(A::FMatrix) = (A.d, A.d+1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Base.size(A::FMatrix) = (A.d, A.d+1)
Base.size(F::FMatrix) = (F.d, F.d + 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unsure about how Julia would handle conflicts of names, if the arguments of the functions always overwrite the other variables w/ that name I agree that using F looks much more readable.

src/matrices.jl Outdated
F(d::Int) = FMatrix(d)
Base.size(A::FMatrix) = (A.d, A.d+1)
Base.IndexStyle(::Type{<:FMatrix}) = IndexCartesian()
Base.getindex(A::FMatrix{T}, i::Integer, j::Integer) where {T} = one(T)*((i==j) - (j==(A.d+1)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Base.getindex(A::FMatrix{T}, i::Integer, j::Integer) where {T} = one(T)*((i==j) - (j==(A.d+1)))
Base.getindex(F::FMatrix{T}, i::Integer, j::Integer) where {T} = one(T)*((i == j) - (j == (F.d + 1)))

src/matrices.jl Outdated
end
HMatrix(d::Int) = HMatrix{Int}(d)
H(d::Int) = HMatrix(d)
Base.size(A::HMatrix) = (A.d, A.d)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Base.size(A::HMatrix) = (A.d, A.d)
Base.size(H::HMatrix) = (H.d, H.d)

src/matrices.jl Outdated
H(d::Int) = HMatrix(d)
Base.size(A::HMatrix) = (A.d, A.d)
Base.IndexStyle(::Type{<:HMatrix}) = IndexCartesian()
Base.getindex(A::HMatrix{T}, i::Integer, j::Integer) where {T} = one(T)*(i==j) + one(T)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Base.getindex(A::HMatrix{T}, i::Integer, j::Integer) where {T} = one(T)*(i==j) + one(T)
Base.getindex(::HMatrix{T}, i::Integer, j::Integer) where {T} = one(T)*(i == j) + one(T)

@rlschuller
Copy link
Contributor Author

I didn't realize that I was guessing the dimensions automatically, that's indeed a great feature.

@juliohm
Copy link
Member

juliohm commented Sep 12, 2021

I didn't realize that I was guessing the dimensions automatically, that's indeed a great feature.

Yes, it is a nice side effect of multiple-dispatch in the language. The product A*x has a behavior that is determined by both arguments, in particular the second argument is what matters. So whenever I is passed as the first argument it is completely ignored. Similar expressions like x'*I*x can be implemented without specifying dimension of I. The beauty of Julia as I mentioned before :)

@codecov-commenter
Copy link

codecov-commenter commented Sep 12, 2021

Codecov Report

Merging #21 (f0092ef) into master (54c3f44) will increase coverage by 2.15%.
The diff coverage is 100.00%.

❗ Current head f0092ef differs from pull request most recent head 4389bfa. Consider uploading reports for the commit 4389bfa to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master      #21      +/-   ##
==========================================
+ Coverage   82.10%   84.25%   +2.15%     
==========================================
  Files           3        4       +1     
  Lines          95      108      +13     
==========================================
+ Hits           78       91      +13     
  Misses         17       17              
Impacted Files Coverage Δ
src/matrices.jl 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 54c3f44...4389bfa. Read the comment docs.

@juliohm
Copy link
Member

juliohm commented Sep 12, 2021 via email

@rlschuller rlschuller force-pushed the rodrigo-task-2 branch 2 times, most recently from f0092ef to 6cb339d Compare September 12, 2021 20:32
As defined by Aitchison 1986.
@juliohm
Copy link
Member

juliohm commented Sep 12, 2021

@rlschuller why you are closing the PRs instead of updating them in place? That way we loose all the suggestions in multiple places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants