From 5d047a87cbb5443d60b9ccdfe41e241b1d343403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Tue, 5 Nov 2024 16:41:46 -0500 Subject: [PATCH 01/16] define labelledunitrangedual --- .../src/lib/GradedAxes/src/GradedAxes.jl | 1 + .../src/lib/GradedAxes/src/gradedunitrange.jl | 2 +- .../lib/GradedAxes/src/gradedunitrangedual.jl | 8 +++- .../GradedAxes/src/labelledunitrangedual.jl | 38 +++++++++++++++++++ .../src/lib/GradedAxes/test/test_dual.jl | 6 +-- 5 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl diff --git a/NDTensors/src/lib/GradedAxes/src/GradedAxes.jl b/NDTensors/src/lib/GradedAxes/src/GradedAxes.jl index 7edd09bf84..ba17c175f0 100644 --- a/NDTensors/src/lib/GradedAxes/src/GradedAxes.jl +++ b/NDTensors/src/lib/GradedAxes/src/GradedAxes.jl @@ -2,6 +2,7 @@ module GradedAxes include("blockedunitrange.jl") include("gradedunitrange.jl") include("dual.jl") +include("labelledunitrangedual.jl") include("gradedunitrangedual.jl") include("onetoone.jl") include("fusion.jl") diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl index 0bd35707a7..110c31baaf 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl @@ -250,7 +250,7 @@ function blockedunitrange_getindices( # if they exist. This makes it so that # `only(axes(a[indices])) isa `GradedUnitRange` # if `a isa `GradedUnitRange`, for example. - return mortar(blocks, length.(blocks)) + return mortar(blocks, length.(blocks)) # LOOSE DUAL end # The block labels of the corresponding slice. diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 217d4b401f..5f41c676ef 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -31,11 +31,15 @@ function blockedunitrange_getindices(a::GradedUnitRangeDual, indices::Integer) end function blockedunitrange_getindices(a::GradedUnitRangeDual, indices::Block{1}) - return label_dual(getindex(nondual(a), indices)) + return dual(getindex(nondual(a), indices)) end function blockedunitrange_getindices(a::GradedUnitRangeDual, indices::BlockRange) - return label_dual(getindex(nondual(a), indices)) + return dual(getindex(nondual(a), indices)) +end + +function blockedunitrange_getindices(a::GradedUnitRangeDual, indices::BlockIndexRange) + return dual(nondual(a)[indices]) end # fix ambiguity diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl new file mode 100644 index 0000000000..256cd3f77d --- /dev/null +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -0,0 +1,38 @@ +# LabelledUnitRangeDual is obtained by slicing a GradedUnitRangeDual with a block + +using ..LabelledNumbers: LabelledNumbers, label, labelled, unlabel + +struct LabelledUnitRangeDual{T,NondualUnitRange<:AbstractUnitRange{T}} <: + AbstractUnitRange{T} + nondual_unitrange::NondualUnitRange +end + +dual(a::LabelledUnitRange) = LabelledUnitRangeDual(a) +nondual(a::LabelledUnitRangeDual) = a.nondual_unitrange +dual(a::LabelledUnitRangeDual) = nondual(a) +flip(a::LabelledUnitRangeDual) = dual(flip(nondual(a))) +isdual(::LabelledUnitRangeDual) = true + +LabelledNumbers.label(a::LabelledUnitRangeDual) = dual(label(nondual(a))) +LabelledNumbers.unlabel(a::LabelledUnitRangeDual) = unlabel(nondual(a)) + +for f in [:first, :getindex, :last, :length, :step] + @eval Base.$f(a::LabelledUnitRangeDual, args...) = + labelled($f(unlabel(a), args...), label(a)) +end + +# fix ambiguities +Base.getindex(a::LabelledUnitRangeDual, i::Integer) = dual(nondual(a)[i]) + +function Base.show(io::IO, ::MIME"text/plain", a::LabelledUnitRangeDual) + println(io, typeof(a)) + return print(io, label(a), " => ", unlabel(a)) +end + +function Base.show(io::IO, a::LabelledUnitRangeDual) + return print(io, nameof(typeof(a)), " ", label(a), " => ", unlabel(a)) +end + +function Base.AbstractUnitRange{T}(a::LabelledUnitRangeDual) where {T} + return AbstractUnitRange{T}(nondual(a)) +end diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index 18dbac045c..1b9072bbf5 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -124,13 +124,13 @@ end @test blockmergesortperm(a) == [Block(1), Block(2)] @test blockmergesortperm(ad) == [Block(1), Block(2)] - @test_broken isdual(ad[Block(1)]) - @test_broken isdual(ad[Block(1)[1:1]]) + @test isdual(ad[Block(1)]) + @test isdual(ad[Block(1)[1:1]]) I = mortar([Block(2)[1:1]]) g = ad[I] @test length(g) == 1 @test label(first(g)) == U1(-1) - @test_broken isdual(g[Block(1)]) + @test isdual(g[Block(1)]) end end From c60c080a458540ba844c44b257ec00d38b9fad75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 6 Nov 2024 15:51:24 -0500 Subject: [PATCH 02/16] fix dual when slicing with Vector --- .../test/runtests.jl | 18 ++++----- .../lib/GradedAxes/src/gradedunitrangedual.jl | 38 +++++++++++++++++-- .../src/lib/GradedAxes/test/test_dual.jl | 3 ++ 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/NDTensors/src/lib/BlockSparseArrays/ext/BlockSparseArraysGradedAxesExt/test/runtests.jl b/NDTensors/src/lib/BlockSparseArrays/ext/BlockSparseArraysGradedAxesExt/test/runtests.jl index 53c883bf54..41f7fc3478 100644 --- a/NDTensors/src/lib/BlockSparseArrays/ext/BlockSparseArraysGradedAxesExt/test/runtests.jl +++ b/NDTensors/src/lib/BlockSparseArrays/ext/BlockSparseArraysGradedAxesExt/test/runtests.jl @@ -1,6 +1,6 @@ @eval module $(gensym()) using Compat: Returns -using Test: @test, @testset, @test_broken +using Test: @test, @testset using BlockArrays: AbstractBlockArray, Block, BlockedOneTo, blockedrange, blocklengths, blocksize using NDTensors.BlockSparseArrays: BlockSparseArray, block_nstored @@ -217,10 +217,10 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test size(a[I, I]) == (1, 1) @test isdual(axes(a[I, :], 2)) @test isdual(axes(a[:, I], 1)) - @test_broken isdual(axes(a[I, :], 1)) - @test_broken isdual(axes(a[:, I], 2)) - @test_broken isdual(axes(a[I, I], 1)) - @test_broken isdual(axes(a[I, I], 2)) + @test isdual(axes(a[I, :], 1)) + @test isdual(axes(a[:, I], 2)) + @test isdual(axes(a[I, I], 1)) + @test isdual(axes(a[I, I], 2)) end @testset "dual GradedUnitRange" begin @@ -243,10 +243,10 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test size(a[I, I]) == (1, 1) @test isdual(axes(a[I, :], 2)) @test isdual(axes(a[:, I], 1)) - @test_broken isdual(axes(a[I, :], 1)) - @test_broken isdual(axes(a[:, I], 2)) - @test_broken isdual(axes(a[I, I], 1)) - @test_broken isdual(axes(a[I, I], 2)) + @test isdual(axes(a[I, :], 1)) + @test isdual(axes(a[:, I], 2)) + @test isdual(axes(a[I, I], 1)) + @test isdual(axes(a[I, I], 2)) end @testset "dual BlockedUnitRange" begin # self dual diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 5f41c676ef..07f9977bdd 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -59,14 +59,46 @@ function gradedunitrangedual_getindices_blocks(a::GradedUnitRangeDual, indices) end # TODO: Move this to a `BlockArraysExtensions` library. -function blockedunitrange_getindices(a::GradedUnitRangeDual, indices::Vector{<:Block{1}}) +function blockedunitrange_getindices( + a::GradedUnitRangeDual, indices::Vector{<:BlockIndexRange{1}} +) return gradedunitrangedual_getindices_blocks(a, indices) end function blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::Vector{<:BlockIndexRange{1}} + a::GradedUnitRangeDual, + indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, ) - return gradedunitrangedual_getindices_blocks(a, indices) + arr = mortar(map(b -> a[b], blocks(indices))) + # GradedOneTo appears in mortar + # flip arr axis to preserve dual information + # axes(arr) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) + # TODO way to create BlockArray with specified axis without relying on internal? + block_axes = (flip(only(axes(arr))),) + flipped = BlockArrays._BlockArray(vec.(blocks(arr)), block_axes) + return flipped +end + +function blockedunitrange_getindices( + a::GradedUnitRangeDual, indices::AbstractVector{<:Union{Block{1},BlockIndexRange{1}}} +) + # Without converting `indices` to `Vector`, + # mapping `indices` outputs a `BlockVector` + # which is harder to reason about. + vblocks = map(index -> a[index], Vector(indices)) + # We pass `length.(blocks)` to `mortar` in order + # to pass block labels to the axes of the output, + # if they exist. This makes it so that + # `only(axes(a[indices])) isa `GradedUnitRange` + # if `a isa `GradedUnitRange`, for example. + + arr = mortar(vblocks, length.(vblocks)) + # GradedOneTo appears in mortar + # axes(arr) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) + # TODO way to create BlockArray with specified axis without relying on internal? + block_axes = (flip(only(axes(arr))),) + flipped = BlockArrays._BlockArray(vec.(blocks(arr)), block_axes) + return flipped end Base.axes(a::GradedUnitRangeDual) = axes(nondual(a)) diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index 1b9072bbf5..60ba8cd584 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -131,6 +131,9 @@ end @test length(g) == 1 @test label(first(g)) == U1(-1) @test isdual(g[Block(1)]) + + @test isdual(axes(ad[[Block(1)]], 1)) # used in view(::BlockSparseVector, [Block(1)]) + @test isdual(axes(ad[mortar([Block(1)[1:1]])], 1)) # used in view(::BlockSparseVector, [Block(1)[1:1]]) end end From 0216abc1289876ab5ef0261a27b301c9083dc64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 6 Nov 2024 15:53:46 -0500 Subject: [PATCH 03/16] avoid gradedunitrangedual_getindices_blocks --- NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 07f9977bdd..91ae9bf264 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -53,16 +53,12 @@ function BlockArrays.blocklengths(a::GradedUnitRangeDual) return dual.(blocklengths(nondual(a))) end -function gradedunitrangedual_getindices_blocks(a::GradedUnitRangeDual, indices) - a_indices = getindex(nondual(a), indices) - return mortar([label_dual(b) for b in blocks(a_indices)]) -end - # TODO: Move this to a `BlockArraysExtensions` library. function blockedunitrange_getindices( a::GradedUnitRangeDual, indices::Vector{<:BlockIndexRange{1}} ) - return gradedunitrangedual_getindices_blocks(a, indices) + a_indices = getindex(nondual(a), indices) + return mortar([label_dual(b) for b in blocks(a_indices)]) end function blockedunitrange_getindices( From d0c02bdafdb7107f57234aa2246ff339a8e8443d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 6 Nov 2024 15:58:36 -0500 Subject: [PATCH 04/16] define flip_blockvector --- .../lib/GradedAxes/src/gradedunitrangedual.jl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 91ae9bf264..994adacd31 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -65,14 +65,11 @@ function blockedunitrange_getindices( a::GradedUnitRangeDual, indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, ) - arr = mortar(map(b -> a[b], blocks(indices))) + v = mortar(map(b -> a[b], blocks(indices))) # GradedOneTo appears in mortar # flip arr axis to preserve dual information # axes(arr) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) - # TODO way to create BlockArray with specified axis without relying on internal? - block_axes = (flip(only(axes(arr))),) - flipped = BlockArrays._BlockArray(vec.(blocks(arr)), block_axes) - return flipped + return flip_blockvector(v) end function blockedunitrange_getindices( @@ -88,12 +85,16 @@ function blockedunitrange_getindices( # `only(axes(a[indices])) isa `GradedUnitRange` # if `a isa `GradedUnitRange`, for example. - arr = mortar(vblocks, length.(vblocks)) + v = mortar(vblocks, length.(vblocks)) # GradedOneTo appears in mortar # axes(arr) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) + return flip_blockvector(v) +end + +function flip_blockvector(v::BlockVector) # TODO way to create BlockArray with specified axis without relying on internal? - block_axes = (flip(only(axes(arr))),) - flipped = BlockArrays._BlockArray(vec.(blocks(arr)), block_axes) + block_axes = flip.(axes(v)) + flipped = BlockArrays._BlockArray(vec.(blocks(v)), block_axes) return flipped end From 8e988c074830a693c1809eb81d20cd4edafb715e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 6 Nov 2024 16:04:36 -0500 Subject: [PATCH 05/16] fix comments --- NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl | 2 +- NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl index 110c31baaf..0bd35707a7 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl @@ -250,7 +250,7 @@ function blockedunitrange_getindices( # if they exist. This makes it so that # `only(axes(a[indices])) isa `GradedUnitRange` # if `a isa `GradedUnitRange`, for example. - return mortar(blocks, length.(blocks)) # LOOSE DUAL + return mortar(blocks, length.(blocks)) end # The block labels of the corresponding slice. diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 994adacd31..87cb529565 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -67,8 +67,8 @@ function blockedunitrange_getindices( ) v = mortar(map(b -> a[b], blocks(indices))) # GradedOneTo appears in mortar - # flip arr axis to preserve dual information - # axes(arr) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) + # flip v axis to preserve dual information + # axes(v) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) return flip_blockvector(v) end @@ -87,7 +87,8 @@ function blockedunitrange_getindices( v = mortar(vblocks, length.(vblocks)) # GradedOneTo appears in mortar - # axes(arr) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) + # flip v axis to preserve dual information + # axes(v) will appear in axes(view(::BlockSparseArray, [Block(1)])) return flip_blockvector(v) end From 9b64ab2fe7d398d67bd11d8019f9ccf98c988b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 6 Nov 2024 17:31:52 -0500 Subject: [PATCH 06/16] add tests for LabelledUnitRangeDual --- .../src/lib/GradedAxes/test/test_dual.jl | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index 60ba8cd584..8ebf75a520 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -17,6 +17,7 @@ using NDTensors.GradedAxes: AbstractGradedUnitRange, GradedAxes, GradedUnitRangeDual, + LabelledUnitRangeDual, OneToOne, blocklabels, blockmergesortperm, @@ -27,7 +28,8 @@ using NDTensors.GradedAxes: gradedrange, isdual, nondual -using NDTensors.LabelledNumbers: LabelledInteger, label, labelled, labelled_isequal +using NDTensors.LabelledNumbers: + LabelledInteger, LabelledUnitRange, label, labelled, labelled_isequal, unlabel using Test: @test, @test_broken, @testset struct U1 n::Int @@ -58,6 +60,24 @@ Base.isless(c1::U1, c2::U1) = c1.n < c2.n @test blockisequal(ad, a) end +@testset "LabelledUnitRangeDual" begin + la = labelled(1:2, U1(1)) + @test la isa LabelledUnitRange + @test label(la) == U1(1) + @test unlabel(la) == 1:2 + @test la == 1:2 + @test !isdual(la) + + lad = dual(la) + @test lad isa LabelledUnitRangeDual + @test label(lad) == U1(-1) + @test unlabel(lad) == 1:2 + @test lad == 1:2 + @test isdual(lad) + @test nondual(lad) === la + @test dual(lad) === la +end + @testset "GradedUnitRangeDual" begin for a in [gradedrange([U1(0) => 2, U1(1) => 3]), gradedrange([U1(0) => 2, U1(1) => 3])[1:5]] @@ -126,6 +146,11 @@ end @test isdual(ad[Block(1)]) @test isdual(ad[Block(1)[1:1]]) + @test ad[Block(1)] isa LabelledUnitRangeDual + @test ad[Block(1)[1:1]] isa LabelledUnitRangeDual + @test label(ad[Block(2)]) == U1(-1) + @test label(ad[Block(2)[1:1]]) == U1(-1) + I = mortar([Block(2)[1:1]]) g = ad[I] @test length(g) == 1 From 8f358d2863cd9c6d04c8225809d661f3c6039fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 10:36:31 -0500 Subject: [PATCH 07/16] use mortar --- NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 87cb529565..4eab524f17 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -93,9 +93,8 @@ function blockedunitrange_getindices( end function flip_blockvector(v::BlockVector) - # TODO way to create BlockArray with specified axis without relying on internal? block_axes = flip.(axes(v)) - flipped = BlockArrays._BlockArray(vec.(blocks(v)), block_axes) + flipped = mortar(vec.(blocks(v)), block_axes) return flipped end From ee32b83262f25e58317f5a15d9b6752e6dc0155d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:10:28 -0500 Subject: [PATCH 08/16] similar show for LabelledUnitRange and LabelledUnitRangeDual --- NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl index 256cd3f77d..98923733ab 100644 --- a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -24,12 +24,14 @@ end # fix ambiguities Base.getindex(a::LabelledUnitRangeDual, i::Integer) = dual(nondual(a)[i]) -function Base.show(io::IO, ::MIME"text/plain", a::LabelledUnitRangeDual) +function Base.show( + io::IO, ::MIME"text/plain", a::Union{LabelledUnitRange,LabelledUnitRangeDual} +) println(io, typeof(a)) return print(io, label(a), " => ", unlabel(a)) end -function Base.show(io::IO, a::LabelledUnitRangeDual) +function Base.show(io::IO, a::Union{LabelledUnitRange,LabelledUnitRangeDual}) return print(io, nameof(typeof(a)), " ", label(a), " => ", unlabel(a)) end From 584448131256411eba8a9ac64df38c44d95e86a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:13:55 -0500 Subject: [PATCH 09/16] default self dual for Any --- NDTensors/src/lib/GradedAxes/src/dual.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/dual.jl b/NDTensors/src/lib/GradedAxes/src/dual.jl index ca985e30a0..edb9006bc7 100644 --- a/NDTensors/src/lib/GradedAxes/src/dual.jl +++ b/NDTensors/src/lib/GradedAxes/src/dual.jl @@ -1,5 +1,5 @@ -# default behavior: self-dual -dual(r::AbstractUnitRange) = r +# default behavior: any object is self-dual +dual(x) = x nondual(r::AbstractUnitRange) = r isdual(::AbstractUnitRange) = false From 64cbffb434100612376d94691c284dbff6534788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:14:12 -0500 Subject: [PATCH 10/16] keep show(LabelledUnitRange) inside LabelledNumbers --- .../src/lib/GradedAxes/src/labelledunitrangedual.jl | 6 ++---- .../src/lib/LabelledNumbers/src/labelledunitrange.jl | 9 +++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl index 98923733ab..256cd3f77d 100644 --- a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -24,14 +24,12 @@ end # fix ambiguities Base.getindex(a::LabelledUnitRangeDual, i::Integer) = dual(nondual(a)[i]) -function Base.show( - io::IO, ::MIME"text/plain", a::Union{LabelledUnitRange,LabelledUnitRangeDual} -) +function Base.show(io::IO, ::MIME"text/plain", a::LabelledUnitRangeDual) println(io, typeof(a)) return print(io, label(a), " => ", unlabel(a)) end -function Base.show(io::IO, a::Union{LabelledUnitRange,LabelledUnitRangeDual}) +function Base.show(io::IO, a::LabelledUnitRangeDual) return print(io, nameof(typeof(a)), " ", label(a), " => ", unlabel(a)) end diff --git a/NDTensors/src/lib/LabelledNumbers/src/labelledunitrange.jl b/NDTensors/src/lib/LabelledNumbers/src/labelledunitrange.jl index 03965f62f5..4f432c9226 100644 --- a/NDTensors/src/lib/LabelledNumbers/src/labelledunitrange.jl +++ b/NDTensors/src/lib/LabelledNumbers/src/labelledunitrange.jl @@ -52,3 +52,12 @@ function Base.iterate(a::LabelledUnitRange, i) next = convert(eltype(a), labelled(i + step(a), label(a))) return (next, next) end + +function Base.show(io::IO, ::MIME"text/plain", a::LabelledUnitRange) + println(io, typeof(a)) + return print(io, label(a), " => ", unlabel(a)) +end + +function Base.show(io::IO, a::LabelledUnitRange) + return print(io, nameof(typeof(a)), " ", label(a), " => ", unlabel(a)) +end From 027465d85678c4e249ce717e048f11ce688b8a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:24:23 -0500 Subject: [PATCH 11/16] define blocklabels(::LabelledUnitRange) --- .../src/lib/GradedAxes/src/gradedunitrange.jl | 1 + .../lib/GradedAxes/src/labelledunitrangedual.jl | 1 + NDTensors/src/lib/GradedAxes/test/test_dual.jl | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl index 0bd35707a7..9197a99fe8 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl @@ -68,6 +68,7 @@ end # == is just a range comparison that ignores labels. Need dedicated function to check equality. struct NoLabel end blocklabels(r::AbstractUnitRange) = Fill(NoLabel(), blocklength(r)) +blocklabels(la::LabelledUnitRange) = label(la) function LabelledNumbers.labelled_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) return blockisequal(a1, a2) && (blocklabels(a1) == blocklabels(a2)) diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl index 256cd3f77d..0dd13ddb5a 100644 --- a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -12,6 +12,7 @@ nondual(a::LabelledUnitRangeDual) = a.nondual_unitrange dual(a::LabelledUnitRangeDual) = nondual(a) flip(a::LabelledUnitRangeDual) = dual(flip(nondual(a))) isdual(::LabelledUnitRangeDual) = true +blocklabels(la::LabelledUnitRangeDual) = label(la) LabelledNumbers.label(a::LabelledUnitRangeDual) = dual(label(nondual(a))) LabelledNumbers.unlabel(a::LabelledUnitRangeDual) = unlabel(nondual(a)) diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index 8ebf75a520..17c03c8edd 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -73,6 +73,21 @@ end @test label(lad) == U1(-1) @test unlabel(lad) == 1:2 @test lad == 1:2 + @test !labelled_isequal(la, lad) + @test !space_isequal(la, lad) + @test isdual(lad) + @test nondual(lad) === la + @test dual(lad) === la + + # check default behavior for objects without dual + la = labelled(1:2, 'x') + lad = dual(la) + @test lad isa LabelledUnitRangeDual + @test label(lad) == 'x' + @test unlabel(lad) == 1:2 + @test lad == 1:2 + @test labelled_isequal(la, lad) + @test !space_isequal(la, lad) @test isdual(lad) @test nondual(lad) === la @test dual(lad) === la From b66c22f66300786bf2ba2afcd40bd9a398daa6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:32:47 -0500 Subject: [PATCH 12/16] add tests --- NDTensors/src/lib/GradedAxes/test/test_dual.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index 17c03c8edd..8fdc811905 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -64,15 +64,21 @@ end la = labelled(1:2, U1(1)) @test la isa LabelledUnitRange @test label(la) == U1(1) + @test blocklabels(la) == U1(1) @test unlabel(la) == 1:2 @test la == 1:2 @test !isdual(la) + @test labelled_isequal(la, la) + @test space_isequal(la, la) lad = dual(la) @test lad isa LabelledUnitRangeDual @test label(lad) == U1(-1) + @test blocklabels(lad) == U1(-1) @test unlabel(lad) == 1:2 @test lad == 1:2 + @test labelled_isequal(lad, lad) + @test space_isequal(lad, lad) @test !labelled_isequal(la, lad) @test !space_isequal(la, lad) @test isdual(lad) @@ -84,8 +90,11 @@ end lad = dual(la) @test lad isa LabelledUnitRangeDual @test label(lad) == 'x' + @test blocklabels(la) == 'x' @test unlabel(lad) == 1:2 @test lad == 1:2 + @test labelled_isequal(lad, lad) + @test space_isequal(lad, lad) @test labelled_isequal(la, lad) @test !space_isequal(la, lad) @test isdual(lad) From e44876aebbf912bf53e47b1a8ec76a013ce8785b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:41:54 -0500 Subject: [PATCH 13/16] blocklabels to return Vector --- NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl | 2 +- NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl | 2 +- NDTensors/src/lib/GradedAxes/test/test_dual.jl | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl index 9197a99fe8..76eaf42692 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrange.jl @@ -68,7 +68,7 @@ end # == is just a range comparison that ignores labels. Need dedicated function to check equality. struct NoLabel end blocklabels(r::AbstractUnitRange) = Fill(NoLabel(), blocklength(r)) -blocklabels(la::LabelledUnitRange) = label(la) +blocklabels(la::LabelledUnitRange) = [label(la)] function LabelledNumbers.labelled_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) return blockisequal(a1, a2) && (blocklabels(a1) == blocklabels(a2)) diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl index 0dd13ddb5a..0c0679a01f 100644 --- a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -12,7 +12,7 @@ nondual(a::LabelledUnitRangeDual) = a.nondual_unitrange dual(a::LabelledUnitRangeDual) = nondual(a) flip(a::LabelledUnitRangeDual) = dual(flip(nondual(a))) isdual(::LabelledUnitRangeDual) = true -blocklabels(la::LabelledUnitRangeDual) = label(la) +blocklabels(la::LabelledUnitRangeDual) = [label(la)] LabelledNumbers.label(a::LabelledUnitRangeDual) = dual(label(nondual(a))) LabelledNumbers.unlabel(a::LabelledUnitRangeDual) = unlabel(nondual(a)) diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index 8fdc811905..f9506a8fa0 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -64,7 +64,7 @@ end la = labelled(1:2, U1(1)) @test la isa LabelledUnitRange @test label(la) == U1(1) - @test blocklabels(la) == U1(1) + @test blocklabels(la) == [U1(1)] @test unlabel(la) == 1:2 @test la == 1:2 @test !isdual(la) @@ -74,7 +74,7 @@ end lad = dual(la) @test lad isa LabelledUnitRangeDual @test label(lad) == U1(-1) - @test blocklabels(lad) == U1(-1) + @test blocklabels(lad) == [U1(-1)] @test unlabel(lad) == 1:2 @test lad == 1:2 @test labelled_isequal(lad, lad) @@ -90,7 +90,7 @@ end lad = dual(la) @test lad isa LabelledUnitRangeDual @test label(lad) == 'x' - @test blocklabels(la) == 'x' + @test blocklabels(lad) == ['x'] @test unlabel(lad) == 1:2 @test lad == 1:2 @test labelled_isequal(lad, lad) From 17d457d4fb83b2a0afa7e8debdad39a1cb2c5d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 12:53:16 -0500 Subject: [PATCH 14/16] stay consistent in blockedunitrange_getindices --- NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl index 4eab524f17..97c8a96d71 100644 --- a/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/gradedunitrangedual.jl @@ -58,7 +58,9 @@ function blockedunitrange_getindices( a::GradedUnitRangeDual, indices::Vector{<:BlockIndexRange{1}} ) a_indices = getindex(nondual(a), indices) - return mortar([label_dual(b) for b in blocks(a_indices)]) + v = mortar(dual.(blocks(a_indices))) + # flip v to stay consistent with other cases where axes(v) are used + return flip_blockvector(v) end function blockedunitrange_getindices( From 0d51b197a75d2a83f1a007d25110c3337b8f66e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 14:40:00 -0500 Subject: [PATCH 15/16] fix flip --- NDTensors/src/lib/GradedAxes/src/dual.jl | 1 + .../GradedAxes/src/labelledunitrangedual.jl | 3 +- .../src/lib/GradedAxes/test/test_dual.jl | 29 ++++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/NDTensors/src/lib/GradedAxes/src/dual.jl b/NDTensors/src/lib/GradedAxes/src/dual.jl index edb9006bc7..877ba1a857 100644 --- a/NDTensors/src/lib/GradedAxes/src/dual.jl +++ b/NDTensors/src/lib/GradedAxes/src/dual.jl @@ -11,4 +11,5 @@ label_dual(x) = label_dual(LabelledStyle(x), x) label_dual(::NotLabelled, x) = x label_dual(::IsLabelled, x) = labelled(unlabel(x), dual(label(x))) +flip(a::AbstractUnitRange) = dual(label_dual(a)) flip(g::AbstractGradedUnitRange) = dual(gradedrange(label_dual.(blocklengths(g)))) diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl index 0c0679a01f..37a257ce94 100644 --- a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -10,12 +10,13 @@ end dual(a::LabelledUnitRange) = LabelledUnitRangeDual(a) nondual(a::LabelledUnitRangeDual) = a.nondual_unitrange dual(a::LabelledUnitRangeDual) = nondual(a) -flip(a::LabelledUnitRangeDual) = dual(flip(nondual(a))) +label_dual(::IsLabelled, a::LabelledUnitRangeDual) = dual(label_dual(nondual(a))) isdual(::LabelledUnitRangeDual) = true blocklabels(la::LabelledUnitRangeDual) = [label(la)] LabelledNumbers.label(a::LabelledUnitRangeDual) = dual(label(nondual(a))) LabelledNumbers.unlabel(a::LabelledUnitRangeDual) = unlabel(nondual(a)) +LabelledNumbers.LabelledStyle(::LabelledUnitRangeDual) = IsLabelled() for f in [:first, :getindex, :last, :length, :step] @eval Base.$f(a::LabelledUnitRangeDual, args...) = diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index f9506a8fa0..c4f67c51a9 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -29,7 +29,7 @@ using NDTensors.GradedAxes: isdual, nondual using NDTensors.LabelledNumbers: - LabelledInteger, LabelledUnitRange, label, labelled, labelled_isequal, unlabel + LabelledInteger, LabelledUnitRange, label, label_type, labelled, labelled_isequal, unlabel using Test: @test, @test_broken, @testset struct U1 n::Int @@ -70,6 +70,7 @@ end @test !isdual(la) @test labelled_isequal(la, la) @test space_isequal(la, la) + @test label_type(la) == U1 lad = dual(la) @test lad isa LabelledUnitRangeDual @@ -84,6 +85,22 @@ end @test isdual(lad) @test nondual(lad) === la @test dual(lad) === la + @test label_type(lad) == U1 + + laf = flip(la) + @test laf isa LabelledUnitRangeDual + @test label(laf) == U1(1) + @test unlabel(laf) == 1:2 + + ladf = flip(dual(la)) + @test ladf isa LabelledUnitRange + @test label(ladf) == U1(-1) + @test unlabel(ladf) == 1:2 + + lafd = dual(flip(la)) + @test lafd isa LabelledUnitRange + @test label(lafd) == U1(-1) + @test unlabel(lafd) == 1:2 # check default behavior for objects without dual la = labelled(1:2, 'x') @@ -100,6 +117,16 @@ end @test isdual(lad) @test nondual(lad) === la @test dual(lad) === la + + laf = flip(la) + @test laf isa LabelledUnitRangeDual + @test label(laf) == 'x' + @test unlabel(laf) == 1:2 + + ladf = flip(lad) + @test ladf isa LabelledUnitRange + @test label(ladf) == 'x' + @test unlabel(ladf) == 1:2 end @testset "GradedUnitRangeDual" begin From f6911eea7735e9ecbc39da8b21fc0ad64cea8bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 7 Nov 2024 15:37:13 -0500 Subject: [PATCH 16/16] fix getindex and iterate --- .../lib/GradedAxes/src/labelledunitrangedual.jl | 9 +++++++++ NDTensors/src/lib/GradedAxes/test/test_dual.jl | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl index 37a257ce94..466d64945b 100644 --- a/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl +++ b/NDTensors/src/lib/GradedAxes/src/labelledunitrangedual.jl @@ -25,6 +25,15 @@ end # fix ambiguities Base.getindex(a::LabelledUnitRangeDual, i::Integer) = dual(nondual(a)[i]) +function Base.getindex(a::LabelledUnitRangeDual, indices::AbstractUnitRange{<:Integer}) + return dual(nondual(a)[indices]) +end + +function Base.iterate(a::LabelledUnitRangeDual, i) + i == last(a) && return nothing + next = convert(eltype(a), labelled(i + step(a), label(a))) + return (next, next) +end function Base.show(io::IO, ::MIME"text/plain", a::LabelledUnitRangeDual) println(io, typeof(a)) diff --git a/NDTensors/src/lib/GradedAxes/test/test_dual.jl b/NDTensors/src/lib/GradedAxes/test/test_dual.jl index c4f67c51a9..98b8838542 100644 --- a/NDTensors/src/lib/GradedAxes/test/test_dual.jl +++ b/NDTensors/src/lib/GradedAxes/test/test_dual.jl @@ -72,6 +72,11 @@ end @test space_isequal(la, la) @test label_type(la) == U1 + @test iterate(la) == (1, 1) + @test iterate(la) == (1, 1) + @test iterate(la, 1) == (2, 2) + @test isnothing(iterate(la, 2)) + lad = dual(la) @test lad isa LabelledUnitRangeDual @test label(lad) == U1(-1) @@ -87,10 +92,22 @@ end @test dual(lad) === la @test label_type(lad) == U1 + @test iterate(lad) == (1, 1) + @test iterate(lad) == (1, 1) + @test iterate(lad, 1) == (2, 2) + @test isnothing(iterate(lad, 2)) + + lad2 = lad[1:1] + @test lad2 isa LabelledUnitRangeDual + @test label(lad2) == U1(-1) + @test unlabel(lad2) == 1:1 + laf = flip(la) @test laf isa LabelledUnitRangeDual @test label(laf) == U1(1) @test unlabel(laf) == 1:2 + @test labelled_isequal(la, laf) + @test !space_isequal(la, laf) ladf = flip(dual(la)) @test ladf isa LabelledUnitRange