Mercurial > repos > public > sbplib_julia
changeset 498:5a600ec40ccc feature/outer_product
Merge in default
author | Jonatan Werpers <jonatan@werpers.com> |
---|---|
date | Thu, 05 Nov 2020 15:27:04 +0100 |
parents | 2dc2eac27f75 (current diff) d8075fb14418 (diff) |
children | 7b550c714f3f |
files | src/LazyTensors/lazy_tensor_operations.jl test/testLazyTensors.jl |
diffstat | 4 files changed, 85 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/TODO.md Thu Nov 05 12:48:30 2020 +0100 +++ b/TODO.md Thu Nov 05 15:27:04 2020 +0100 @@ -13,6 +13,7 @@ - [ ] Fix indexing signatures. We should make sure we are not too specific. For the "inbetween" layers we don't know what type of index is coming so we should use `I...` instead of `I::Vararg{Int,R}` or probably better `I::Vararg{Any,R}` - [ ] Use `@inferred` in a lot of tests. - [ ] Make sure we are setting tolerances in tests in a consistent way + - [ ] Add check for correct domain sizes to lazy tensor operations using SizeMismatch ## Repo - [ ] Add Vidar to the authors list
--- a/src/LazyTensors/lazy_tensor_operations.jl Thu Nov 05 12:48:30 2020 +0100 +++ b/src/LazyTensors/lazy_tensor_operations.jl Thu Nov 05 15:27:04 2020 +0100 @@ -86,12 +86,9 @@ t2::TM2 @inline function TensorMappingComposition(t1::TensorMapping{T,R,K}, t2::TensorMapping{T,K,D}) where {T,R,K,D} - @boundscheck if domain_size(t1) != range_size(t2) - throw(DimensionMismatch("the first argument has domain size $(domain_size(t1)) while the second has range size $(range_size(t2)) ")) - end + @boundscheck check_domain_size(t1, range_size(t2)) return new{T,R,K,D, typeof(t1), typeof(t2)}(t1,t2) end - # Add check for matching sizes as a boundscheck end export TensorMappingComposition @@ -170,6 +167,27 @@ apply(tmi::IdentityMapping{T,D}, v::AbstractArray{T,D}, I::Vararg{Any,D}) where {T,D} = v[I...] apply_transpose(tmi::IdentityMapping{T,D}, v::AbstractArray{T,D}, I::Vararg{Any,D}) where {T,D} = v[I...] +""" +Base.:∘(tm, tmi) +Base.:∘(tmi, tm) + +Composes a `Tensormapping` `tm` with an `IdentityMapping` `tmi`, by returning `tm` +""" +@inline function Base.:∘(tm::TensorMapping{T,R,D}, tmi::IdentityMapping{T,D}) where {T,R,D} + @boundscheck check_domain_size(tm, range_size(tmi)) + return tm +end + +@inline function Base.:∘(tmi::IdentityMapping{T,R}, tm::TensorMapping{T,R,D}) where {T,R,D} + @boundscheck check_domain_size(tmi, range_size(tm)) + return tm +end +# Specialization for the case where tm is an IdentityMapping. Required to resolve ambiguity. +@inline function Base.:∘(tm::IdentityMapping{T,D}, tmi::IdentityMapping{T,D}) where {T,D} + @boundscheck check_domain_size(tm, range_size(tmi)) + return tmi +end + """ InflatedTensorMapping{T,R,D} <: TensorMapping{T,R,D} @@ -203,8 +221,20 @@ The outer product of `before`, `tm` and `after`, where `before` and `after` are `IdentityMapping`s. If one of `before` or `after` is left out, a 0-dimensional `IdentityMapping` is used as the default value. + +If `tm` already is an `InflatedTensorMapping`, `before` and `after` will be extended instead of +creating a nested `InflatedTensorMapping`. """ InflatedTensorMapping(::IdentityMapping, ::TensorMapping, ::IdentityMapping) + +function InflatedTensorMapping(before, itm::InflatedTensorMapping, after) + return InflatedTensorMapping( + IdentityMapping(before.size..., itm.before.size...), + itm.tm, + IdentityMapping(itm.after.size..., after.size...), + ) +end + InflatedTensorMapping(before::IdentityMapping, tm::TensorMapping{T}) where T = InflatedTensorMapping(before,tm,IdentityMapping{T}()) InflatedTensorMapping(tm::TensorMapping{T}, after::IdentityMapping) where T = InflatedTensorMapping(IdentityMapping{T}(),tm,after) # Resolve ambiguity between the two previous methods @@ -280,7 +310,6 @@ flatten_tuple(t::Tuple) = ((flatten_tuple.(t)...)...,) # simplify? flatten_tuple(ts::Vararg) = flatten_tuple(ts) - """ LazyOuterProduct(tms...) @@ -334,3 +363,20 @@ export ⊗ # TBD: Should we implement simplifications for outer products of LazyIdentities other LazyIdentities or Inflated tensormappings? + +function check_domain_size(tm::TensorMapping, sz) + if domain_size(tm) != sz + throw(SizeMismatch(tm,sz)) + end +end + +struct SizeMismatch <: Exception + tm::TensorMapping + sz +end +export SizeMismatch + +function Base.showerror(io::IO, err::SizeMismatch) + print(io, "SizeMismatch: ") + print(io, "domain size $(domain_size(err.tm)) of TensorMapping not matching size $(err.sz)") +end
--- a/src/LazyTensors/tensor_mapping.jl Thu Nov 05 12:48:30 2020 +0100 +++ b/src/LazyTensors/tensor_mapping.jl Thu Nov 05 15:27:04 2020 +0100 @@ -64,4 +64,11 @@ export range_size, domain_size +""" + eltype(::TensorMapping{T}) + +The type of elements the TensorMapping acts on. +""" +Base.eltype(::TensorMapping{T}) where T = T + # TODO: Think about boundschecking!
--- a/test/testLazyTensors.jl Thu Nov 05 12:48:30 2020 +0100 +++ b/test/testLazyTensors.jl Thu Nov 05 15:27:04 2020 +0100 @@ -12,6 +12,8 @@ @test range_dim(DummyMapping{Int,2,3}()) == 2 @test domain_dim(DummyMapping{Int,2,3}()) == 3 @test apply(DummyMapping{Int,2,3}(), zeros(Int, (0,0,0)),(Index{Unknown}(0),Index{Unknown}(0))) == :apply + @test eltype(DummyMapping{Int,2,3}()) == Int + @test eltype(DummyMapping{Float64,2,3}()) == Float64 end @testset "Mapping transpose" begin @@ -177,6 +179,7 @@ @test_throws BoundsError (v1 +̃ v2)[4] v2 = [1., 2, 3, 4] # Test that size of arrays is asserted when not specified inbounds + # TODO: Replace these errors with SizeMismatch @test_throws DimensionMismatch v1 +̃ v2 # Test operations on LazyArray @@ -193,6 +196,7 @@ @test_throws BoundsError (v1 + v2)[4] v2 = [1., 2, 3, 4] # Test that size of arrays is asserted when not specified inbounds + # TODO: Replace these errors with SizeMismatch @test_throws DimensionMismatch v1 + v2 end @@ -226,7 +230,7 @@ @test Ã∘B̃ isa TensorMappingComposition @test range_size(Ã∘B̃) == (2,) @test domain_size(Ã∘B̃) == (4,) - @test_throws DimensionMismatch B̃∘Ã + @test_throws SizeMismatch B̃∘Ã # @test @inbounds B̃∘Ã # Should not error even though dimensions don't match. (Since ]test runs with forced boundschecking this is currently not testable 2020-10-16) @@ -312,6 +316,17 @@ @inferred range_dim(I) @inferred domain_dim(I) + + Ã = rand(4,2) + A = LazyLinearMap(Ã,(1,),(2,)) + I1 = IdentityMapping{Float64}(2) + I2 = IdentityMapping{Float64}(4) + @test A∘I1 == A + @test I2∘A == A + @test I1∘I1 == I1 + @test_throws SizeMismatch I1∘A + @test_throws SizeMismatch A∘I2 + @test_throws SizeMismatch I1∘I2 end @testset "InflatedTensorMapping" begin @@ -381,6 +396,16 @@ @inferred apply(tm,v,Index{Unknown}.((1,2,3,2,2,4))...) @inferred (tm*v)[1,2,3,2,2,4] + @testset "InflatedTensorMapping of InflatedTensorMapping" begin + A = ScalingOperator(2.0,(2,3)) + itm = InflatedTensorMapping(I(3,2), A, I(4)) + @test InflatedTensorMapping(I(4), itm, I(2)) == InflatedTensorMapping(I(4,3,2), A, I(4,2)) + @test InflatedTensorMapping(itm, I(2)) == InflatedTensorMapping(I(3,2), A, I(4,2)) + @test InflatedTensorMapping(I(4), itm) == InflatedTensorMapping(I(4,3,2), A, I(4)) + + @test InflatedTensorMapping(I(2), I(2), I(2)) isa InflatedTensorMapping # The constructor should always return its type. + end + end @testset "slice_tuple" begin