-
Notifications
You must be signed in to change notification settings - Fork 42
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
IndexMap #65
IndexMap #65
Conversation
Codecov Report
@@ Coverage Diff @@
## master #65 +/- ##
=========================================
- Coverage 98.62% 90.5% -8.12%
=========================================
Files 8 9 +1
Lines 435 474 +39
=========================================
Hits 429 429
- Misses 6 45 +39
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #65 +/- ##
==========================================
+ Coverage 97.67% 97.79% +0.12%
==========================================
Files 8 9 +1
Lines 473 545 +72
==========================================
+ Hits 462 533 +71
- Misses 11 12 +1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, in principle. What I like is the implicit permutation of indices, that doesn't require writing a permutation factor in a CompositeMap
. On the downside, I think we overlooked one major drawback of this approach in #64. The surrounding zero blocks don't come without cost. You have to set all entries to zero first, before you do any meaningful computation on the (potentially much) smaller block. Also, in the linear combination of IndexMap
s, you would add entire vectors, as opposed to adding only the relevant part. This could be probably worked around partially by writing a specific 5-arg mul!
method. That one, however, is not employed in the LinearCombination
A_mul_B!
, and there are non-trivial obstacles to switching to it. For those reasons, without significant rework of those other parts of the code, I don't think this LinearCombination
-of-IndexMap
s-approach can currently replace the BlockMap
(and similarly an anticipated BlockDiagonalMap
) implementation, unfortunately.
src/indexmap.jl
Outdated
""" | ||
struct IndexMap{T, As <: LinearMap, Rs <: AbstractVector{Int}, | ||
Cs <: AbstractVector{Int}} <: LinearMap{T} | ||
map::As |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any indention throughout should be 4 spaces, not aligned with stuff from the previous line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, then how does one make it clear to the reader that this is a continuation line rather than a new line of code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in this case, this is a new line of code, isn't it? You have the (split) line for the struct
, and then the new line for its field map
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, perhaps I misunderstood the previous comment.
Perhaps the issue is that I'm using a 4-space tab which looks ok in my editor but different in github where it defaults to an 8-space tab.
I had/have the same concern about the zeros. I have some ideas but it will take time. |
My first attempt was to try to have only the first of a set of blocks zero out the output vector but that did not work for multiple reasons, including the fact that the addition order seems not to be preserved by the current implementation of |
This is just code saving, and calls the explicit method above, since order of addition is usually not expected to matter. If you need the final order of addition (whatever it comes out to be), you could define a special |
Thanks for the tips. I might wait for the dust to settle on the 5-arg mul-branch. |
Actually, you would only need to define a special 5-arg |
Hi @dkarrasch, I'm back to working on |
What do you mean? It's probably easy to overlook, but we now have this and this Note that 5-arg mul! is available only in Julia v1.3. |
Oops, I did overlook that because it is not executed in v1.2. |
Hi @dkarrasch, I know you have been busy with |
Yes, currently it's more about grading, proposal writing, paper reviewing and paper revisions, and less |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, so here's a bunch of comments (sorry for the mess!). As for the linear combination, you could use the current FreeMap
multiplication code:
function A_mul_B!(y::AbstractVector, A::LinearCombination{T,As}, x::AbstractVector) where {T, As<:Tuple{Vararg{IndexMap}}}
# no size checking, will be done by individual maps
A_mul_B!(y, A.maps[1], x) # this fills with zeros, and applies first map
for n in 2:length(A.maps)
mul!(y, A.maps[n], x, true, true) # these act only on the respective indices and adds the result
end
return y
end
And for the 5-arg mul!
, a slight modification of the one I wrote in #72:
function mul!(y::AbstractVector, A::LinearCombination{T,As}, x::AbstractVector, α::Number=true, β::Number=false) where {T, As<:Tuple{Vararg{IndexMap}}}
require_one_based_indexing(y, x)
m, n = size(A)
@boundscheck (m == length(y) && n == length(x)) || throw(DimensionMismatch("A_mul_B!"))
iszero(β) && (A_mul_B!(y, A, x); rmul!(y, α); return y)
if isone(β)
fill!(y, zero(eltype(y)))
else
rmul!(y, β)
end
mapind = 0
@views @inbounds for i in 1:length(A.maps)
mul!(y, A.maps[i], x, α, true)
end
return y
end
At the end of the day, it's pretty much the exact same code running for BlockMap
s or the BlockDiagonalMap
s, except for the additional zero filling (and a few more generation of views, which is negligible).
@@ -0,0 +1,19 @@ | |||
# This file is machine-generated - editing it directly is not advised |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is unnecessary and should be deleted.
@@ -4,6 +4,7 @@ version = "2.5.2" | |||
|
|||
[deps] | |||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" | |||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what exactly you need to set the random seed for. Are your tests sensitive to the actual random matrix that is generated?
|
||
|
||
""" | ||
`check_index(row::AbstractVector{Int}, dims::Dims{2})` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is indented by four spaces, then it is typeset as code even without the ` marks.
Base.:(==)(A::IndexMap, B::IndexMap) = (eltype(A) == eltype(B)) && | ||
(A.map == B.map) && (A.dims == B.dims) && | ||
(A.rows == B.rows) && (A.cols == B.cols) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, but I believe this would be the fallback comparison for custom structs
anyway, like comparing all parameter types and all fields. If this is so, then one could remove this definition.
using LinearMaps: LinearMap | ||
using SparseArrays: sparse, blockdiag | ||
using Random: seed! | ||
using Test: @test, @testset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for the tests, I'd recommend to follow the style in the other test files. I don't think there is need to use
every single command. Most likely, users will simply load these packages as a whole.
x = randn(size(Md,2)) | ||
# Bd = @inferred block_diag(L1, L2, L3, L2, L1) # todo: type instability | ||
Bd = block_diag(L1, L2, L3, L2, L1) | ||
@test Bd isa LinearMaps.LinearCombination |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we would want to hardcode this type. Whatever the type of Bd
is, it should yield the correct y = A*x
, and this is what we test with, e.g., Matrix(Bd)
etc.
@test Matrix(Bh') == Matrix(Bh)' | ||
@test Mh * x ≈ Bh * x | ||
|
||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these tests look like fairly generic tests. I don't think it's worth it to set the random seed, and therefore add another dependency, even if it's a stdlib.
# Md = diag(M1, M2, M3, M2, M1) # unsupported so use sparse: | ||
Md = Matrix(blockdiag(sparse.((M1, M2, M3, M2, M1))...)) | ||
x = randn(size(Md,2)) | ||
# Bd = @inferred block_diag(L1, L2, L3, L2, L1) # todo: type instability |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One would have to take a closer look to identify the cause for type instability. This must be somewhere in the construction of IndexMap
, since this comes first and seems to be type-unstable, according to your comment in the other test file.
fill!(y_in, zero(eltype(y_in))) | ||
A_mul_B!(yv, A, xv) | ||
elseif isone(β) # β = 1 | ||
yv .+= A * xv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be mul!(yv, A, xv, true, true)
, otherwise the right hand side will allocate, even if A
is matrix.
This is a generalization of the
OffsetMap
idea that will address #64 when complete.I have some local tests that pass but it needs more testing, but I thought I'd get comments first.
Eventually this will support block diagonal linear maps and perhaps will simplify hvcat.