From a1e5a18ab0615b29c25698566d2ef12e28535601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 11 Apr 2025 16:21:07 -0400 Subject: [PATCH 01/33] define sectorunitrange --- src/GradedUnitRanges/GradedUnitRanges.jl | 1 + src/GradedUnitRanges/sectorunitrange.jl | 114 +++++++++++++++++ src/SymmetrySectors/abstractsector.jl | 2 + test/test_gradedunitranges_sectorunitrange.jl | 119 ++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 src/GradedUnitRanges/sectorunitrange.jl create mode 100644 test/test_gradedunitranges_sectorunitrange.jl diff --git a/src/GradedUnitRanges/GradedUnitRanges.jl b/src/GradedUnitRanges/GradedUnitRanges.jl index fa8f0b2..c2363a6 100644 --- a/src/GradedUnitRanges/GradedUnitRanges.jl +++ b/src/GradedUnitRanges/GradedUnitRanges.jl @@ -2,6 +2,7 @@ module GradedUnitRanges export gradedrange +include("sectorunitrange.jl") include("gradedunitrange.jl") include("dual.jl") include("labelledunitrangedual.jl") diff --git a/src/GradedUnitRanges/sectorunitrange.jl b/src/GradedUnitRanges/sectorunitrange.jl new file mode 100644 index 0000000..c72a0a9 --- /dev/null +++ b/src/GradedUnitRanges/sectorunitrange.jl @@ -0,0 +1,114 @@ + +using BlockArrays: BlockArrays + +# This implementation contains the "full range" +# it does not check that such a range is consistent with the sector quantum_dimension +# when sliced directly, the label is dropped +# when sliced between multiplicities with sr[(:,1:1)], it returns another SectorUnitRange +# TBD impose some compatibility constraints between range and quantum_dimension? +struct SectorUnitRange{T,Sector,Range<:AbstractUnitRange{T}} <: AbstractUnitRange{T} + nondual_sector::Sector + full_range::Range + isdual::Bool + + function SectorUnitRange(s, r, b) + return new{eltype(r),typeof(s),typeof(r)}(s, r, b) + end +end + +const SectorOneTo{T,Sector,Range} = SectorUnitRange{T,Sector,Base.OneTo{T}} + +# +# Constructors +# + +# sectorunitrange(SU2(1), 2:5) +sectorunitrange(s, r::AbstractUnitRange, b::Bool=false) = SectorUnitRange(s, r, b) + +# sectorunitrange(SU2(1), 1) +function sectorunitrange(s, m::Integer, b::Bool=false) + return sectorunitrange(s, Base.oneto(m * length(s)), b) +end + +# sectorunitrange(SU2(1) => 1) +function sectorunitrange(p::Pair, b::Bool=false) + return sectorunitrange(first(p), last(p), b) +end + +# +# accessors +# +nondual_sector(sr::SectorUnitRange) = sr.nondual_sector +full_range(sr::SectorUnitRange) = sr.full_range +isdual(sr::SectorUnitRange) = sr.isdual + +# +# Base interface +# +Base.first(sr::SectorUnitRange) = first(full_range(sr)) + +Base.iterate(sr::SectorUnitRange) = iterate(full_range(sr)) +Base.iterate(sr::SectorUnitRange, i::Integer) = iterate(full_range(sr), i) + +Base.length(sr::SectorUnitRange) = length(full_range(sr)) + +Base.last(sr::SectorUnitRange) = last(full_range(sr)) + +# slicing +Base.getindex(sr::SectorUnitRange, i::Integer) = full_range(sr)[i] +function Base.getindex(sr::SectorUnitRange, r::AbstractUnitRange{T}) where {T<:Integer} + return full_range(sr)[r] +end + +# TODO replace (:,x) indexing with kronecker(:, x) +Base.getindex(sr::SectorUnitRange, t::Tuple{Colon,<:Integer}) = sr[(:, last(t):last(t))] +function Base.getindex(sr::SectorUnitRange, t::Tuple{Colon,<:AbstractUnitRange}) + r = last(t) + new_range = + ((first(r) - 1) * length(nondual_sector(sr)) + 1):(last(r) * length(nondual_sector(sr))) + return sectorunitrange(nondual_sector(sr), full_range(sr)[new_range], isdual(sr)) +end + +function Base.show(io::IO, sr::SectorUnitRange) + print(io, nameof(typeof(sr)), " ") + if isdual(sr) + print(io, "dual(", nondual_sector(sr), ")") + else + print(io, nondual_sector(sr)) + end + return print(io, " => ", full_range(sr)) +end + +# +# BlockArrays interface +# + +# +# GradedUnitRanges interface +# +function blocklabels(sr::SectorUnitRange) + return isdual(sr) ? [dual(nondual_sector(sr))] : [nondual_sector(sr)] +end + +# TBD error for non-integer? +sector_mulitplicities(sr::SectorUnitRange) = length(sr) ÷ length(nondual_sector(sr)) + +function dual(sr::SectorUnitRange) + return sectorunitrange(nondual_sector(sr), full_range(sr), !isdual(sr)) +end + +function flip(sr::SectorUnitRange) + return sectorunitrange(dual(nondual_sector(sr)), full_range(sr), !isdual(sr)) +end + +function map_blocklabels(f, sr::SectorUnitRange) + return sectorunitrange(f(nondual_sector(sr)), full_range(sr), isdual(sr)) +end + +sector_type(::Type{<:SectorUnitRange{T,Sector}}) where {T,Sector} = Sector + +function space_isequal(sr1::SectorUnitRange, sr2::SectorUnitRange) + return nondual_sector(sr1) == nondual_sector(sr2) && + isdual(sr1) == isdual(sr2) && + full_range(sr1) == full_range(sr2) +end diff --git a/src/SymmetrySectors/abstractsector.jl b/src/SymmetrySectors/abstractsector.jl index 137398c..a470399 100644 --- a/src/SymmetrySectors/abstractsector.jl +++ b/src/SymmetrySectors/abstractsector.jl @@ -13,6 +13,8 @@ function Base.isless(c1::C, c2::C) where {C<:AbstractSector} return isless(sector_label(c1), sector_label(c2)) end +Base.length(s::AbstractSector) = quantum_dimension(s) + # ================================= Sectors interface ==================================== trivial(x) = trivial(typeof(x)) function trivial(axis_type::Type{<:AbstractUnitRange}) diff --git a/test/test_gradedunitranges_sectorunitrange.jl b/test/test_gradedunitranges_sectorunitrange.jl new file mode 100644 index 0000000..7ed4f8b --- /dev/null +++ b/test/test_gradedunitranges_sectorunitrange.jl @@ -0,0 +1,119 @@ +using Test: @test, @test_throws, @testset + +using BlockArrays: Block, blocklength, blocklengths, blockisequal, blocks + +using GradedArrays.GradedUnitRanges: + SectorUnitRange, + blocklabels, + dual, + flip, + full_range, + isdual, + nondual_sector, + sector_mulitplicities, + sector_type, + sectorunitrange, + space_isequal +using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension + +@testset "SectorUnitRange" begin + sr = sectorunitrange(SU((1, 0)), 2) + @test sr isa SectorUnitRange + + # accessors + @test nondual_sector(sr) == SU((1, 0)) + @test full_range(sr) isa Base.OneTo + @test full_range(sr) == 1:6 + @test !isdual(sr) + + # Base interface + @test first(sr) == 1 + @test last(sr) == 6 + @test length(sr) == 6 + @test firstindex(sr) == 1 + @test lastindex(sr) == 6 + @test eltype(sr) === Int + @test step(sr) == 1 + @test eachindex(sr) == Base.oneto(6) + @test only(axes(sr)) isa Base.OneTo + @test only(axes(sr)) == 1:6 + + @test sr == 1:6 + @test sr == sr + @test space_isequal(sr, sr) + + sr = sectorunitrange(SU((1, 0)) => 2) + @test sr isa SectorUnitRange + @test nondual_sector(sr) == SU((1, 0)) + @test full_range(sr) isa Base.OneTo + @test full_range(sr) == 1:6 + @test !isdual(sr) + + sr = sectorunitrange(SU((1, 0)) => 2, true) + @test sr isa SectorUnitRange + @test nondual_sector(sr) == SU((1, 0)) + @test full_range(sr) isa Base.OneTo + @test full_range(sr) == 1:6 + @test isdual(sr) + + sr = sectorunitrange(SU((1, 0)), 4:10, true) + @test sr isa SectorUnitRange + @test nondual_sector(sr) == SU((1, 0)) + @test full_range(sr) isa UnitRange + @test full_range(sr) == 4:10 + @test isdual(sr) + + sr = sectorunitrange(SU((1, 0)), 2) + @test !space_isequal(sr, sectorunitrange(SU((1, 1)), 2)) + @test !space_isequal(sr, sectorunitrange(SU((1, 0)), 2:7)) + @test !space_isequal(sr, sectorunitrange(SU((1, 1)), 2, true)) + + sr2 = copy(sr) + @test sr2 isa SectorUnitRange + @test space_isequal(sr, sr2) + sr3 = deepcopy(sr) + @test sr3 isa SectorUnitRange + @test space_isequal(sr, sr3) + + # BlockArrays interface + @test blockaxes(sr) isa Tuple{BlockRange{1,<:Tuple{Base.OneTo}}} + @test space_isequal(sr[Block(1)], sr) + @test only(blocklasts(sr)) == 6 + @test findblock(sr, 2) == Block(1) + + @test blocklength(sr) == 1 + @test blocklengths(sr) == [6] + @test only(blocks(sr)) == 1:6 + @test blockisequal(sr, sr) + + # GradedUnitRanges interface + @test sector_type(sr) === SU{3,2} + @test sector_type(typeof(sr)) === SU{3,2} + @test blocklabels(sr) == [SU((1, 0))] + @test sector_mulitplicities(sr) == [2] + + srd = dual(sr) + @test nondual_sector(srd) == SU((1, 0)) + @test space_isequal(srd, sectorunitrange(SU((1, 0)), 2, true)) + + srf = flip(sr) + @test nondual_sector(srf) == SU((1, 1)) + @test space_isequal(srf, sectorunitrange(SU((1, 1)), 2, true)) + + # getindex + @test_throws BoundsError sr[0] + @test_throws BoundsError sr[7] + for i in 1:6 + @test sr[i] == i + end + @test sr[2:3] == 2:3 + @test sr[Block(1)] === sr + @test_throws BlockBoundsError sr[Block(2)] + + sr2 = sr[(:, 2)] + @test sr2 isa SectorUnitRange + @test space_isequal(sr2, sectorunitrange(SU((1, 0)), 4:6)) + sr3 = sr[(:, 1:2)] + @test sr3 isa SectorUnitRange + @test space_isequal(sr3, sectorunitrange(SU((1, 0)), 1:6)) +end From 56f310700b081f4a691590f7d598ac9f2c00b48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Mon, 14 Apr 2025 11:53:25 -0400 Subject: [PATCH 02/33] basics of GradedUnitRange --- src/GradedUnitRanges/GradedUnitRanges.jl | 2 - src/GradedUnitRanges/dual.jl | 8 -- src/GradedUnitRanges/fusion.jl | 6 +- src/GradedUnitRanges/gradedunitrange.jl | 175 +++++++++-------------- src/GradedUnitRanges/sectorunitrange.jl | 12 +- 5 files changed, 78 insertions(+), 125 deletions(-) diff --git a/src/GradedUnitRanges/GradedUnitRanges.jl b/src/GradedUnitRanges/GradedUnitRanges.jl index c2363a6..ec857ba 100644 --- a/src/GradedUnitRanges/GradedUnitRanges.jl +++ b/src/GradedUnitRanges/GradedUnitRanges.jl @@ -5,8 +5,6 @@ export gradedrange include("sectorunitrange.jl") include("gradedunitrange.jl") include("dual.jl") -include("labelledunitrangedual.jl") -include("gradedunitrangedual.jl") include("fusion.jl") end diff --git a/src/GradedUnitRanges/dual.jl b/src/GradedUnitRanges/dual.jl index f52cb7a..960c343 100644 --- a/src/GradedUnitRanges/dual.jl +++ b/src/GradedUnitRanges/dual.jl @@ -1,5 +1,3 @@ -using ..LabelledNumbers: LabelledInteger, label, labelled, unlabel - """ dual(x) @@ -12,12 +10,6 @@ dual(x) = x nondual(r::AbstractUnitRange) = r isdual(::AbstractUnitRange) = false -dual_type(x) = dual_type(typeof(x)) -dual_type(T::Type) = T -nondual_type(x) = nondual_type(typeof(x)) -nondual_type(T::Type) = T - -dual(i::LabelledInteger) = labelled(unlabel(i), dual(label(i))) flip(a::AbstractUnitRange) = dual(map_blocklabels(dual, a)) """ diff --git a/src/GradedUnitRanges/fusion.jl b/src/GradedUnitRanges/fusion.jl index 4286f8a..9abc763 100644 --- a/src/GradedUnitRanges/fusion.jl +++ b/src/GradedUnitRanges/fusion.jl @@ -1,10 +1,9 @@ -using BlockArrays: AbstractBlockedUnitRange, blocklengths +using BlockArrays: blocklengths using ..LabelledNumbers: LabelledInteger, label, labelled using SplitApplyCombine: groupcount using TensorProducts: TensorProducts, OneToOne, tensor_product -flip_dual(r::AbstractUnitRange) = r -flip_dual(r::GradedUnitRangeDual) = flip(r) +flip_dual(r::AbstractUnitRange) = isdual(r) ? flip(r) : r function fuse_labels(x, y) return error( @@ -68,7 +67,6 @@ function sectormergesort(g::AbstractGradedUnitRange) return gradedrange(new_blocklengths) end -sectormergesort(g::GradedUnitRangeDual) = flip(sectormergesort(flip(g))) sectormergesort(g::AbstractUnitRange) = g # tensor_product produces a sorted, non-dual GradedUnitRange diff --git a/src/GradedUnitRanges/gradedunitrange.jl b/src/GradedUnitRanges/gradedunitrange.jl index a80d4bb..68857a0 100644 --- a/src/GradedUnitRanges/gradedunitrange.jl +++ b/src/GradedUnitRanges/gradedunitrange.jl @@ -12,6 +12,7 @@ using BlockArrays: BlockedUnitRange, block, blockedrange, + blockfirsts, blockisequal, blocklasts, blocklength, @@ -28,99 +29,93 @@ using BlockSparseArrays: blockedunitrange_getindices using Compat: allequal using FillArrays: Fill -using ..LabelledNumbers: - LabelledNumbers, - LabelledInteger, - LabelledUnitRange, - label, - label_type, - labelled, - labelled_isequal, - unlabel abstract type AbstractGradedUnitRange{T,BlockLasts} <: AbstractBlockedUnitRange{T,BlockLasts} end -struct GradedUnitRange{T,BlockLasts<:Vector{T}} <: AbstractGradedUnitRange{T,BlockLasts} - first::T - lasts::BlockLasts +struct GradedUnitRange{ + T,BlockLasts,BR<:AbstractBlockedUnitRange{T,BlockLasts},SUR<:SectorOneTo{T} +} <: AbstractGradedUnitRange{T,BlockLasts} + sectors::Vector{SUR} + range::BR + + function GradedUnitRange( + sectors::AbstractVector, range::AbstractBlockedUnitRange{T,BlockLasts} + ) where {T,BlockLasts} + @assert length.(sectors) == blocklengths(range) + return new{T,BlockLasts,typeof(range),eltype(sectors)}(sectors, range) + end end -struct GradedOneTo{T,BlockLasts<:Vector{T}} <: AbstractGradedUnitRange{T,BlockLasts} - lasts::BlockLasts +const GradedOneTo{T,BlockLasts,BR,SUR} = + GradedUnitRange{T,BlockLasts,BR,SUR} where {BR<:BlockedOneTo} - # assume that lasts is sorted, no checks carried out here - function GradedOneTo(lasts::BlockLasts) where {T<:Integer,BlockLasts<:AbstractVector{T}} - Base.require_one_based_indexing(lasts) - isempty(lasts) || first(lasts) >= 0 || throw(ArgumentError("blocklasts must be >= 0")) - return new{T,BlockLasts}(lasts) - end - function GradedOneTo(lasts::BlockLasts) where {T<:Integer,BlockLasts<:Tuple{T,Vararg{T}}} - first(lasts) >= 0 || throw(ArgumentError("blocklasts must be >= 0")) - return new{T,BlockLasts}(lasts) - end +# Accessors +sectors(g::GradedUnitRange) = g.sectors +unlabel_blocks(g::GradedUnitRange) = g.range + +# +# Constructors +# + +#= TBD remove? +# assume that lasts is sorted, no checks carried out here +function GradedOneTo(lasts::BlockLasts) where {T<:Integer,BlockLasts<:AbstractVector{T}} +Base.require_one_based_indexing(lasts) +isempty(lasts) || first(lasts) >= 0 || throw(ArgumentError("blocklasts must be >= 0")) +return new{T,BlockLasts}(lasts) +end +function GradedOneTo(lasts::BlockLasts) where {T<:Integer,BlockLasts<:Tuple{T,Vararg{T}}} +first(lasts) >= 0 || throw(ArgumentError("blocklasts must be >= 0")) +return new{T,BlockLasts}(lasts) +end +=# + +sector_type(x) = sector_type(typeof(x)) +sector_type(::Type) = error("Not implemented") +sector_type(::Type{<:GradedUnitRange{<:Any,<:Any,<:Any,SUR}}) where {SUR} = sector_type(SUR) + +function gradedrange( + lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; dual::Bool=false +) + brange = blockedrange(last.(lblocklengths) .* length.(first.(lblocklengths))) + sectors = sectorunitrange.(lblocklengths, dual) + return GradedUnitRange(sectors, brange) end +dual(g::GradedUnitRange) = GradedUnitRange(dual.(sectors(g)), unlabel_blocks(g)) + +sector_mulitplicities(g::GradedUnitRange) = sector_mulitplicities.(sectors(g)) + function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) - v = map(b -> label(b) => unlabel(b), blocks(g)) println(io, typeof(g)) - return print(io, join(repr.(v), '\n')) + return print(io, join(repr.(blocks(g)), '\n')) end function Base.show(io::IO, g::AbstractGradedUnitRange) - v = map(b -> label(b) => unlabel(b), blocks(g)) + v = blocklabels(g) .=> blocklengths(g) return print(io, nameof(typeof(g)), '[', join(repr.(v), ", "), ']') 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) - # TODO: fix type piracy - return blockisequal(a1, a2) && (blocklabels(a1) == blocklabels(a2)) -end function space_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) - return (isdual(a1) == isdual(a2)) && labelled_isequal(a1, a2) + return (isdual(a1) == isdual(a2)) && + blocklabels(a1) == blocklabels(a2) && + blockisequal(a1, a2) end # needed in BlockSparseArrays -function Base.AbstractUnitRange{T}( - a::AbstractGradedUnitRange{<:LabelledInteger{T}} -) where {T} +function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} return unlabel_blocks(a) end -function Base.last(a::AbstractGradedUnitRange) - return isempty(a.lasts) ? labelled(first(a) - 1, label(first(a))) : last(a.lasts) -end +Base.last(a::AbstractGradedUnitRange) = last(unlabel_blocks(a)) # TODO: Use `TypeParameterAccessors`. Base.eltype(::Type{<:GradedUnitRange{T}}) where {T} = T -LabelledNumbers.label_type(g::AbstractGradedUnitRange) = label_type(typeof(g)) -LabelledNumbers.label_type(T::Type{<:AbstractGradedUnitRange}) = label_type(eltype(T)) - -to_sector(x) = x - -sector_type(x) = sector_type(typeof(x)) -sector_type(T::Type) = error("Not implemented") -sector_type(T::Type{<:AbstractUnitRange}) = label_type(T) - -# To help with generic code. -function BlockArrays.blockedrange(lblocklengths::AbstractVector{<:LabelledInteger}) - return gradedrange(lblocklengths) -end - -function gradedrange(lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}) - return gradedrange(labelled.(last.(lblocklengths), first.(lblocklengths))) -end -function gradedrange(lblocklengths::AbstractVector{<:LabelledInteger}) - brange = blockedrange(unlabel.(lblocklengths)) - lblocklasts = labelled.(blocklasts(brange), to_sector.(label.(lblocklengths))) - return GradedOneTo(lblocklasts) -end function labelled_blocks(a::BlockedOneTo, labels) # TODO: Use `blocklasts(a)`? That might @@ -172,64 +167,36 @@ function blocklabels(a::AbstractBlockVector) end function blocklabels(a::AbstractBlockedUnitRange) - # Using `a.lasts` here since that is what is stored - # inside of `BlockedUnitRange`, maybe change that. - # For example, it could be something like: - # - # map(BlockRange(a)) do block - # return label(@view(a[block])) - # end - # - return label.(a.lasts) -end - -# TODO: This relies on internals of `BlockArrays`, maybe redesign -# to try to avoid that. -# TODO: Define `set_grades`, `set_sector_labels`, `set_labels`. -function unlabel_blocks(a::GradedOneTo) - # TODO: Use `blocklasts(a)`. - return BlockedOneTo(unlabel.(a.lasts)) -end -function unlabel_blocks(a::GradedUnitRange) - return BlockArrays._BlockedUnitRange(a.first, unlabel.(a.lasts)) + return map(sr -> only(blocklabels(sr)), sectors(a)) end ## BlockedUnitRange interface function Base.axes(ga::AbstractGradedUnitRange) - return map(axes(unlabel_blocks(ga))) do a - return labelled_blocks(a, blocklabels(ga)) - end + return (GradedUnitRange(sectors(ga), blockedrange(blocklengths(ga))),) end function gradedunitrange_blockfirsts(a::AbstractGradedUnitRange) - return labelled.(blockfirsts(unlabel_blocks(a)), blocklabels(a)) + return blockfirsts(unlabel_blocks(a)) end function BlockArrays.blockfirsts(a::AbstractGradedUnitRange) return gradedunitrange_blockfirsts(a) end function BlockArrays.blocklasts(a::AbstractGradedUnitRange) - return labelled.(blocklasts(unlabel_blocks(a)), blocklabels(a)) + return blocklasts(unlabel_blocks(a)) end function BlockArrays.blocklengths(a::AbstractGradedUnitRange) - return labelled.(blocklengths(unlabel_blocks(a)), blocklabels(a)) + return blocklengths(unlabel_blocks(a)) end -function gradedunitrange_first(a::AbstractUnitRange) - return labelled(first(unlabel_blocks(a)), label(a[Block(1)])) -end function Base.first(a::AbstractGradedUnitRange) - return gradedunitrange_first(a) + return first(unlabel_blocks(a)) end -Base.iterate(a::AbstractGradedUnitRange) = isempty(a) ? nothing : (first(a), first(a)) -function Base.iterate(a::AbstractGradedUnitRange, i) - i == last(a) && return nothing - next = a[i + step(a)] - return (next, next) -end +Base.iterate(a::AbstractGradedUnitRange) = iterate(unlabel_blocks(a)) +Base.iterate(a::AbstractGradedUnitRange, i) = iterate(unlabel_blocks(a), i) function firstblockindices(a::AbstractGradedUnitRange) return labelled.(firstblockindices(unlabel_blocks(a)), blocklabels(a)) @@ -238,7 +205,8 @@ end function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, index::Block{1} ) - return labelled(unlabel_blocks(a)[index], get_label(a, index)) + sr = sectors(a)[Int(index)] + return sectorunitrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) end function BlockSparseArrays.blockedunitrange_getindices( @@ -289,8 +257,7 @@ end function BlockSparseArrays.blockedunitrange_getindices( ga::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} ) - a_indices = blockedunitrange_getindices(unlabel_blocks(ga), indices) - return labelled_blocks(a_indices, blocklabels(ga, indices)) + return blockedunitrange_getindices(unlabel_blocks(ga), indices) end function BlockSparseArrays.blockedunitrange_getindices( @@ -312,7 +279,7 @@ function BlockSparseArrays.blockedunitrange_getindices( end function Base.getindex(a::AbstractGradedUnitRange, index::Integer) - return labelled(unlabel_blocks(a)[index], get_label(a, index)) + return unlabel_blocks(a)[index] end function Base.getindex(a::AbstractGradedUnitRange, index::Block{1}) @@ -383,13 +350,13 @@ end # that mixed dense and graded axes. # TODO: Maybe come up with a more general solution. function BlockArrays.combine_blockaxes( - a1::AbstractGradedUnitRange{<:LabelledInteger{T}}, a2::AbstractUnitRange{T} + a1::AbstractGradedUnitRange{T}, a2::AbstractUnitRange{T} ) where {T<:Integer} combined_blocklasts = sort!(union(unlabel.(blocklasts(a1)), blocklasts(a2))) return BlockedOneTo(combined_blocklasts) end function BlockArrays.combine_blockaxes( - a1::AbstractUnitRange{T}, a2::AbstractGradedUnitRange{<:LabelledInteger{T}} + a1::AbstractUnitRange{T}, a2::AbstractGradedUnitRange{T} ) where {T<:Integer} return BlockArrays.combine_blockaxes(a2, a1) end diff --git a/src/GradedUnitRanges/sectorunitrange.jl b/src/GradedUnitRanges/sectorunitrange.jl index c72a0a9..c18306f 100644 --- a/src/GradedUnitRanges/sectorunitrange.jl +++ b/src/GradedUnitRanges/sectorunitrange.jl @@ -22,8 +22,12 @@ const SectorOneTo{T,Sector,Range} = SectorUnitRange{T,Sector,Base.OneTo{T}} # Constructors # +to_sector(x) = x + # sectorunitrange(SU2(1), 2:5) -sectorunitrange(s, r::AbstractUnitRange, b::Bool=false) = SectorUnitRange(s, r, b) +function sectorunitrange(s, r::AbstractUnitRange, b::Bool=false) + SectorUnitRange(to_sector(s), r, b) +end # sectorunitrange(SU2(1), 1) function sectorunitrange(s, m::Integer, b::Bool=false) @@ -106,9 +110,3 @@ function map_blocklabels(f, sr::SectorUnitRange) end sector_type(::Type{<:SectorUnitRange{T,Sector}}) where {T,Sector} = Sector - -function space_isequal(sr1::SectorUnitRange, sr2::SectorUnitRange) - return nondual_sector(sr1) == nondual_sector(sr2) && - isdual(sr1) == isdual(sr2) && - full_range(sr1) == full_range(sr2) -end From b636fef0eff27fb5f8c1273c1cd6b1a6c5061c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Tue, 15 Apr 2025 09:29:01 -0400 Subject: [PATCH 03/33] WIP --- src/GradedArrays.jl | 14 ++---- src/GradedUnitRanges/GradedUnitRanges.jl | 2 +- src/GradedUnitRanges/fusion.jl | 46 +++++++++-------- src/GradedUnitRanges/gradedunitrange.jl | 15 ++++-- src/GradedUnitRanges/sectorunitrange.jl | 6 ++- src/SymmetrySectors/abstractsector.jl | 49 +++---------------- src/SymmetrySectors/sector_product.jl | 12 ++--- src/SymmetrySectors/symmetry_style.jl | 5 +- test/test_gradedunitranges_sectorunitrange.jl | 4 +- test/test_symmetrysectors_fusion_rules.jl | 21 ++------ test/test_symmetrysectors_sector_product.jl | 15 +++--- 11 files changed, 69 insertions(+), 120 deletions(-) diff --git a/src/GradedArrays.jl b/src/GradedArrays.jl index cbded68..45e2096 100644 --- a/src/GradedArrays.jl +++ b/src/GradedArrays.jl @@ -3,31 +3,25 @@ module GradedArrays include("LabelledNumbers/LabelledNumbers.jl") using .LabelledNumbers: LabelledNumbers include("GradedUnitRanges/GradedUnitRanges.jl") +include("SymmetrySectors/SymmetrySectors.jl") +include("GradedUnitRanges/fusion.jl") + # This makes the following names accessible # as `GradedArrays.x`. using .GradedUnitRanges: GradedUnitRanges, GradedOneTo, GradedUnitRange, - GradedUnitRangeDual, - LabelledUnitRangeDual, blocklabels, dag, dual, - dual_type, flip, gradedrange, isdual, map_blocklabels, nondual, - nondual_type, sector_type, - sectormergesort, - sectormergesortperm, - sectorsortperm, - space_isequal, - unmerged_tensor_product -include("SymmetrySectors/SymmetrySectors.jl") + space_isequal using .SymmetrySectors: SymmetrySectors include("gradedarray.jl") diff --git a/src/GradedUnitRanges/GradedUnitRanges.jl b/src/GradedUnitRanges/GradedUnitRanges.jl index ec857ba..1bee9da 100644 --- a/src/GradedUnitRanges/GradedUnitRanges.jl +++ b/src/GradedUnitRanges/GradedUnitRanges.jl @@ -5,6 +5,6 @@ export gradedrange include("sectorunitrange.jl") include("gradedunitrange.jl") include("dual.jl") -include("fusion.jl") +#include("fusion.jl") end diff --git a/src/GradedUnitRanges/fusion.jl b/src/GradedUnitRanges/fusion.jl index 9abc763..c3d0549 100644 --- a/src/GradedUnitRanges/fusion.jl +++ b/src/GradedUnitRanges/fusion.jl @@ -1,40 +1,44 @@ -using BlockArrays: blocklengths -using ..LabelledNumbers: LabelledInteger, label, labelled +using BlockArrays: Block, blocklengths, blocks using SplitApplyCombine: groupcount -using TensorProducts: TensorProducts, OneToOne, tensor_product +using TensorProducts: TensorProducts, ⊗, OneToOne + +using ..GradedUnitRanges: + SectorUnitRange, + AbstractGradedUnitRange, + nondual_sector, + rangemortar, + sector_multiplicity, + sectors +using ..SymmetrySectors: to_gradedrange flip_dual(r::AbstractUnitRange) = isdual(r) ? flip(r) : r -function fuse_labels(x, y) - return error( - "`fuse_labels` not implemented for object of type `$(typeof(x))` and `$(typeof(y))`." +# TensorProducts interface +function TensorProducts.tensor_product(sr1::SectorUnitRange, sr2::SectorUnitRange) + # TBD dispatch on SymmetryStyle and return either SectorUnitRange or GradedUnitRange? + s = to_gradedrange(nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2))) + return gradedrange( + blocklabels(s) .=> + sector_multiplicity(sr1) * sector_multiplicity(sr2) .* sector_multiplicity(s), ) end -function fuse_blocklengths(x::LabelledInteger, y::LabelledInteger) - # return blocked unit range to keep non-abelian interface - return blockedrange([labelled(x * y, fuse_labels(label(x), label(y)))]) -end - unmerged_tensor_product() = OneToOne() unmerged_tensor_product(a) = a unmerged_tensor_product(a, ::OneToOne) = a unmerged_tensor_product(::OneToOne, a) = a unmerged_tensor_product(::OneToOne, ::OneToOne) = OneToOne() -unmerged_tensor_product(a1, a2) = tensor_product(a1, a2) +unmerged_tensor_product(a1, a2) = a1 ⊗ a2 function unmerged_tensor_product(a1, a2, as...) return unmerged_tensor_product(unmerged_tensor_product(a1, a2), as...) end function unmerged_tensor_product(a1::AbstractGradedUnitRange, a2::AbstractGradedUnitRange) - nested = map(Iterators.flatten((Iterators.product(blocks(a1), blocks(a2)),))) do it - return mapreduce(length, fuse_blocklengths, it) - end - new_blocklengths = mapreduce(blocklengths, vcat, nested) - return blockedrange(new_blocklengths) + new_axes = map(splat(⊗), Iterators.flatten((Iterators.product(blocks(a1), blocks(a2)),))) + return rangemortar(reduce(vcat, sectors.(new_axes))) end -# convention: sort GradedUnitRangeDual according to nondual blocks +# convention: sort dual GradedUnitRange according to nondual blocks function sectorsortperm(a::AbstractUnitRange) return Block.(sortperm(blocklabels(nondual(a)))) end @@ -55,14 +59,14 @@ function sectormergesortperm(a::AbstractUnitRange) return Block.(groupsortperm(blocklabels(nondual(a)))) end -# Used by `TensorAlgebra.splitdims` in `BlockSparseArraysGradedUnitRangesExt`. +# Used by `TensorAlgebra.unmatricize` in `GradedArraysTensorAlgebraExt`. invblockperm(a::Vector{<:Block{1}}) = Block.(invperm(Int.(a))) function sectormergesort(g::AbstractGradedUnitRange) glabels = blocklabels(g) - gblocklengths = blocklengths(g) + multiplicities = sector_multiplicity(g) new_blocklengths = map(sort(unique(glabels))) do la - return labelled(sum(gblocklengths[findall(==(la), glabels)]; init=0), la) + return la => sum(multiplicities[findall(==(la), glabels)]; init=0) end return gradedrange(new_blocklengths) end diff --git a/src/GradedUnitRanges/gradedunitrange.jl b/src/GradedUnitRanges/gradedunitrange.jl index 68857a0..42f2c57 100644 --- a/src/GradedUnitRanges/gradedunitrange.jl +++ b/src/GradedUnitRanges/gradedunitrange.jl @@ -16,6 +16,7 @@ using BlockArrays: blockisequal, blocklasts, blocklength, + blocklengths, blocks, blockindex, combine_blockaxes, @@ -71,22 +72,26 @@ return new{T,BlockLasts}(lasts) end =# +sector_multiplicity(g::GradedUnitRange) = sector_multiplicity.(sectors(g)) + sector_type(x) = sector_type(typeof(x)) sector_type(::Type) = error("Not implemented") sector_type(::Type{<:GradedUnitRange{<:Any,<:Any,<:Any,SUR}}) where {SUR} = sector_type(SUR) +function rangemortar(sectors::Vector{<:SectorOneTo}) + brange = blockedrange(length.(sectors)) + return GradedUnitRange(sectors, brange) +end + function gradedrange( lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; dual::Bool=false ) - brange = blockedrange(last.(lblocklengths) .* length.(first.(lblocklengths))) sectors = sectorunitrange.(lblocklengths, dual) - return GradedUnitRange(sectors, brange) + return rangemortar(sectors) end dual(g::GradedUnitRange) = GradedUnitRange(dual.(sectors(g)), unlabel_blocks(g)) -sector_mulitplicities(g::GradedUnitRange) = sector_mulitplicities.(sectors(g)) - function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) println(io, typeof(g)) return print(io, join(repr.(blocks(g)), '\n')) @@ -404,5 +409,5 @@ end map_blocklabels(::Any, a::AbstractUnitRange) = a function map_blocklabels(f, g::AbstractGradedUnitRange) # use labelled_blocks to preserve GradedUnitRange - return labelled_blocks(unlabel_blocks(g), f.(blocklabels(g))) + return GradedUnitRange(map_blocklabels.(f, sectors(g)), unlabel_blocks(g)) end diff --git a/src/GradedUnitRanges/sectorunitrange.jl b/src/GradedUnitRanges/sectorunitrange.jl index c18306f..d4e3720 100644 --- a/src/GradedUnitRanges/sectorunitrange.jl +++ b/src/GradedUnitRanges/sectorunitrange.jl @@ -1,6 +1,8 @@ using BlockArrays: BlockArrays +using TensorProducts: tensor_product + # This implementation contains the "full range" # it does not check that such a range is consistent with the sector quantum_dimension # when sliced directly, the label is dropped @@ -26,7 +28,7 @@ to_sector(x) = x # sectorunitrange(SU2(1), 2:5) function sectorunitrange(s, r::AbstractUnitRange, b::Bool=false) - SectorUnitRange(to_sector(s), r, b) + return SectorUnitRange(to_sector(s), r, b) end # sectorunitrange(SU2(1), 1) @@ -95,7 +97,7 @@ function blocklabels(sr::SectorUnitRange) end # TBD error for non-integer? -sector_mulitplicities(sr::SectorUnitRange) = length(sr) ÷ length(nondual_sector(sr)) +sector_multiplicity(sr::SectorUnitRange) = length(sr) ÷ length(nondual_sector(sr)) function dual(sr::SectorUnitRange) return sectorunitrange(nondual_sector(sr), full_range(sr), !isdual(sr)) diff --git a/src/SymmetrySectors/abstractsector.jl b/src/SymmetrySectors/abstractsector.jl index a470399..72f41b7 100644 --- a/src/SymmetrySectors/abstractsector.jl +++ b/src/SymmetrySectors/abstractsector.jl @@ -2,8 +2,7 @@ # all fusion categories (Z{2}, SU2, Ising...) are subtypes of AbstractSector using BlockArrays: blocklengths -using ..LabelledNumbers: LabelledInteger, label, label_type, labelled, unlabel, unlabel_type -using ..GradedUnitRanges: GradedUnitRanges, blocklabels, fuse_blocklengths, gradedrange +using ..GradedUnitRanges: GradedUnitRanges, blocklabels, gradedrange, sector_multiplicity using TensorProducts: TensorProducts, ⊗, tensor_product abstract type AbstractSector end @@ -18,10 +17,7 @@ Base.length(s::AbstractSector) = quantum_dimension(s) # ================================= Sectors interface ==================================== trivial(x) = trivial(typeof(x)) function trivial(axis_type::Type{<:AbstractUnitRange}) - return gradedrange([trivial(eltype(axis_type))]) # always returns nondual -end -function trivial(la_type::Type{<:LabelledInteger}) - return labelled(one(unlabel_type(la_type)), trivial(label_type(la_type))) + return gradedrange([trivial(sector_type(axis_type)) => 1]) # always returns nondual end function trivial(type::Type) return error("`trivial` not defined for type $(type).") @@ -33,32 +29,24 @@ function sector_label(c::AbstractSector) return error("method `sector_label` not defined for type $(typeof(c))") end -block_dimensions(g::AbstractUnitRange) = block_dimensions(SymmetryStyle(g), g) -block_dimensions(::AbelianStyle, g) = unlabel.(blocklengths(g)) -function block_dimensions(::NotAbelianStyle, g) - return quantum_dimension.(blocklabels(g)) .* blocklengths(g) -end - -quantum_dimension(x) = quantum_dimension(SymmetryStyle(x), x) +quantum_dimension(g::AbstractUnitRange) = length(g) +quantum_dimension(s::AbstractSector) = quantum_dimension(SymmetryStyle(s), s) function quantum_dimension(::NotAbelianStyle, c::AbstractSector) return error("method `quantum_dimension` not defined for type $(typeof(c))") end quantum_dimension(::AbelianStyle, ::AbstractSector) = 1 -quantum_dimension(::AbelianStyle, g::AbstractUnitRange) = length(g) -quantum_dimension(::NotAbelianStyle, g::AbstractUnitRange) = sum(block_dimensions(g)) # convert to range -to_gradedrange(c::AbstractSector) = to_gradedrange(labelled(1, c)) -to_gradedrange(l::LabelledInteger) = gradedrange([l]) +to_gradedrange(c::AbstractSector) = gradedrange([c => 1]) to_gradedrange(g::AbstractUnitRange) = g function nsymbol(s1::AbstractSector, s2::AbstractSector, s3::AbstractSector) full_space = to_gradedrange(s1 ⊗ s2) i = findfirst(==(s3), blocklabels(full_space)) isnothing(i) && return 0 - return unlabel(blocklengths(full_space)[i]) + return sector_multiplicity(full_space)[i] end # =============================== Fusion rule interface ================================== @@ -73,7 +61,7 @@ end # abelian case: return Sector function fusion_rule(::AbelianStyle, c1::C, c2::C) where {C<:AbstractSector} - return label(only(fusion_rule(NotAbelianStyle(), c1, c2))) + return only(blocklabels(fusion_rule(NotAbelianStyle(), c1, c2))) end function label_fusion_rule(sector_type::Type{<:AbstractSector}, l1, l2) @@ -98,26 +86,3 @@ end # ================================ GradedUnitRanges interface ================================== GradedUnitRanges.sector_type(S::Type{<:AbstractSector}) = S - -# tensor_product interface -function GradedUnitRanges.fuse_blocklengths( - l1::LabelledInteger{<:Integer,<:AbstractSector}, - l2::LabelledInteger{<:Integer,<:AbstractSector}, -) - return fuse_blocklengths(combine_styles(SymmetryStyle(l1), SymmetryStyle(l2)), l1, l2) -end - -function GradedUnitRanges.fuse_blocklengths( - ::NotAbelianStyle, l1::LabelledInteger, l2::LabelledInteger -) - fused = label(l1) ⊗ label(l2) - v = labelled.(l1 * l2 .* blocklengths(fused), blocklabels(fused)) - return gradedrange(v) -end - -function GradedUnitRanges.fuse_blocklengths( - ::AbelianStyle, l1::LabelledInteger, l2::LabelledInteger -) - fused = label(l1) ⊗ label(l2) - return gradedrange([labelled(l1 * l2, fused)]) -end diff --git a/src/SymmetrySectors/sector_product.jl b/src/SymmetrySectors/sector_product.jl index 473327a..7c27746 100644 --- a/src/SymmetrySectors/sector_product.jl +++ b/src/SymmetrySectors/sector_product.jl @@ -2,7 +2,6 @@ # e.g. U(1)×U(1), U(1)×SU2(2)×SU(3) using BlockArrays: blocklengths -using ..LabelledNumbers: LabelledInteger, label, labelled, unlabel using ..GradedUnitRanges: GradedUnitRanges, dual, map_blocklabels # ===================================== Definition ======================================= @@ -102,16 +101,11 @@ end ×(c1::NamedTuple, c2::AbstractSector) = ×(SectorProduct(c1), SectorProduct(c2)) ×(c1::AbstractSector, c2::NamedTuple) = ×(SectorProduct(c1), SectorProduct(c2)) -function ×(l1::LabelledInteger, l2::LabelledInteger) - c3 = label(l1) × label(l2) - m3 = unlabel(l1) * unlabel(l2) - return labelled(m3, c3) -end - function ×(g1::AbstractUnitRange, g2::AbstractUnitRange) + # TBD SectorUnitRange? v = map( ((l1, l2),) -> l1 × l2, - Iterators.flatten((Iterators.product(blocklengths(g1), blocklengths(g2)),),), + Iterators.flatten((Iterators.product(blocklabels(g1), blocklabels(g2)),),), ) return gradedrange(v) end @@ -136,7 +130,7 @@ end # Abelian case: fusion returns SectorProduct function fusion_rule(::AbelianStyle, s1::SectorProduct, s2::SectorProduct) - return label(only(fusion_rule(NotAbelianStyle(), s1, s2))) + return only(blocklabels((fusion_rule(NotAbelianStyle(), s1, s2)))) end # lift ambiguities for TrivialSector diff --git a/src/SymmetrySectors/symmetry_style.jl b/src/SymmetrySectors/symmetry_style.jl index f3e443d..275387e 100644 --- a/src/SymmetrySectors/symmetry_style.jl +++ b/src/SymmetrySectors/symmetry_style.jl @@ -1,7 +1,7 @@ # This file defines SymmetryStyle, a trait to distinguish abelian groups, non-abelian groups # and non-group fusion categories. -using ..LabelledNumbers: LabelledInteger, label_type +using ..GradedUnitRanges: sector_type abstract type SymmetryStyle end @@ -10,8 +10,7 @@ struct NotAbelianStyle <: SymmetryStyle end SymmetryStyle(x) = SymmetryStyle(typeof(x)) SymmetryStyle(T::Type) = error("method `SymmetryStyle` not defined for type $(T)") -SymmetryStyle(L::Type{<:LabelledInteger}) = SymmetryStyle(label_type(L)) -SymmetryStyle(G::Type{<:AbstractUnitRange}) = SymmetryStyle(eltype(G)) +SymmetryStyle(G::Type{<:AbstractUnitRange}) = SymmetryStyle(sector_type(G)) combine_styles(::AbelianStyle, ::AbelianStyle) = AbelianStyle() combine_styles(::SymmetryStyle, ::SymmetryStyle) = NotAbelianStyle() diff --git a/test/test_gradedunitranges_sectorunitrange.jl b/test/test_gradedunitranges_sectorunitrange.jl index 7ed4f8b..bcb3035 100644 --- a/test/test_gradedunitranges_sectorunitrange.jl +++ b/test/test_gradedunitranges_sectorunitrange.jl @@ -10,7 +10,7 @@ using GradedArrays.GradedUnitRanges: full_range, isdual, nondual_sector, - sector_mulitplicities, + sector_multiplicities, sector_type, sectorunitrange, space_isequal @@ -90,7 +90,7 @@ using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @test sector_type(sr) === SU{3,2} @test sector_type(typeof(sr)) === SU{3,2} @test blocklabels(sr) == [SU((1, 0))] - @test sector_mulitplicities(sr) == [2] + @test sector_multiplicities(sr) == [2] srd = dual(sr) @test nondual_sector(srd) == SU((1, 0)) diff --git a/test/test_symmetrysectors_fusion_rules.jl b/test/test_symmetrysectors_fusion_rules.jl index a849f94..4d90d2b 100644 --- a/test/test_symmetrysectors_fusion_rules.jl +++ b/test/test_symmetrysectors_fusion_rules.jl @@ -1,17 +1,6 @@ using GradedArrays: dual, space_isequal, gradedrange, flip, unmerged_tensor_product using GradedArrays.SymmetrySectors: - Fib, - Ising, - O2, - SU, - SU2, - TrivialSector, - U1, - Z, - block_dimensions, - nsymbol, - quantum_dimension, - trivial + Fib, Ising, O2, SU, SU2, TrivialSector, U1, Z, nsymbol, quantum_dimension, trivial using TensorProducts: ⊗, tensor_product using Test: @test, @testset, @test_throws using TestExtras: @constinferred @@ -37,7 +26,7 @@ using TestExtras: @constinferred @test tensor_product(z0, z0, z0) == z0 @test tensor_product(z0, z0, z0, z0) == z0 - @test (@constinferred block_dimensions(gradedrange([z1 => 1]))) == [1] + @test (@constinferred quantum_dimension(gradedrange([z1 => 1]))) == 1 end @testset "U(1) fusion rules" begin q1 = U1(1) @@ -77,7 +66,6 @@ using TestExtras: @constinferred ) @test (@constinferred quantum_dimension(s0o ⊗ s1)) == 2 - @test (@constinferred block_dimensions(s0o ⊗ s1)) == [2] end @testset "SU2 fusion rules" begin @@ -93,7 +81,6 @@ using TestExtras: @constinferred @test space_isequal(j3 ⊗ j3, gradedrange([j1 => 1, j3 => 1, j5 => 1])) @test space_isequal((@constinferred j1 ⊗ j2), gradedrange([j2 => 1])) @test (@constinferred quantum_dimension(j1 ⊗ j2)) == 2 - @test (@constinferred block_dimensions(j1 ⊗ j2)) == [2] @test tensor_product(j2) == j2 @test space_isequal(tensor_product(j2, j1), gradedrange([j2 => 1])) @@ -142,7 +129,7 @@ end g2 = gradedrange([U1(-2) => 2, U1(0) => 1, U1(1) => 2]) @test space_isequal(flip(dual(g1)), gradedrange([U1(1) => 1, U1(0) => 1, U1(-1) => 2])) - @test (@constinferred block_dimensions(g1)) == [1, 1, 2] + @test (@constinferred blocklengths(g1)) == [1, 1, 2] gt = gradedrange([ U1(-3) => 2, @@ -241,7 +228,7 @@ end (@constinferred tensor_product(g3, g4)), gradedrange([SU2(0) => 4, SU2(1//2) => 6, SU2(1) => 6, SU2(3//2) => 5, SU2(2) => 2]), ) - @test (@constinferred block_dimensions(g3)) == [1, 4, 3] + @test (@constinferred blocklengths(g3)) == [1, 4, 3] # test dual on non self-conjugate non-abelian representations s1 = SU{3}((0, 0)) diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index ef607b5..72d17bd 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -8,7 +8,6 @@ using GradedArrays.SymmetrySectors: TrivialSector, U1, Z, - block_dimensions, quantum_dimension, arguments, trivial @@ -90,15 +89,15 @@ using TestExtras: @constinferred (SU2(1) × SU2(1)) => 1, ]) @test (@constinferred quantum_dimension(g)) == 16 - @test (@constinferred block_dimensions(g)) == [1, 3, 3, 9] + @test (@constinferred blocklengths(g)) == [1, 3, 3, 9] # mixed group g = gradedrange([(U1(2) × SU2(0) × Z{2}(0)) => 1, (U1(2) × SU2(1) × Z{2}(0)) => 1]) @test (@constinferred quantum_dimension(g)) == 4 - @test (@constinferred block_dimensions(g)) == [1, 3] + @test (@constinferred blocklengths(g)) == [1, 3] g = gradedrange([(SU2(0) × U1(0) × SU2(1//2)) => 1, (SU2(0) × U1(1) × SU2(1//2)) => 1]) @test (@constinferred quantum_dimension(g)) == 4 - @test (@constinferred block_dimensions(g)) == [2, 2] + @test (@constinferred blocklengths(g)) == [2, 2] # NonGroupCategory g_fib = gradedrange([(Fib("1") × Fib("1")) => 1]) @@ -107,8 +106,8 @@ using TestExtras: @constinferred @test (@constinferred quantum_dimension(g_fib)) == 1.0 @test (@constinferred quantum_dimension(g_ising)) == 1.0 @test (@constinferred quantum_dimension((Ising("1") × Ising("1")))) == 1.0 - @test (@constinferred block_dimensions(g_fib)) == [1.0] - @test (@constinferred block_dimensions(g_ising)) == [1.0] + @test (@constinferred blocklengths(g_fib)) == [1.0] + @test (@constinferred blocklengths(g_ising)) == [1.0] @test (@constinferred quantum_dimension(U1(1) × Fib("1"))) == 1.0 @test (@constinferred quantum_dimension(gradedrange([U1(1) × Fib("1") => 1]))) == 1.0 @@ -121,7 +120,7 @@ using TestExtras: @constinferred (U1(2) × SU2(1) × Ising("ψ")) => 1, ]) @test (@constinferred quantum_dimension(g)) == 8.0 - @test (@constinferred block_dimensions(g)) == [1.0, 3.0, 1.0, 3.0] + @test (@constinferred blocklengths(g)) == [1.0, 3.0, 1.0, 3.0] ϕ = (1 + √5) / 2 g = gradedrange([ @@ -131,7 +130,7 @@ using TestExtras: @constinferred (Fib("τ") × SU2(1) × U1(2)) => 1, ]) @test (@constinferred quantum_dimension(g)) == 4.0 + 4.0ϕ - @test (@constinferred block_dimensions(g)) == [1.0, 3.0, 1.0ϕ, 3.0ϕ] + @test (@constinferred blocklengths(g)) == [1.0, 3.0, 1.0ϕ, 3.0ϕ] end @testset "Fusion of Abelian products" begin From d754b117ea11f6df2776e29a7e3b6510f0a0bbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Tue, 15 Apr 2025 10:16:26 -0400 Subject: [PATCH 04/33] refactor tests --- src/GradedArrays.jl | 1 + src/GradedUnitRanges/gradedunitrange.jl | 2 +- test/test_gradedunitranges_basics.jl | 231 +++++++++--------------- 3 files changed, 84 insertions(+), 150 deletions(-) diff --git a/src/GradedArrays.jl b/src/GradedArrays.jl index 45e2096..0e46930 100644 --- a/src/GradedArrays.jl +++ b/src/GradedArrays.jl @@ -21,6 +21,7 @@ using .GradedUnitRanges: map_blocklabels, nondual, sector_type, + sectorunitrange, space_isequal using .SymmetrySectors: SymmetrySectors include("gradedarray.jl") diff --git a/src/GradedUnitRanges/gradedunitrange.jl b/src/GradedUnitRanges/gradedunitrange.jl index 42f2c57..6453368 100644 --- a/src/GradedUnitRanges/gradedunitrange.jl +++ b/src/GradedUnitRanges/gradedunitrange.jl @@ -368,7 +368,7 @@ end # preserve labels inside combine_blockaxes function BlockArrays.combine_blockaxes(a::GradedOneTo, b::GradedOneTo) - return GradedOneTo(sortedunion(blocklasts(a), blocklasts(b))) + return GradedUnitRange(sortedunion(blocklasts(a), blocklasts(b))) end function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) new_blocklasts = sortedunion(blocklasts(a), blocklasts(b)) diff --git a/test/test_gradedunitranges_basics.jl b/test/test_gradedunitranges_basics.jl index adccf9d..64cc512 100644 --- a/test/test_gradedunitranges_basics.jl +++ b/test/test_gradedunitranges_basics.jl @@ -12,166 +12,99 @@ using BlockArrays: combine_blockaxes, mortar using GradedArrays: - GradedOneTo, GradedUnitRange, blocklabels, gradedrange, sector_type, space_isequal -using GradedArrays.LabelledNumbers: - LabelledUnitRange, islabelled, label, labelled, labelled_isequal, unlabel + GradedOneTo, + GradedUnitRange, + SectorUnitRange, + blocklabels, + gradedrange, + nondual_sector, + sector_type, + sectorunitrange, + space_isequal using Test: @test, @test_broken, @testset @testset "GradedUnitRanges basics" begin a0 = Base.OneTo(1) - for a in ( - blockedrange([labelled(2, "x"), labelled(3, "y")]), - gradedrange([labelled(2, "x"), labelled(3, "y")]), - gradedrange(["x" => 2, "y" => 3]), - ) - @test a isa GradedOneTo - @test sector_type(a) === String - @test labelled_isequal(a, a) - @test !labelled_isequal(a0, a) - @test !labelled_isequal(a, a0) - @test !labelled_isequal(a, 1:5) - for x in iterate(a) - @test x == 1 - @test label(x) == "x" - end - for x in iterate(a, labelled(1, "x")) - @test x == 2 - @test label(x) == "x" - end - for x in iterate(a, labelled(2, "x")) - @test x == 3 - @test label(x) == "y" - end - for x in iterate(a, labelled(3, "y")) - @test x == 4 - @test label(x) == "y" - end - for x in iterate(a, labelled(4, "y")) - @test x == 5 - @test label(x) == "y" - end - @test isnothing(iterate(a, labelled(5, "y"))) - @test labelled_isequal(a, a) - @test length(a) == 5 - @test step(a) == 1 - @test !islabelled(step(a)) - @test length(blocks(a)) == 2 - @test blocks(a)[1] == 1:2 - @test label(blocks(a)[1]) == "x" - @test blocks(a)[2] == 3:5 - @test label(blocks(a)[2]) == "y" - @test a[Block(2)] == 3:5 - @test label(a[Block(2)]) == "y" - @test a[Block(2)] isa LabelledUnitRange - @test a[4] == 4 - @test label(a[4]) == "y" - @test unlabel(a[4]) == 4 - @test blocklengths(a) == [2, 3] - @test blocklabels(a) == ["x", "y"] - @test label.(blocklengths(a)) == ["x", "y"] - @test blockfirsts(a) == [1, 3] - @test label.(blockfirsts(a)) == ["x", "y"] - @test first(a) == 1 - @test label(first(a)) == "x" - @test blocklasts(a) == [2, 5] - @test label.(blocklasts(a)) == ["x", "y"] - @test last(a) == 5 - @test label(last(a)) == "y" - @test a[Block(2)] == 3:5 - @test label(a[Block(2)]) == "y" - @test length(a[Block(2)]) == 3 - @test blocklengths(only(axes(a))) == blocklengths(a) - @test blocklabels(only(axes(a))) == blocklabels(a) - - @test axes(Base.Slice(a)) isa Tuple{typeof(a)} - @test AbstractUnitRange{Int}(a) == 1:5 - b = combine_blockaxes(a, a) - @test b isa GradedOneTo - @test b == 1:5 - @test space_isequal(b, a) + a = gradedrange(["x" => 2, "y" => 3]) + @test a isa GradedOneTo + @test a isa GradedUnitRange + @test sector_type(a) === String + @test space_isequal(a, a) + @test !space_isequal(a0, a) + @test !space_isequal(a, a0) + @test !space_isequal(a, 1:5) + for x in iterate(a) + @test x == 1 + end + for x in iterate(a, 1) + @test x == 2 + end + for x in iterate(a, 2) + @test x == 3 + end + for x in iterate(a, 3) + @test x == 4 + end + for x in iterate(a, 4) + @test x == 5 end + @test isnothing(iterate(a, 5)) + @test length(a) == 5 + @test step(a) == 1 + @test length(blocks(a)) == 2 + @test blocks(a)[1] == 1:2 + @test nondual_sector(blocks(a)[1]) == "x" + @test blocks(a)[2] == 3:5 + @test nondual_sector(blocks(a)[2]) == "y" - # Slicing operations - x = gradedrange(["x" => 2, "y" => 3]) - a = x[2:4] - @test a isa GradedUnitRange - @test length(a) == 3 - @test blocklength(a) == 2 - @test a[Block(1)] == 2:2 - @test label(a[Block(1)]) == "x" - @test a[Block(2)] == 3:4 - @test label(a[Block(2)]) == "y" - ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - @test blocklengths(ax) == blocklengths(a) - @test blocklabels(ax) == blocklabels(a) - @test blockfirsts(a) == [2, 3] + @test a[Block(2)] isa SectorUnitRange + @test space_isequal(a[Block(2)], sectorunitrange("y", 3:5)) - @test AbstractUnitRange{Int}(a) == 2:4 - b = combine_blockaxes(a, a) - @test b isa GradedUnitRange - @test b == 1:4 + @test a[4] == 4 + @test blocklengths(a) == [2, 3] + @test blocklabels(a) == ["x", "y"] + @test blockfirsts(a) == [1, 3] + @test first(a) == 1 + @test blocklasts(a) == [2, 5] + @test last(a) == 5 + @test blocklengths(only(axes(a))) == blocklengths(a) + @test blocklabels(only(axes(a))) == blocklabels(a) + + @test axes(Base.Slice(a)) isa Tuple{typeof(a)} + @test AbstractUnitRange{Int}(a) == 1:5 + b = combine_blockaxes(a, a) # TODO + @test b isa GradedOneTo + @test b == 1:5 + @test space_isequal(b, a) - @test x[[2, 4]] == [labelled(2, "x"), labelled(4, "y")] - @test labelled_isequal(x[BlockRange(1)], gradedrange(["x" => 2])) + # Slicing operations + g = gradedrange(["x" => 2, "y" => 3]) + a = g[2:4] + @test a isa BlockedUnitRange + @test blockisequal(a, blockedrange([1, 1, 2])[Block.(2:3)]) + @test g[[2, 4]] == [2, 4] # Regression test for ambiguity error. - x = gradedrange(["x" => 2, "y" => 3]) - a = x[BlockSlice(Block(1), Base.OneTo(2))] + g = gradedrange(["x" => 2, "y" => 3]) + a = g[BlockSlice(Block(1), Base.OneTo(2))] @test length(a) == 2 @test a == 1:2 @test blocklength(a) == 1 - # TODO: Should this be a `GradedUnitRange`, - # or maybe just a `LabelledUnitRange`? - @test a isa LabelledUnitRange - @test length(a[Block(1)]) == 2 - @test label(a) == "x" - @test a[Block(1)] == 1:2 - @test label(a[Block(1)]) == "x" + @test a isa SectorUnitRange + @test space_isequal(a, sectorunitrange("x" => 2)) - x = gradedrange(["x" => 2, "y" => 3]) - a = x[3:4] - @test a isa GradedUnitRange - @test length(a) == 2 - @test blocklength(a) == 1 - @test a[Block(1)] == 3:4 - @test label(a[Block(1)]) == "y" - ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - @test blocklengths(ax) == blocklengths(a) - @test blocklabels(ax) == blocklabels(a) - @test axes(Base.Slice(a)) isa Tuple{typeof(a)} - - x = gradedrange(["x" => 2, "y" => 3]) - a = x[2:4][1:2] - @test a isa GradedUnitRange - @test length(a) == 2 - @test blocklength(a) == 2 - @test a[Block(1)] == 2:2 - @test label(a[Block(1)]) == "x" - @test a[Block(2)] == 3:3 - @test label(a[Block(2)]) == "y" - ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - @test blocklengths(ax) == blocklengths(a) - @test blocklabels(ax) == blocklabels(a) + g = gradedrange(["x" => 2, "y" => 3]) + a = g[3:4] + @test a isa BlockedUnitRange + @test blockisequal(a, blockedrange([2, 2])[Block.(2:2)]) - x = gradedrange(["x" => 2, "y" => 3]) - a = x[Block(2)[2:3]] - @test a isa LabelledUnitRange - @test length(a) == 2 + g = gradedrange(["x" => 2, "y" => 3]) + a = g[Block(2)[2:3]] + @test a isa UnitRange @test a == 4:5 - @test label(a) == "y" - ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - @test label(ax) == label(a) - x = gradedrange(["x" => 2, "y" => 3, "z" => 4]) - a = x[Block(2):Block(3)] + g = gradedrange(["x" => 2, "y" => 3, "z" => 4]) + a = g[Block(2):Block(3)] @test a isa GradedUnitRange @test length(a) == 7 @test blocklength(a) == 2 @@ -185,8 +118,8 @@ using Test: @test, @test_broken, @testset @test blocklengths(ax) == blocklengths(a) @test blocklabels(ax) == blocklabels(a) - x = gradedrange(["x" => 2, "y" => 3, "z" => 4]) - a = x[[Block(3), Block(2)]] + g = gradedrange(["x" => 2, "y" => 3, "z" => 4]) + a = g[[Block(3), Block(2)]] @test a isa BlockVector @test length(a) == 7 @test blocklength(a) == 2 @@ -226,15 +159,15 @@ using Test: @test, @test_broken, @testset @test blocklengths(ax) == [2, 2] @test blocklabels(ax) == blocklabels(a) - x = gradedrange(["x" => 2, "y" => 3]) + g = gradedrange(["x" => 2, "y" => 3]) I = mortar([Block(1)[1:1]]) - a = x[I] + a = g[I] @test length(a) == 1 @test label(first(a)) == "x" - x = gradedrange(["x" => 2, "y" => 3])[1:5] + g = gradedrange(["x" => 2, "y" => 3])[1:5] I = mortar([Block(1)[1:1]]) - a = x[I] + a = g[I] @test length(a) == 1 @test label(first(a)) == "x" end From 80c6cc998498459bee918f163f41236136076ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 16 Apr 2025 17:46:53 -0400 Subject: [PATCH 05/33] refactor type parameters and accessors --- src/GradedUnitRanges/fusion.jl | 17 +- src/GradedUnitRanges/gradedunitrange.jl | 216 +++++++++--------- src/GradedUnitRanges/sectorunitrange.jl | 25 +- src/SymmetrySectors/abstractsector.jl | 10 +- test/test_gradedunitranges_sectorunitrange.jl | 28 +-- test/test_gradedunitranges_tensor_product.jl | 1 - test/test_symmetrysectors_fusion_rules.jl | 32 +-- test/test_symmetrysectors_sector_product.jl | 46 +--- 8 files changed, 152 insertions(+), 223 deletions(-) diff --git a/src/GradedUnitRanges/fusion.jl b/src/GradedUnitRanges/fusion.jl index c3d0549..ce2c750 100644 --- a/src/GradedUnitRanges/fusion.jl +++ b/src/GradedUnitRanges/fusion.jl @@ -6,9 +6,10 @@ using ..GradedUnitRanges: SectorUnitRange, AbstractGradedUnitRange, nondual_sector, - rangemortar, - sector_multiplicity, - sectors + axis_cat, + sector_axes, + sector_multiplicities, + sector_multiplicity using ..SymmetrySectors: to_gradedrange flip_dual(r::AbstractUnitRange) = isdual(r) ? flip(r) : r @@ -19,7 +20,7 @@ function TensorProducts.tensor_product(sr1::SectorUnitRange, sr2::SectorUnitRang s = to_gradedrange(nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2))) return gradedrange( blocklabels(s) .=> - sector_multiplicity(sr1) * sector_multiplicity(sr2) .* sector_multiplicity(s), + sector_multiplicity(sr1) * sector_multiplicity(sr2) .* sector_multiplicities(s), ) end @@ -34,8 +35,10 @@ function unmerged_tensor_product(a1, a2, as...) end function unmerged_tensor_product(a1::AbstractGradedUnitRange, a2::AbstractGradedUnitRange) - new_axes = map(splat(⊗), Iterators.flatten((Iterators.product(blocks(a1), blocks(a2)),))) - return rangemortar(reduce(vcat, sectors.(new_axes))) + new_axes = map( + splat(⊗), Iterators.flatten((Iterators.product(sector_axes(a1), sector_axes(a2)),)) + ) + return axis_cat(reduce(vcat, sector_axes.(new_axes))) end # convention: sort dual GradedUnitRange according to nondual blocks @@ -64,7 +67,7 @@ invblockperm(a::Vector{<:Block{1}}) = Block.(invperm(Int.(a))) function sectormergesort(g::AbstractGradedUnitRange) glabels = blocklabels(g) - multiplicities = sector_multiplicity(g) + multiplicities = sector_multiplicities(g) new_blocklengths = map(sort(unique(glabels))) do la return la => sum(multiplicities[findall(==(la), glabels)]; init=0) end diff --git a/src/GradedUnitRanges/gradedunitrange.jl b/src/GradedUnitRanges/gradedunitrange.jl index 6453368..60dcef2 100644 --- a/src/GradedUnitRanges/gradedunitrange.jl +++ b/src/GradedUnitRanges/gradedunitrange.jl @@ -34,51 +34,48 @@ using FillArrays: Fill abstract type AbstractGradedUnitRange{T,BlockLasts} <: AbstractBlockedUnitRange{T,BlockLasts} end -struct GradedUnitRange{ - T,BlockLasts,BR<:AbstractBlockedUnitRange{T,BlockLasts},SUR<:SectorOneTo{T} -} <: AbstractGradedUnitRange{T,BlockLasts} - sectors::Vector{SUR} - range::BR - - function GradedUnitRange( - sectors::AbstractVector, range::AbstractBlockedUnitRange{T,BlockLasts} - ) where {T,BlockLasts} - @assert length.(sectors) == blocklengths(range) - return new{T,BlockLasts,typeof(range),eltype(sectors)}(sectors, range) +struct GradedUnitRange{T,SUR<:SectorOneTo{T},BR<:AbstractUnitRange{T},BlockLasts} <: + AbstractGradedUnitRange{T,BlockLasts} + sector_axes::Vector{SUR} + full_range::BR + + function GradedUnitRange{T,SUR,BR,BlockLasts}( + sector_axes::AbstractVector{SUR}, full_range::AbstractUnitRange{T} + ) where {T,SUR,BR,BlockLasts} + length.(sector_axes) == blocklengths(full_range) || + throw(ArgumentError("sectors and range are not compatible")) + typeof(blocklasts(full_range)) == BlockLasts || + throw(TypeError(:BlockLasts, "", blocklasts(full_range))) + return new{T,SUR,BR,BlockLasts}(sector_axes, full_range) end end -const GradedOneTo{T,BlockLasts,BR,SUR} = - GradedUnitRange{T,BlockLasts,BR,SUR} where {BR<:BlockedOneTo} +const GradedOneTo{T,SUR,BR,BlockLasts} = + GradedUnitRange{T,SUR,BR,BlockLasts} where {BR<:BlockedOneTo} -# Accessors -sectors(g::GradedUnitRange) = g.sectors -unlabel_blocks(g::GradedUnitRange) = g.range - -# -# Constructors -# - -#= TBD remove? -# assume that lasts is sorted, no checks carried out here -function GradedOneTo(lasts::BlockLasts) where {T<:Integer,BlockLasts<:AbstractVector{T}} -Base.require_one_based_indexing(lasts) -isempty(lasts) || first(lasts) >= 0 || throw(ArgumentError("blocklasts must be >= 0")) -return new{T,BlockLasts}(lasts) +function GradedUnitRange(sector_axes::AbstractVector, full_range::AbstractUnitRange) + return GradedUnitRange{ + eltype(full_range),eltype(sector_axes),typeof(full_range),typeof(blocklasts(full_range)) + }( + sector_axes, full_range + ) end -function GradedOneTo(lasts::BlockLasts) where {T<:Integer,BlockLasts<:Tuple{T,Vararg{T}}} -first(lasts) >= 0 || throw(ArgumentError("blocklasts must be >= 0")) -return new{T,BlockLasts}(lasts) -end -=# -sector_multiplicity(g::GradedUnitRange) = sector_multiplicity.(sectors(g)) +# Accessors +sector_axes(g::GradedUnitRange) = g.sector_axes +unlabel_blocks(g::GradedUnitRange) = g.full_range # TBD use full_range? + +sector_multiplicities(g::GradedUnitRange) = sector_multiplicity.(sector_axes(g)) sector_type(x) = sector_type(typeof(x)) sector_type(::Type) = error("Not implemented") -sector_type(::Type{<:GradedUnitRange{<:Any,<:Any,<:Any,SUR}}) where {SUR} = sector_type(SUR) +sector_type(::Type{<:GradedUnitRange{<:Any,SUR}}) where {SUR} = sector_type(SUR) + +# +# Constructors +# -function rangemortar(sectors::Vector{<:SectorOneTo}) +function axis_cat(sectors::AbstractVector{<:SectorOneTo}) brange = blockedrange(length.(sectors)) return GradedUnitRange(sectors, brange) end @@ -86,42 +83,74 @@ end function gradedrange( lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; dual::Bool=false ) - sectors = sectorunitrange.(lblocklengths, dual) - return rangemortar(sectors) + sectors = sectorrange.(lblocklengths, dual) + return axis_cat(sectors) end -dual(g::GradedUnitRange) = GradedUnitRange(dual.(sectors(g)), unlabel_blocks(g)) +# GradedUnitRange interface +dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), unlabel_blocks(g)) -function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) - println(io, typeof(g)) - return print(io, join(repr.(blocks(g)), '\n')) +struct NoLabel end +blocklabels(r::AbstractUnitRange) = Fill(NoLabel(), blocklength(r)) + +function blocklabels(a::AbstractBlockVector) + return map(BlockRange(a)) do block + return label(@view(a[block])) + end end -function Base.show(io::IO, g::AbstractGradedUnitRange) - v = blocklabels(g) .=> blocklengths(g) - return print(io, nameof(typeof(g)), '[', join(repr.(v), ", "), ']') +function blocklabels(g::AbstractBlockedUnitRange) + nondual_blocklabels = nondual_sector.(sector_axes(g)) + return isdual(g) ? dual.(nondual_blocklabels) : nondual_blocklabels 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)) +# The block labels of the corresponding slice. +function blocklabels(a::AbstractUnitRange, indices) + return map(_blocks(a, indices)) do block + return label(a[block]) + end +end +# == is just a range comparison that ignores labels. Need dedicated function to check equality. function space_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) return (isdual(a1) == isdual(a2)) && blocklabels(a1) == blocklabels(a2) && blockisequal(a1, a2) end +map_blocklabels(::Any, a::AbstractUnitRange) = a +function map_blocklabels(f, g::AbstractGradedUnitRange) + # use labelled_blocks to preserve GradedUnitRange + return GradedUnitRange(map_blocklabels.(f, sector_axes(g)), unlabel_blocks(g)) +end + +# Base interface + # needed in BlockSparseArrays function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} return unlabel_blocks(a) end +function Base.axes(ga::AbstractGradedUnitRange) + return (GradedUnitRange(sectors(ga), blockedrange(blocklengths(ga))),) +end + +function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) + println(io, typeof(g)) + return print(io, join(repr.(blocks(g)), '\n')) +end + +function Base.show(io::IO, g::AbstractGradedUnitRange) + v = blocklabels(g) .=> blocklengths(g) + return print(io, nameof(typeof(g)), '[', join(repr.(v), ", "), ']') +end + Base.last(a::AbstractGradedUnitRange) = last(unlabel_blocks(a)) # TODO: Use `TypeParameterAccessors`. Base.eltype(::Type{<:GradedUnitRange{T}}) where {T} = T +#= function labelled_blocks(a::BlockedOneTo, labels) # TODO: Use `blocklasts(a)`? That might # cause a recursive loop. @@ -132,53 +161,19 @@ function labelled_blocks(a::BlockedUnitRange, labels) # cause a recursive loop. return GradedUnitRange(labelled(a.first, labels[1]), labelled.(a.lasts, labels)) end +=# -function BlockArrays.findblock(a::AbstractGradedUnitRange, index::Integer) - return blockedunitrange_findblock(unlabel_blocks(a), index) -end - -function BlockSparseArrays.blockedunitrange_findblock( - a::AbstractGradedUnitRange, index::Integer -) - return blockedunitrange_findblock(unlabel_blocks(a), index) -end - -function BlockSparseArrays.blockedunitrange_findblockindex( - a::AbstractGradedUnitRange, index::Integer -) - return blockedunitrange_findblockindex(unlabel_blocks(a), index) -end - -function BlockArrays.findblockindex(a::AbstractGradedUnitRange, index::Integer) - return blockedunitrange_findblockindex(unlabel_blocks(a), index) -end - -## Block label interface - -# Internal function -function get_label(a::AbstractUnitRange, index::Block{1}) - return label(blocklasts(a)[Int(index)]) -end - -# Internal function -function get_label(a::AbstractUnitRange, index::Integer) - return get_label(a, blockedunitrange_findblock(a, index)) -end - -function blocklabels(a::AbstractBlockVector) - return map(BlockRange(a)) do block - return label(@view(a[block])) - end +function Base.first(a::AbstractGradedUnitRange) + return first(unlabel_blocks(a)) end -function blocklabels(a::AbstractBlockedUnitRange) - return map(sr -> only(blocklabels(sr)), sectors(a)) -end +Base.iterate(a::AbstractGradedUnitRange) = iterate(unlabel_blocks(a)) +Base.iterate(a::AbstractGradedUnitRange, i) = iterate(unlabel_blocks(a), i) -## BlockedUnitRange interface +# BlockArrays interface -function Base.axes(ga::AbstractGradedUnitRange) - return (GradedUnitRange(sectors(ga), blockedrange(blocklengths(ga))),) +function BlockArrays.findblock(a::AbstractGradedUnitRange, index::Integer) + return blockedunitrange_findblock(unlabel_blocks(a), index) end function gradedunitrange_blockfirsts(a::AbstractGradedUnitRange) @@ -196,22 +191,36 @@ function BlockArrays.blocklengths(a::AbstractGradedUnitRange) return blocklengths(unlabel_blocks(a)) end -function Base.first(a::AbstractGradedUnitRange) - return first(unlabel_blocks(a)) +# BlockSparseArrays interface + +function BlockSparseArrays.blockedunitrange_findblock( + a::AbstractGradedUnitRange, index::Integer +) + return blockedunitrange_findblock(unlabel_blocks(a), index) end -Base.iterate(a::AbstractGradedUnitRange) = iterate(unlabel_blocks(a)) -Base.iterate(a::AbstractGradedUnitRange, i) = iterate(unlabel_blocks(a), i) +function BlockSparseArrays.blockedunitrange_findblockindex( + a::AbstractGradedUnitRange, index::Integer +) + return blockedunitrange_findblockindex(unlabel_blocks(a), index) +end -function firstblockindices(a::AbstractGradedUnitRange) - return labelled.(firstblockindices(unlabel_blocks(a)), blocklabels(a)) +function BlockArrays.findblockindex(a::AbstractGradedUnitRange, index::Integer) + return blockedunitrange_findblockindex(unlabel_blocks(a), index) end +## BlockedUnitRange interface + +# TBD remove +#function firstblockindices(a::AbstractGradedUnitRange) +# return labelled.(firstblockindices(unlabel_blocks(a)), blocklabels(a)) +#end + function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, index::Block{1} ) - sr = sectors(a)[Int(index)] - return sectorunitrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) + sr = sector_axes(a)[Int(index)] + return sectorrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) end function BlockSparseArrays.blockedunitrange_getindices( @@ -252,13 +261,6 @@ function BlockSparseArrays.blockedunitrange_getindices( return mortar(blocks, length.(blocks)) end -# The block labels of the corresponding slice. -function blocklabels(a::AbstractUnitRange, indices) - return map(_blocks(a, indices)) do block - return label(a[block]) - end -end - function BlockSparseArrays.blockedunitrange_getindices( ga::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} ) @@ -405,9 +407,3 @@ function BlockSparseArrays.blockedunitrange_getindices( # if `a isa `GradedUnitRange`, for example. return mortar(blks, labelled_length.(blks)) end - -map_blocklabels(::Any, a::AbstractUnitRange) = a -function map_blocklabels(f, g::AbstractGradedUnitRange) - # use labelled_blocks to preserve GradedUnitRange - return GradedUnitRange(map_blocklabels.(f, sectors(g)), unlabel_blocks(g)) -end diff --git a/src/GradedUnitRanges/sectorunitrange.jl b/src/GradedUnitRanges/sectorunitrange.jl index d4e3720..a44800e 100644 --- a/src/GradedUnitRanges/sectorunitrange.jl +++ b/src/GradedUnitRanges/sectorunitrange.jl @@ -26,19 +26,19 @@ const SectorOneTo{T,Sector,Range} = SectorUnitRange{T,Sector,Base.OneTo{T}} to_sector(x) = x -# sectorunitrange(SU2(1), 2:5) -function sectorunitrange(s, r::AbstractUnitRange, b::Bool=false) +# sectorrange(SU2(1), 2:5) +function sectorrange(s, r::AbstractUnitRange, b::Bool=false) return SectorUnitRange(to_sector(s), r, b) end -# sectorunitrange(SU2(1), 1) -function sectorunitrange(s, m::Integer, b::Bool=false) - return sectorunitrange(s, Base.oneto(m * length(s)), b) +# sectorrange(SU2(1), 1) +function sectorrange(s, m::Integer, b::Bool=false) + return sectorrange(s, Base.oneto(m * length(s)), b) end -# sectorunitrange(SU2(1) => 1) -function sectorunitrange(p::Pair, b::Bool=false) - return sectorunitrange(first(p), last(p), b) +# sectorrange(SU2(1) => 1) +function sectorrange(p::Pair, b::Bool=false) + return sectorrange(first(p), last(p), b) end # @@ -72,7 +72,7 @@ function Base.getindex(sr::SectorUnitRange, t::Tuple{Colon,<:AbstractUnitRange}) r = last(t) new_range = ((first(r) - 1) * length(nondual_sector(sr)) + 1):(last(r) * length(nondual_sector(sr))) - return sectorunitrange(nondual_sector(sr), full_range(sr)[new_range], isdual(sr)) + return sectorrange(nondual_sector(sr), full_range(sr)[new_range], isdual(sr)) end function Base.show(io::IO, sr::SectorUnitRange) @@ -98,17 +98,18 @@ end # TBD error for non-integer? sector_multiplicity(sr::SectorUnitRange) = length(sr) ÷ length(nondual_sector(sr)) +sector_multiplicities(sr::SectorUnitRange) = [sector_multiplicity(sr)] # TBD remove? function dual(sr::SectorUnitRange) - return sectorunitrange(nondual_sector(sr), full_range(sr), !isdual(sr)) + return sectorrange(nondual_sector(sr), full_range(sr), !isdual(sr)) end function flip(sr::SectorUnitRange) - return sectorunitrange(dual(nondual_sector(sr)), full_range(sr), !isdual(sr)) + return sectorrange(dual(nondual_sector(sr)), full_range(sr), !isdual(sr)) end function map_blocklabels(f, sr::SectorUnitRange) - return sectorunitrange(f(nondual_sector(sr)), full_range(sr), isdual(sr)) + return sectorrange(f(nondual_sector(sr)), full_range(sr), isdual(sr)) end sector_type(::Type{<:SectorUnitRange{T,Sector}}) where {T,Sector} = Sector diff --git a/src/SymmetrySectors/abstractsector.jl b/src/SymmetrySectors/abstractsector.jl index 72f41b7..8e9dc9b 100644 --- a/src/SymmetrySectors/abstractsector.jl +++ b/src/SymmetrySectors/abstractsector.jl @@ -2,8 +2,8 @@ # all fusion categories (Z{2}, SU2, Ising...) are subtypes of AbstractSector using BlockArrays: blocklengths -using ..GradedUnitRanges: GradedUnitRanges, blocklabels, gradedrange, sector_multiplicity -using TensorProducts: TensorProducts, ⊗, tensor_product +using ..GradedUnitRanges: GradedUnitRanges, blocklabels, gradedrange, sector_multiplicities +using TensorProducts: TensorProducts, ⊗ abstract type AbstractSector end @@ -46,7 +46,7 @@ function nsymbol(s1::AbstractSector, s2::AbstractSector, s3::AbstractSector) full_space = to_gradedrange(s1 ⊗ s2) i = findfirst(==(s3), blocklabels(full_space)) isnothing(i) && return 0 - return sector_multiplicity(full_space)[i] + return sector_multiplicities(full_space)[i] end # =============================== Fusion rule interface ================================== @@ -77,11 +77,11 @@ end # allow to fuse a Sector with a GradedUnitRange function TensorProducts.tensor_product(c::AbstractSector, g::AbstractUnitRange) - return tensor_product(to_gradedrange(c), g) + return to_gradedrange(c) ⊗ g end function TensorProducts.tensor_product(g::AbstractUnitRange, c::AbstractSector) - return tensor_product(g, to_gradedrange(c)) + return g ⊗ to_gradedrange(c) end # ================================ GradedUnitRanges interface ================================== diff --git a/test/test_gradedunitranges_sectorunitrange.jl b/test/test_gradedunitranges_sectorunitrange.jl index bcb3035..87fe01b 100644 --- a/test/test_gradedunitranges_sectorunitrange.jl +++ b/test/test_gradedunitranges_sectorunitrange.jl @@ -11,13 +11,14 @@ using GradedArrays.GradedUnitRanges: isdual, nondual_sector, sector_multiplicities, + sector_multiplicity, sector_type, - sectorunitrange, + sectorrange, space_isequal using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @testset "SectorUnitRange" begin - sr = sectorunitrange(SU((1, 0)), 2) + sr = sectorrange(SU((1, 0)), 2) @test sr isa SectorUnitRange # accessors @@ -42,31 +43,31 @@ using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @test sr == sr @test space_isequal(sr, sr) - sr = sectorunitrange(SU((1, 0)) => 2) + sr = sectorrange(SU((1, 0)) => 2) @test sr isa SectorUnitRange @test nondual_sector(sr) == SU((1, 0)) @test full_range(sr) isa Base.OneTo @test full_range(sr) == 1:6 @test !isdual(sr) - sr = sectorunitrange(SU((1, 0)) => 2, true) + sr = sectorrange(SU((1, 0)) => 2, true) @test sr isa SectorUnitRange @test nondual_sector(sr) == SU((1, 0)) @test full_range(sr) isa Base.OneTo @test full_range(sr) == 1:6 @test isdual(sr) - sr = sectorunitrange(SU((1, 0)), 4:10, true) + sr = sectorrange(SU((1, 0)), 4:10, true) @test sr isa SectorUnitRange @test nondual_sector(sr) == SU((1, 0)) @test full_range(sr) isa UnitRange @test full_range(sr) == 4:10 @test isdual(sr) - sr = sectorunitrange(SU((1, 0)), 2) - @test !space_isequal(sr, sectorunitrange(SU((1, 1)), 2)) - @test !space_isequal(sr, sectorunitrange(SU((1, 0)), 2:7)) - @test !space_isequal(sr, sectorunitrange(SU((1, 1)), 2, true)) + sr = sectorrange(SU((1, 0)), 2) + @test !space_isequal(sr, sectorrange(SU((1, 1)), 2)) + @test !space_isequal(sr, sectorrange(SU((1, 0)), 2:7)) + @test !space_isequal(sr, sectorrange(SU((1, 1)), 2, true)) sr2 = copy(sr) @test sr2 isa SectorUnitRange @@ -90,15 +91,16 @@ using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @test sector_type(sr) === SU{3,2} @test sector_type(typeof(sr)) === SU{3,2} @test blocklabels(sr) == [SU((1, 0))] + @test sector_multipliciy(sr) == 2 @test sector_multiplicities(sr) == [2] srd = dual(sr) @test nondual_sector(srd) == SU((1, 0)) - @test space_isequal(srd, sectorunitrange(SU((1, 0)), 2, true)) + @test space_isequal(srd, sectorrange(SU((1, 0)), 2, true)) srf = flip(sr) @test nondual_sector(srf) == SU((1, 1)) - @test space_isequal(srf, sectorunitrange(SU((1, 1)), 2, true)) + @test space_isequal(srf, sectorrange(SU((1, 1)), 2, true)) # getindex @test_throws BoundsError sr[0] @@ -112,8 +114,8 @@ using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension sr2 = sr[(:, 2)] @test sr2 isa SectorUnitRange - @test space_isequal(sr2, sectorunitrange(SU((1, 0)), 4:6)) + @test space_isequal(sr2, sectorrange(SU((1, 0)), 4:6)) sr3 = sr[(:, 1:2)] @test sr3 isa SectorUnitRange - @test space_isequal(sr3, sectorunitrange(SU((1, 0)), 1:6)) + @test space_isequal(sr3, sectorrange(SU((1, 0)), 1:6)) end diff --git a/test/test_gradedunitranges_tensor_product.jl b/test/test_gradedunitranges_tensor_product.jl index 10ba4ed..fde681d 100644 --- a/test/test_gradedunitranges_tensor_product.jl +++ b/test/test_gradedunitranges_tensor_product.jl @@ -9,7 +9,6 @@ using GradedArrays: gradedrange, space_isequal, isdual -using GradedArrays.LabelledNumbers: labelled_isequal using TensorProducts: OneToOne, tensor_product using Test: @test, @testset diff --git a/test/test_symmetrysectors_fusion_rules.jl b/test/test_symmetrysectors_fusion_rules.jl index 4d90d2b..2784886 100644 --- a/test/test_symmetrysectors_fusion_rules.jl +++ b/test/test_symmetrysectors_fusion_rules.jl @@ -1,6 +1,6 @@ using GradedArrays: dual, space_isequal, gradedrange, flip, unmerged_tensor_product using GradedArrays.SymmetrySectors: - Fib, Ising, O2, SU, SU2, TrivialSector, U1, Z, nsymbol, quantum_dimension, trivial + O2, SU, SU2, TrivialSector, U1, Z, nsymbol, quantum_dimension, trivial using TensorProducts: ⊗, tensor_product using Test: @test, @testset, @test_throws using TestExtras: @constinferred @@ -86,36 +86,8 @@ using TestExtras: @constinferred @test space_isequal(tensor_product(j2, j1), gradedrange([j2 => 1])) @test space_isequal(tensor_product(j2, j1, j1), gradedrange([j2 => 1])) end - - @testset "Fibonacci fusion rules" begin - ı = Fib("1") - τ = Fib("τ") - - @test space_isequal(ı ⊗ ı, gradedrange([ı => 1])) - @test space_isequal(ı ⊗ τ, gradedrange([τ => 1])) - @test space_isequal(τ ⊗ ı, gradedrange([τ => 1])) - @test space_isequal((@constinferred τ ⊗ τ), gradedrange([ı => 1, τ => 1])) - @test (@constinferred quantum_dimension(gradedrange([ı => 1, ı => 1]))) == 2.0 - end - - @testset "Ising fusion rules" begin - ı = Ising("1") - σ = Ising("σ") - ψ = Ising("ψ") - - @test space_isequal(ı ⊗ ı, gradedrange([ı => 1])) - @test space_isequal(ı ⊗ σ, gradedrange([σ => 1])) - @test space_isequal(σ ⊗ ı, gradedrange([σ => 1])) - @test space_isequal(ı ⊗ ψ, gradedrange([ψ => 1])) - @test space_isequal(ψ ⊗ ı, gradedrange([ψ => 1])) - @test space_isequal(σ ⊗ σ, gradedrange([ı => 1, ψ => 1])) - @test space_isequal(σ ⊗ ψ, gradedrange([σ => 1])) - @test space_isequal(ψ ⊗ σ, gradedrange([σ => 1])) - @test space_isequal(ψ ⊗ ψ, gradedrange([ı => 1])) - @test space_isequal((@constinferred ψ ⊗ ψ), gradedrange([ı => 1])) - @test (@constinferred quantum_dimension(σ ⊗ σ)) == 2.0 - end end + @testset "Gradedrange fusion rules" begin @testset "Trivial GradedUnitRange" begin g1 = gradedrange([U1(0) => 1]) diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index 72d17bd..2bcdf25 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -1,16 +1,5 @@ using GradedArrays.SymmetrySectors: - ×, - Fib, - Ising, - SectorProduct, - SU, - SU2, - TrivialSector, - U1, - Z, - quantum_dimension, - arguments, - trivial + ×, SectorProduct, SU, SU2, TrivialSector, U1, Z, quantum_dimension, arguments, trivial using GradedArrays: dual, space_isequal, gradedrange, sector_type using TensorProducts: ⊗ using Test: @test, @testset, @test_throws @@ -98,39 +87,6 @@ using TestExtras: @constinferred g = gradedrange([(SU2(0) × U1(0) × SU2(1//2)) => 1, (SU2(0) × U1(1) × SU2(1//2)) => 1]) @test (@constinferred quantum_dimension(g)) == 4 @test (@constinferred blocklengths(g)) == [2, 2] - - # NonGroupCategory - g_fib = gradedrange([(Fib("1") × Fib("1")) => 1]) - g_ising = gradedrange([(Ising("1") × Ising("1")) => 1]) - @test (@constinferred quantum_dimension((Fib("1") × Fib("1")))) == 1.0 - @test (@constinferred quantum_dimension(g_fib)) == 1.0 - @test (@constinferred quantum_dimension(g_ising)) == 1.0 - @test (@constinferred quantum_dimension((Ising("1") × Ising("1")))) == 1.0 - @test (@constinferred blocklengths(g_fib)) == [1.0] - @test (@constinferred blocklengths(g_ising)) == [1.0] - - @test (@constinferred quantum_dimension(U1(1) × Fib("1"))) == 1.0 - @test (@constinferred quantum_dimension(gradedrange([U1(1) × Fib("1") => 1]))) == 1.0 - - # mixed product Abelian / NonAbelian / NonGroup - g = gradedrange([ - (U1(2) × SU2(0) × Ising(1)) => 1, - (U1(2) × SU2(1) × Ising(1)) => 1, - (U1(2) × SU2(0) × Ising("ψ")) => 1, - (U1(2) × SU2(1) × Ising("ψ")) => 1, - ]) - @test (@constinferred quantum_dimension(g)) == 8.0 - @test (@constinferred blocklengths(g)) == [1.0, 3.0, 1.0, 3.0] - - ϕ = (1 + √5) / 2 - g = gradedrange([ - (Fib("1") × SU2(0) × U1(2)) => 1, - (Fib("1") × SU2(1) × U1(2)) => 1, - (Fib("τ") × SU2(0) × U1(2)) => 1, - (Fib("τ") × SU2(1) × U1(2)) => 1, - ]) - @test (@constinferred quantum_dimension(g)) == 4.0 + 4.0ϕ - @test (@constinferred blocklengths(g)) == [1.0, 3.0, 1.0ϕ, 3.0ϕ] end @testset "Fusion of Abelian products" begin From 3c6b76fc878c183b73b76878528da97a170de4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 17 Apr 2025 10:15:17 -0400 Subject: [PATCH 06/33] refactor package structure --- .../GradedArraysTensorAlgebraExt.jl | 6 +- src/GradedArrays.jl | 39 +++-- src/GradedUnitRanges/GradedUnitRanges.jl | 10 -- src/GradedUnitRanges/gradedunitrangedual.jl | 165 ------------------ src/GradedUnitRanges/labelledunitrangedual.jl | 67 ------- src/LabelledNumbers/LabelledNumbers.jl | 12 -- .../LabelledNumbersBlockArraysExt.jl | 21 --- src/LabelledNumbers/labelled_interface.jl | 62 ------- src/LabelledNumbers/labelledarray.jl | 20 --- src/LabelledNumbers/labelledinteger.jl | 125 ------------- src/LabelledNumbers/labellednumber.jl | 41 ----- src/LabelledNumbers/labelledunitrange.jl | 63 ------- src/SymmetrySectors/SymmetrySectors.jl | 18 -- src/{SymmetrySectors => }/abstractsector.jl | 3 +- src/{GradedUnitRanges => }/fusion.jl | 10 -- src/gradedarray.jl | 1 - src/{GradedUnitRanges => }/gradedunitrange.jl | 2 +- .../dual.jl => gradedunitrange_interface.jl} | 0 .../namedtuple_operations.jl | 0 .../sector_definitions/fib.jl | 3 +- .../sector_definitions/ising.jl | 3 +- .../sector_definitions/o2.jl | 3 +- .../sector_definitions/su.jl | 5 +- .../sector_definitions/su2k.jl | 3 +- .../sector_definitions/trivial.jl | 4 +- .../sector_definitions/u1.jl | 4 +- .../sector_definitions/zn.jl | 4 +- src/{SymmetrySectors => }/sector_product.jl | 5 +- src/{GradedUnitRanges => }/sectorunitrange.jl | 0 src/{SymmetrySectors => }/symmetry_style.jl | 2 - test/test_exports.jl | 20 +++ test/test_gradedarray.jl | 11 +- test/test_gradedunitranges_basics.jl | 6 +- test/test_gradedunitranges_dual.jl | 8 - test/test_gradedunitranges_exports.jl | 6 - test/test_labellednumbers_basics.jl | 121 ------------- test/test_labellednumbers_blockarrays.jl | 15 -- test/test_symmetrysectors_fusion_rules.jl | 18 +- test/test_symmetrysectors_sector_product.jl | 18 +- test/test_symmetrysectors_simple_sectors.jl | 5 +- test/test_tensoralgebraext.jl | 3 +- 41 files changed, 101 insertions(+), 831 deletions(-) delete mode 100644 src/GradedUnitRanges/GradedUnitRanges.jl delete mode 100644 src/GradedUnitRanges/gradedunitrangedual.jl delete mode 100644 src/GradedUnitRanges/labelledunitrangedual.jl delete mode 100644 src/LabelledNumbers/LabelledNumbers.jl delete mode 100644 src/LabelledNumbers/LabelledNumbersBlockArraysExt.jl delete mode 100644 src/LabelledNumbers/labelled_interface.jl delete mode 100644 src/LabelledNumbers/labelledarray.jl delete mode 100644 src/LabelledNumbers/labelledinteger.jl delete mode 100644 src/LabelledNumbers/labellednumber.jl delete mode 100644 src/LabelledNumbers/labelledunitrange.jl delete mode 100644 src/SymmetrySectors/SymmetrySectors.jl rename src/{SymmetrySectors => }/abstractsector.jl (95%) rename src/{GradedUnitRanges => }/fusion.jl (93%) rename src/{GradedUnitRanges => }/gradedunitrange.jl (99%) rename src/{GradedUnitRanges/dual.jl => gradedunitrange_interface.jl} (100%) rename src/{SymmetrySectors => }/namedtuple_operations.jl (100%) rename src/{SymmetrySectors => }/sector_definitions/fib.jl (92%) rename src/{SymmetrySectors => }/sector_definitions/ising.jl (93%) rename src/{SymmetrySectors => }/sector_definitions/o2.jl (96%) rename src/{SymmetrySectors => }/sector_definitions/su.jl (97%) rename src/{SymmetrySectors => }/sector_definitions/su2k.jl (86%) rename src/{SymmetrySectors => }/sector_definitions/trivial.jl (93%) rename src/{SymmetrySectors => }/sector_definitions/u1.jl (87%) rename src/{SymmetrySectors => }/sector_definitions/zn.jl (80%) rename src/{SymmetrySectors => }/sector_product.jl (97%) rename src/{GradedUnitRanges => }/sectorunitrange.jl (100%) rename src/{SymmetrySectors => }/symmetry_style.jl (93%) create mode 100644 test/test_exports.jl delete mode 100644 test/test_gradedunitranges_exports.jl delete mode 100644 test/test_labellednumbers_basics.jl delete mode 100644 test/test_labellednumbers_blockarrays.jl diff --git a/ext/GradedArraysTensorAlgebraExt/GradedArraysTensorAlgebraExt.jl b/ext/GradedArraysTensorAlgebraExt/GradedArraysTensorAlgebraExt.jl index 36cf1b6..33a0bc2 100644 --- a/ext/GradedArraysTensorAlgebraExt/GradedArraysTensorAlgebraExt.jl +++ b/ext/GradedArraysTensorAlgebraExt/GradedArraysTensorAlgebraExt.jl @@ -2,15 +2,15 @@ module GradedArraysTensorAlgebraExt using BlockArrays: blocks using BlockSparseArrays: BlockSparseArray, blockreshape -using GradedArrays: GradedArray -using GradedArrays.GradedUnitRanges: +using GradedArrays: AbstractGradedUnitRange, + GradedArray, flip, invblockperm, sectormergesortperm, sectorsortperm, + trivial, unmerged_tensor_product -using GradedArrays.SymmetrySectors: trivial using TensorAlgebra: TensorAlgebra, AbstractBlockPermutation, diff --git a/src/GradedArrays.jl b/src/GradedArrays.jl index 0e46930..c0a0b86 100644 --- a/src/GradedArrays.jl +++ b/src/GradedArrays.jl @@ -1,29 +1,36 @@ module GradedArrays -include("LabelledNumbers/LabelledNumbers.jl") -using .LabelledNumbers: LabelledNumbers -include("GradedUnitRanges/GradedUnitRanges.jl") -include("SymmetrySectors/SymmetrySectors.jl") -include("GradedUnitRanges/fusion.jl") +include("gradedunitrange_interface.jl") +include("symmetry_style.jl") -# This makes the following names accessible -# as `GradedArrays.x`. -using .GradedUnitRanges: - GradedUnitRanges, - GradedOneTo, - GradedUnitRange, +include("sectorunitrange.jl") +include("gradedunitrange.jl") + +include("abstractsector.jl") +include("sector_definitions/fib.jl") +include("sector_definitions/ising.jl") +include("sector_definitions/o2.jl") +include("sector_definitions/trivial.jl") +include("sector_definitions/su.jl") +include("sector_definitions/su2k.jl") +include("sector_definitions/u1.jl") +include("sector_definitions/zn.jl") +include("namedtuple_operations.jl") +include("sector_product.jl") + +include("fusion.jl") +include("gradedarray.jl") + +export U1, + Z, blocklabels, dag, dual, flip, gradedrange, isdual, - map_blocklabels, - nondual, + sectorrange, sector_type, - sectorunitrange, space_isequal -using .SymmetrySectors: SymmetrySectors -include("gradedarray.jl") end diff --git a/src/GradedUnitRanges/GradedUnitRanges.jl b/src/GradedUnitRanges/GradedUnitRanges.jl deleted file mode 100644 index 1bee9da..0000000 --- a/src/GradedUnitRanges/GradedUnitRanges.jl +++ /dev/null @@ -1,10 +0,0 @@ -module GradedUnitRanges - -export gradedrange - -include("sectorunitrange.jl") -include("gradedunitrange.jl") -include("dual.jl") -#include("fusion.jl") - -end diff --git a/src/GradedUnitRanges/gradedunitrangedual.jl b/src/GradedUnitRanges/gradedunitrangedual.jl deleted file mode 100644 index f7d8209..0000000 --- a/src/GradedUnitRanges/gradedunitrangedual.jl +++ /dev/null @@ -1,165 +0,0 @@ -using BlockArrays: - BlockArrays, - BlockIndex, - BlockIndexRange, - BlockSlice, - BlockVector, - blockaxes, - blockfirsts, - combine_blockaxes, - findblock -using BlockSparseArrays: BlockSparseArrays, blockedunitrange_getindices -using ..LabelledNumbers: LabelledNumbers, LabelledUnitRange, label_type, unlabel - -struct GradedUnitRangeDual{ - T,BlockLasts,NondualUnitRange<:AbstractGradedUnitRange{T,BlockLasts} -} <: AbstractGradedUnitRange{T,BlockLasts} - nondual_unitrange::NondualUnitRange -end - -dual(a::AbstractGradedUnitRange) = GradedUnitRangeDual(a) -nondual(a::GradedUnitRangeDual) = a.nondual_unitrange -dual(a::GradedUnitRangeDual) = nondual(a) -flip(a::GradedUnitRangeDual) = dual(flip(nondual(a))) -isdual(::GradedUnitRangeDual) = true - -function nondual_type( - ::Type{<:GradedUnitRangeDual{<:Any,<:Any,NondualUnitRange}} -) where {NondualUnitRange} - return NondualUnitRange -end -dual_type(T::Type{<:GradedUnitRangeDual}) = nondual_type(T) -function dual_type(type::Type{<:AbstractGradedUnitRange{T,BlockLasts}}) where {T,BlockLasts} - return GradedUnitRangeDual{T,BlockLasts,type} -end -function LabelledNumbers.label_type(type::Type{<:GradedUnitRangeDual}) - # `dual_type` right now doesn't do anything but anticipates defining `SectorDual`. - return dual_type(label_type(nondual_type(type))) -end - -## TODO: Define this to instantiate a dual unit range. -## materialize_dual(a::GradedUnitRangeDual) = materialize_dual(nondual(a)) - -Base.first(a::GradedUnitRangeDual) = dual(first(nondual(a))) -Base.last(a::GradedUnitRangeDual) = dual(last(nondual(a))) -Base.step(a::GradedUnitRangeDual) = dual(step(nondual(a))) - -Base.view(a::GradedUnitRangeDual, index::Block{1}) = a[index] - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::AbstractUnitRange{<:Integer} -) - return dual(getindex(nondual(a), indices)) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::Integer -) - return dual(getindex(nondual(a), indices)) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::Block{1} -) - return dual(getindex(nondual(a), indices)) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::BlockRange -) - return dual(getindex(nondual(a), indices)) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::BlockIndexRange{1} -) - return dual(nondual(a)[indices]) -end - -# fix ambiguity -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::BlockRange{1,<:Tuple{AbstractUnitRange{Int}}} -) - return dual(getindex(nondual(a), indices)) -end - -function BlockArrays.blocklengths(a::GradedUnitRangeDual) - return dual.(blocklengths(nondual(a))) -end - -# TODO: Move this to a `BlockArraysExtensions` library. -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::Vector{<:BlockIndexRange{1}} -) - # dual v axes to stay consistent with other cases where axes(v) are used - return dual_axes(blockedunitrange_getindices(nondual(a), indices)) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, - indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, -) - # dual v axis to preserve dual information - # axes(v) will appear in axes(view(::BlockSparseArray, [Block(1)[1:1]])) - return dual_axes(blockedunitrange_getindices(nondual(a), indices)) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::AbstractVector{<:Union{Block{1},BlockIndexRange{1}}} -) - # dual v axis to preserve dual information - # axes(v) will appear in axes(view(::BlockSparseArray, [Block(1)])) - return dual_axes(blockedunitrange_getindices(nondual(a), indices)) -end - -# Fixes ambiguity error. -function BlockSparseArrays.blockedunitrange_getindices( - a::GradedUnitRangeDual, indices::AbstractBlockVector{<:Block{1}} -) - v = blockedunitrange_getindices(nondual(a), indices) - # v elements are not dualled by dual_axes due to different structure. - # take element dual here. - return dual_axes(dual.(v)) -end - -function dual_axes(v::BlockVector) - # dual both v elements and v axes - block_axes = dual.(axes(v)) - return mortar(dual.(blocks(v)), block_axes) -end - -Base.axes(a::GradedUnitRangeDual) = dual.(axes(nondual(a))) - -function BlockArrays.BlockSlice(b::Block, a::LabelledUnitRange) - return BlockSlice(b, unlabel(a)) -end - -function BlockArrays.BlockSlice(b::Block, r::GradedUnitRangeDual) - return BlockSlice(b, dual(r)) -end - -function Base.iterate(a::GradedUnitRangeDual, i) - i == last(a) && return nothing - return dual.(iterate(nondual(a), i)) -end - -BlockArrays.blockaxes(a::GradedUnitRangeDual) = blockaxes(nondual(a)) -BlockArrays.blockfirsts(a::GradedUnitRangeDual) = dual.(blockfirsts(nondual(a))) -BlockArrays.blocklasts(a::GradedUnitRangeDual) = dual.(blocklasts(nondual(a))) -function BlockArrays.findblock(a::GradedUnitRangeDual, index::Integer) - return findblock(nondual(a), index) -end - -blocklabels(a::GradedUnitRangeDual) = dual.(blocklabels(nondual(a))) - -function BlockArrays.combine_blockaxes(a1::GradedUnitRangeDual, a2::GradedUnitRangeDual) - return dual(combine_blockaxes(nondual(a1), nondual(a2))) -end - -function unlabel_blocks(a::GradedUnitRangeDual) - return unlabel_blocks(nondual(a)) -end - -function map_blocklabels(f, g::GradedUnitRangeDual) - return dual(map_blocklabels(f, dual(g))) -end diff --git a/src/GradedUnitRanges/labelledunitrangedual.jl b/src/GradedUnitRanges/labelledunitrangedual.jl deleted file mode 100644 index 48374cd..0000000 --- a/src/GradedUnitRanges/labelledunitrangedual.jl +++ /dev/null @@ -1,67 +0,0 @@ -using ..LabelledNumbers: - LabelledNumbers, IsLabelled, LabelledUnitRange, label, label_type, unlabel - -# LabelledUnitRangeDual is obtained by slicing a GradedUnitRangeDual with a block - -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) -isdual(::LabelledUnitRangeDual) = true -blocklabels(la::LabelledUnitRangeDual) = [label(la)] - -map_blocklabels(f, la::LabelledUnitRange) = labelled(unlabel(la), f(label(la))) -map_blocklabels(f, lad::LabelledUnitRangeDual) = dual(map_blocklabels(f, nondual(lad))) - -function nondual_type( - ::Type{<:LabelledUnitRangeDual{<:Any,NondualUnitRange}} -) where {NondualUnitRange} - return NondualUnitRange -end -dual_type(T::Type{<:LabelledUnitRangeDual}) = nondual_type(T) -function dual_type(T::Type{<:LabelledUnitRange}) - return LabelledUnitRangeDual{eltype(T),T} -end - -LabelledNumbers.label(a::LabelledUnitRangeDual) = dual(label(nondual(a))) -LabelledNumbers.unlabel(a::LabelledUnitRangeDual) = unlabel(nondual(a)) -LabelledNumbers.LabelledStyle(::LabelledUnitRangeDual) = IsLabelled() -function LabelledNumbers.label_type(type::Type{<:LabelledUnitRangeDual}) - # `dual_type` right now doesn't do anything but anticipates defining `SectorDual`. - return dual_type(label_type(nondual_type(type))) -end - -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.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)) - 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/src/LabelledNumbers/LabelledNumbers.jl b/src/LabelledNumbers/LabelledNumbers.jl deleted file mode 100644 index f12cf8a..0000000 --- a/src/LabelledNumbers/LabelledNumbers.jl +++ /dev/null @@ -1,12 +0,0 @@ -module LabelledNumbers - -using Random: AbstractRNG, default_rng - -include("labelled_interface.jl") -include("labellednumber.jl") -include("labelledinteger.jl") -include("labelledarray.jl") -include("labelledunitrange.jl") -include("LabelledNumbersBlockArraysExt.jl") - -end diff --git a/src/LabelledNumbers/LabelledNumbersBlockArraysExt.jl b/src/LabelledNumbers/LabelledNumbersBlockArraysExt.jl deleted file mode 100644 index ef434a8..0000000 --- a/src/LabelledNumbers/LabelledNumbersBlockArraysExt.jl +++ /dev/null @@ -1,21 +0,0 @@ -module LabelledNumbersBlockArraysExt - -using BlockArrays: BlockArrays, Block, BlockBoundsError -using ..LabelledNumbers: LabelledUnitRange, unlabel - -# Fixes ambiguity error with: -# ```julia -# getindex(::LabelledUnitRange, ::Any...) -# getindex(::AbstractArray{<:Any,N}, ::Block{N}) where {N} -# getindex(::AbstractArray, ::Block{1}, ::Any...) -# ``` -function Base.getindex(a::LabelledUnitRange, index::Block{1}) - @boundscheck index == Block(1) || throw(BlockBoundsError(a, index)) - return a -end - -BlockArrays.blockaxes(a::LabelledUnitRange) = BlockArrays.blockaxes(unlabel(a)) -BlockArrays.blockfirsts(a::LabelledUnitRange) = BlockArrays.blockfirsts(unlabel(a)) -BlockArrays.blocklasts(a::LabelledUnitRange) = BlockArrays.blocklasts(unlabel(a)) - -end diff --git a/src/LabelledNumbers/labelled_interface.jl b/src/LabelledNumbers/labelled_interface.jl deleted file mode 100644 index fda9ab0..0000000 --- a/src/LabelledNumbers/labelled_interface.jl +++ /dev/null @@ -1,62 +0,0 @@ -# Labelled object interface. -abstract type LabelledStyle end -struct IsLabelled <: LabelledStyle end -struct NotLabelled <: LabelledStyle end -LabelledStyle(::Type) = NotLabelled() -LabelledStyle(object) = LabelledStyle(typeof(object)) -islabelled(::IsLabelled) = true -islabelled(::NotLabelled) = false -islabelled(object) = islabelled(LabelledStyle(object)) -label(object) = error("This object does not have a label.") -# TODO: Use `TypeParameterAccessors`. -label_type(::Type) = error("No label type defined.") -label_type(object) = typeof(label(object)) -labelled(object, label) = error("Can't add a label to this object.") -# TODO: Turn into a trait function. -function set_label(object, label) - if islabelled(object) - object = unlabel(object) - end - return labelled(object, label) -end -unlabel(object) = object -unlabel_type(type::Type) = type -unlabel_type(object) = typeof(unlabel(object)) - -set_value(x, value) = labelled(value, label(x)) - -labelled_zero(x) = set_value(x, zero(unlabel(x))) -labelled_one(x) = one(unlabel(x)) -labelled_one(type::Type) = one(unlabel_type(type)) -labelled_oneunit(x) = set_value(x, one(x)) -# TODO: Implement this for types where the label is -# encoded in the type. -labelled_oneunit(type::Type) = error("Not implemented.") - -function labelled_binary_op(f, x, y) - return labelled_binary_op(f, LabelledStyle(x), x, LabelledStyle(y), y) -end -labelled_binary_op(f, ::LabelledStyle, x, ::LabelledStyle, y) = f(unlabel(x), unlabel(y)) - -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -labelled_minus(x) = set_value(x, -unlabel(x)) - -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -labelled_hash(x, h::UInt64) = hash(unlabel(x), h) - -for (fname, f) in [ - (:mul, :*), - (:add, :+), - (:minus, :-), - (:division, :/), - (:div, :÷), - (:isequal, :isequal), - (:isless, :isless), -] - labelled_fname = Symbol(:(labelled_), fname) - @eval begin - $labelled_fname(x, y) = labelled_binary_op($f, x, y) - end -end diff --git a/src/LabelledNumbers/labelledarray.jl b/src/LabelledNumbers/labelledarray.jl deleted file mode 100644 index f9dbedb..0000000 --- a/src/LabelledNumbers/labelledarray.jl +++ /dev/null @@ -1,20 +0,0 @@ -struct LabelledArray{T,N,Value<:AbstractArray{T,N},Label} <: - AbstractArray{LabelledInteger{T,Label},N} - value::Value - label::Label -end -LabelledStyle(::Type{<:LabelledArray}) = IsLabelled() -label(lobject::LabelledArray) = lobject.label -# TODO: Use `TypeParameterAccessors`. -label_type(::Type{<:LabelledArray{<:Any,Label}}) where {Label} = Label -labelled(object::AbstractArray, label) = LabelledArray(object, label) -unlabel(lobject::LabelledArray) = lobject.value -unlabel_type(::Type{<:LabelledArray{Value}}) where {Value} = Value - -for f in [:axes] - @eval Base.$f(a::LabelledArray, args...) = $f(unlabel(a), args...) -end - -for f in [:first, :getindex, :last, :length] - @eval Base.$f(a::LabelledArray, args...) = labelled($f(unlabel(a), args...), label(a)) -end diff --git a/src/LabelledNumbers/labelledinteger.jl b/src/LabelledNumbers/labelledinteger.jl deleted file mode 100644 index 6c13e21..0000000 --- a/src/LabelledNumbers/labelledinteger.jl +++ /dev/null @@ -1,125 +0,0 @@ -struct LabelledInteger{Value<:Integer,Label} <: Integer - value::Value - label::Label -end -LabelledStyle(::Type{<:LabelledInteger}) = IsLabelled() -# TODO: Define `set_value` and `set_label`? -label(lobject::LabelledInteger) = lobject.label -# TODO: Use `TypeParameterAccessors`. -label_type(::Type{<:LabelledInteger{<:Any,Label}}) where {Label} = Label -labelled(object::Integer, label) = LabelledInteger(object, label) -unlabel(lobject::LabelledInteger) = lobject.value -unlabel_type(::Type{<:LabelledInteger{Value}}) where {Value} = Value - -# When using as shapes of arrays. -# TODO: Preserve the label? For example: -# labelled(Base.to_shape(unlabel(x)), label(x)) -Base.to_shape(x::LabelledInteger) = Base.to_shape(unlabel(x)) - -# TODO: Define `labelled_convert`. -Base.convert(type::Type{<:Number}, x::LabelledInteger) = type(unlabel(x)) -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -function Base.convert(type::Type{<:LabelledInteger}, x::LabelledInteger) - return type(unlabel(x), label(x)) -end - -# Used by `Base.hash(::Integer)`. -# TODO: Define `labelled_trailing_zeros` to be used by other -# labelled number types. -Base.trailing_zeros(x::LabelledInteger) = trailing_zeros(unlabel(x)) - -# Used by `Base.hash(::Integer)`. -# TODO: Define `labelled_right_bit_shift` to be used by other -# labelled number types. -Base.:>>(x::LabelledInteger, y::Int) = >>(unlabel(x), y) - -Base.:(==)(x::LabelledInteger, y::LabelledInteger) = labelled_isequal(x, y) -Base.:(==)(x::LabelledInteger, y::Number) = labelled_isequal(x, y) -Base.:(==)(x::Number, y::LabelledInteger) = labelled_isequal(x, y) -Base.:<(x::LabelledInteger, y::LabelledInteger) = labelled_isless(x, y) -# This is only needed on older versions of Julia, like Julia 1.6. -# TODO: Delete once we drop support for Julia 1.6. -function Base.:<=(x::LabelledInteger, y::LabelledInteger) - return labelled_isless(x, y) || labelled_isequal(x, y) -end -# TODO: Define `labelled_colon`. -(::Base.Colon)(start::LabelledInteger, stop::LabelledInteger) = unlabel(start):unlabel(stop) -Base.zero(lobject::LabelledInteger) = labelled_zero(lobject) -Base.one(lobject::LabelledInteger) = labelled_one(lobject) -Base.one(type::Type{<:LabelledInteger}) = labelled_one(type) -Base.oneunit(lobject::LabelledInteger) = labelled_oneunit(lobject) -Base.oneunit(type::Type{<:LabelledInteger}) = oneunit(unlabel_type(type)) -Base.zero(type::Type{<:LabelledInteger}) = zero(unlabel_type(type)) - -Base.Int(x::LabelledInteger) = Int(unlabel(x)) - -Base.:+(x::LabelledInteger, y::LabelledInteger) = labelled_add(x, y) -Base.:+(x::LabelledInteger, y::Number) = labelled_add(x, y) -Base.:+(x::Number, y::LabelledInteger) = labelled_add(x, y) -# Fix ambiguity error with `+(::Integer, ::Integer)`. -Base.:+(x::LabelledInteger, y::Integer) = labelled_add(x, y) -Base.:+(x::Integer, y::LabelledInteger) = labelled_add(x, y) - -Base.:-(x::LabelledInteger, y::LabelledInteger) = labelled_minus(x, y) -Base.:-(x::LabelledInteger, y::Number) = labelled_minus(x, y) -Base.:-(x::Number, y::LabelledInteger) = labelled_minus(x, y) -# Fix ambiguity error with `-(::Integer, ::Integer)`. -Base.:-(x::LabelledInteger, y::Integer) = labelled_minus(x, y) -Base.:-(x::Integer, y::LabelledInteger) = labelled_minus(x, y) - -function Base.sub_with_overflow(x::LabelledInteger, y::LabelledInteger) - return labelled_binary_op(Base.sub_with_overflow, x, y) -end - -Base.:*(x::LabelledInteger, y::LabelledInteger) = labelled_mul(x, y) -Base.:*(x::LabelledInteger, y::Number) = labelled_mul(x, y) -Base.:*(x::Number, y::LabelledInteger) = labelled_mul(x, y) -# Fix ambiguity issue with `Base` `Integer`. -Base.:*(x::LabelledInteger, y::Integer) = labelled_mul(x, y) -# Fix ambiguity issue with `Base` `Integer`. -Base.:*(x::Integer, y::LabelledInteger) = labelled_mul(x, y) - -Base.:/(x::LabelledInteger, y::Number) = labelled_division(x, y) -Base.div(x::LabelledInteger, y::Number) = labelled_div(x, y) - -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -# TODO: Define in terms of a generic `labelled_minus` function. -# TODO: Define in terms of `set_value`? -Base.:-(x::LabelledInteger) = labelled_minus(x) - -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -Base.hash(x::LabelledInteger, h::UInt64) = labelled_hash(x, h) - -default_eltype() = Float64 -for f in [:rand, :randn] - @eval begin - function Base.$f( - rng::AbstractRNG, - elt::Type{<:Number}, - dims::Tuple{LabelledInteger,Vararg{LabelledInteger}}, - ) - return a = $f(rng, elt, unlabel.(dims)) - end - function Base.$f( - rng::AbstractRNG, - elt::Type{<:Number}, - dim1::LabelledInteger, - dims::Vararg{LabelledInteger}, - ) - return $f(rng, elt, (dim1, dims...)) - end - Base.$f(elt::Type{<:Number}, dims::Tuple{LabelledInteger,Vararg{LabelledInteger}}) = $f( - default_rng(), elt, dims - ) - Base.$f(elt::Type{<:Number}, dim1::LabelledInteger, dims::Vararg{LabelledInteger}) = $f( - elt, (dim1, dims...) - ) - Base.$f(dims::Tuple{LabelledInteger,Vararg{LabelledInteger}}) = $f( - default_eltype(), dims - ) - Base.$f(dim1::LabelledInteger, dims::Vararg{LabelledInteger}) = $f((dim1, dims...)) - end -end diff --git a/src/LabelledNumbers/labellednumber.jl b/src/LabelledNumbers/labellednumber.jl deleted file mode 100644 index 09a30a4..0000000 --- a/src/LabelledNumbers/labellednumber.jl +++ /dev/null @@ -1,41 +0,0 @@ -struct LabelledNumber{Value<:Number,Label} <: Number - value::Value - label::Label -end -LabelledStyle(::Type{<:LabelledNumber}) = IsLabelled() -label(lobject::LabelledNumber) = lobject.label -# TODO: Use `TypeParameterAccessors`. -label_type(::Type{<:LabelledNumber{<:Any,Label}}) where {Label} = Label -labelled(object::Number, label) = LabelledNumber(object, label) -unlabel(lobject::LabelledNumber) = lobject.value -unlabel_type(::Type{<:LabelledNumber{Value}}) where {Value} = Value - -# TODO: Define `labelled_convert`. -Base.convert(type::Type{<:Number}, x::LabelledNumber) = type(unlabel(x)) - -Base.:(==)(x::LabelledNumber, y::LabelledNumber) = labelled_isequal(x, y) -Base.:<(x::LabelledNumber, y::LabelledNumber) = labelled_isless(x < y) -# TODO: Define `labelled_colon`. -(::Base.Colon)(start::LabelledNumber, stop::LabelledNumber) = unlabel(start):unlabel(stop) -Base.zero(lobject::LabelledNumber) = labelled_zero(lobject) -Base.one(lobject::LabelledNumber) = labelled_one(lobject) -Base.one(type::Type{<:LabelledNumber}) = labelled_one(type) -Base.oneunit(lobject::LabelledNumber) = labelled_oneunit(lobject) -Base.oneunit(type::Type{<:LabelledNumber}) = error("Not implemented.") - -Base.:*(x::LabelledNumber, y::LabelledNumber) = labelled_mul(x, y) -Base.:*(x::LabelledNumber, y::Number) = labelled_mul(x, y) -Base.:*(x::Number, y::LabelledNumber) = labelled_mul(x, y) - -Base.:/(x::LabelledNumber, y::Number) = labelled_division(x, y) -Base.div(x::LabelledNumber, y::Number) = labelled_div(x, y) - -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -# TODO: Define in terms of a generic `labelled_minus` function. -# TODO: Define in terms of `set_value`? -Base.:-(x::LabelledNumber) = labelled_minus(x) - -# TODO: This is only needed for older Julia versions, like Julia 1.6. -# Delete once we drop support for older Julia versions. -Base.hash(x::LabelledNumber, h::UInt64) = labelled_hash(x, h) diff --git a/src/LabelledNumbers/labelledunitrange.jl b/src/LabelledNumbers/labelledunitrange.jl deleted file mode 100644 index 7ab4626..0000000 --- a/src/LabelledNumbers/labelledunitrange.jl +++ /dev/null @@ -1,63 +0,0 @@ -struct LabelledUnitRange{T,Value<:AbstractUnitRange{T},Label} <: - AbstractUnitRange{LabelledInteger{T,Label}} - value::Value - label::Label -end -LabelledStyle(::Type{<:LabelledUnitRange}) = IsLabelled() -label(lobject::LabelledUnitRange) = lobject.label -# TODO: Use `TypeParameterAccessors`. -label_type(::Type{<:LabelledUnitRange{<:Any,<:Any,Label}}) where {Label} = Label -labelled(object::AbstractUnitRange, label) = LabelledUnitRange(object, label) -unlabel(lobject::LabelledUnitRange) = lobject.value -unlabel_type(::Type{<:LabelledUnitRange{Value}}) where {Value} = Value - -# Used by `CartesianIndices` constructor. -# TODO: Maybe reconsider this definition? Also, this should preserve -# the label if possible, currently it drops the label. -function Base.AbstractUnitRange{T}(a::LabelledUnitRange) where {T} - return AbstractUnitRange{T}(unlabel(a)) -end - -# TODO: Is this a good definition? -Base.unitrange(a::LabelledUnitRange) = a - -for f in [:first, :getindex, :last, :length, :step] - @eval Base.$f(a::LabelledUnitRange, args...) = labelled($f(unlabel(a), args...), label(a)) -end - -labelled_getindex(a, index) = labelled(unlabel(a)[index], label(a)) - -# This is required in Julia 1.11 and above since -# the generic `axes(a::AbstractRange)` definition was removed -# and replace with a generic `axes(a)` definition that -# is written in terms of `Base.unchecked_oneto`, i.e.: -# ```julia -# map(Base.unchecked_oneto, size(A)) -# ``` -# which returns a `Base.OneTo` instead of a `LabelledUnitRange`. -Base.axes(a::LabelledUnitRange) = Base.oneto.(size(a)) - -# TODO: Delete this definition, this should output a `Base.OneTo`. -Base.OneTo(stop::LabelledInteger) = labelled(Base.OneTo(unlabel(stop)), label(stop)) - -# Fix ambiguity error with `AbstractRange` definition in `Base`. -Base.getindex(a::LabelledUnitRange, index::Integer) = labelled_getindex(a, index) -# Fix ambiguity error with `AbstractRange` definition in `Base`. -function Base.getindex(a::LabelledUnitRange, indices::AbstractUnitRange{<:Integer}) - return labelled_getindex(a, indices) -end - -function Base.iterate(a::LabelledUnitRange, 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::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 diff --git a/src/SymmetrySectors/SymmetrySectors.jl b/src/SymmetrySectors/SymmetrySectors.jl deleted file mode 100644 index 060eb20..0000000 --- a/src/SymmetrySectors/SymmetrySectors.jl +++ /dev/null @@ -1,18 +0,0 @@ -module SymmetrySectors - -export U1, Z, dual - -include("symmetry_style.jl") -include("abstractsector.jl") -include("sector_definitions/fib.jl") -include("sector_definitions/ising.jl") -include("sector_definitions/o2.jl") -include("sector_definitions/trivial.jl") -include("sector_definitions/su.jl") -include("sector_definitions/su2k.jl") -include("sector_definitions/u1.jl") -include("sector_definitions/zn.jl") -include("namedtuple_operations.jl") -include("sector_product.jl") - -end diff --git a/src/SymmetrySectors/abstractsector.jl b/src/abstractsector.jl similarity index 95% rename from src/SymmetrySectors/abstractsector.jl rename to src/abstractsector.jl index 8e9dc9b..05b380b 100644 --- a/src/SymmetrySectors/abstractsector.jl +++ b/src/abstractsector.jl @@ -2,7 +2,6 @@ # all fusion categories (Z{2}, SU2, Ising...) are subtypes of AbstractSector using BlockArrays: blocklengths -using ..GradedUnitRanges: GradedUnitRanges, blocklabels, gradedrange, sector_multiplicities using TensorProducts: TensorProducts, ⊗ abstract type AbstractSector end @@ -85,4 +84,4 @@ function TensorProducts.tensor_product(g::AbstractUnitRange, c::AbstractSector) end # ================================ GradedUnitRanges interface ================================== -GradedUnitRanges.sector_type(S::Type{<:AbstractSector}) = S +sector_type(S::Type{<:AbstractSector}) = S diff --git a/src/GradedUnitRanges/fusion.jl b/src/fusion.jl similarity index 93% rename from src/GradedUnitRanges/fusion.jl rename to src/fusion.jl index ce2c750..663cfe9 100644 --- a/src/GradedUnitRanges/fusion.jl +++ b/src/fusion.jl @@ -2,16 +2,6 @@ using BlockArrays: Block, blocklengths, blocks using SplitApplyCombine: groupcount using TensorProducts: TensorProducts, ⊗, OneToOne -using ..GradedUnitRanges: - SectorUnitRange, - AbstractGradedUnitRange, - nondual_sector, - axis_cat, - sector_axes, - sector_multiplicities, - sector_multiplicity -using ..SymmetrySectors: to_gradedrange - flip_dual(r::AbstractUnitRange) = isdual(r) ? flip(r) : r # TensorProducts interface diff --git a/src/gradedarray.jl b/src/gradedarray.jl index 918b528..11a283c 100644 --- a/src/gradedarray.jl +++ b/src/gradedarray.jl @@ -7,7 +7,6 @@ using BlockSparseArrays: BlockSparseMatrix, BlockSparseVector, blocktype -using ..GradedUnitRanges: AbstractGradedUnitRange, dual using LinearAlgebra: Adjoint using TypeParameterAccessors: similartype, unwrap_array_type diff --git a/src/GradedUnitRanges/gradedunitrange.jl b/src/gradedunitrange.jl similarity index 99% rename from src/GradedUnitRanges/gradedunitrange.jl rename to src/gradedunitrange.jl index 60dcef2..02cc017 100644 --- a/src/GradedUnitRanges/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -132,7 +132,7 @@ function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} end function Base.axes(ga::AbstractGradedUnitRange) - return (GradedUnitRange(sectors(ga), blockedrange(blocklengths(ga))),) + return (GradedUnitRange(sector_axes(ga), blockedrange(blocklengths(ga))),) end function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) diff --git a/src/GradedUnitRanges/dual.jl b/src/gradedunitrange_interface.jl similarity index 100% rename from src/GradedUnitRanges/dual.jl rename to src/gradedunitrange_interface.jl diff --git a/src/SymmetrySectors/namedtuple_operations.jl b/src/namedtuple_operations.jl similarity index 100% rename from src/SymmetrySectors/namedtuple_operations.jl rename to src/namedtuple_operations.jl diff --git a/src/SymmetrySectors/sector_definitions/fib.jl b/src/sector_definitions/fib.jl similarity index 92% rename from src/SymmetrySectors/sector_definitions/fib.jl rename to src/sector_definitions/fib.jl index bc90174..845723d 100644 --- a/src/SymmetrySectors/sector_definitions/fib.jl +++ b/src/sector_definitions/fib.jl @@ -3,7 +3,6 @@ # # (same fusion rules as subcategory {0,1} of su2{3}) # -using ..GradedUnitRanges: GradedUnitRanges struct Fib <: AbstractSector l::Int @@ -21,7 +20,7 @@ end SymmetryStyle(::Type{Fib}) = NotAbelianStyle() -GradedUnitRanges.dual(f::Fib) = f +dual(f::Fib) = f sector_label(f::Fib) = f.l diff --git a/src/SymmetrySectors/sector_definitions/ising.jl b/src/sector_definitions/ising.jl similarity index 93% rename from src/SymmetrySectors/sector_definitions/ising.jl rename to src/sector_definitions/ising.jl index e8b79c6..1377209 100644 --- a/src/SymmetrySectors/sector_definitions/ising.jl +++ b/src/sector_definitions/ising.jl @@ -5,7 +5,6 @@ # using HalfIntegers: Half, twice -using ..GradedUnitRanges: GradedUnitRanges struct Ising <: AbstractSector l::Half{Int} @@ -21,7 +20,7 @@ end SymmetryStyle(::Type{Ising}) = NotAbelianStyle() -GradedUnitRanges.dual(i::Ising) = i +dual(i::Ising) = i sector_label(i::Ising) = i.l diff --git a/src/SymmetrySectors/sector_definitions/o2.jl b/src/sector_definitions/o2.jl similarity index 96% rename from src/SymmetrySectors/sector_definitions/o2.jl rename to src/sector_definitions/o2.jl index 34e12a2..23038c9 100644 --- a/src/SymmetrySectors/sector_definitions/o2.jl +++ b/src/sector_definitions/o2.jl @@ -10,7 +10,6 @@ # using HalfIntegers: Half, HalfInteger -using ..GradedUnitRanges: GradedUnitRanges # here we use only one half-integer as label: # - l=0 for trivial @@ -36,7 +35,7 @@ iszero_odd(l::HalfInteger) = l == sector_label(zero_odd(O2)) quantum_dimension(::NotAbelianStyle, s::O2) = 2 - is_zero_even_or_odd(s) -GradedUnitRanges.dual(s::O2) = s +dual(s::O2) = s function Base.show(io::IO, s::O2) if iszero_odd(s) diff --git a/src/SymmetrySectors/sector_definitions/su.jl b/src/sector_definitions/su.jl similarity index 97% rename from src/SymmetrySectors/sector_definitions/su.jl rename to src/sector_definitions/su.jl index 83d95b4..9794de8 100644 --- a/src/SymmetrySectors/sector_definitions/su.jl +++ b/src/sector_definitions/su.jl @@ -3,7 +3,6 @@ # using HalfIntegers: HalfInteger, half, twice -using ...GradedUnitRanges: GradedUnitRanges struct SU{N,M} <: AbstractSector # l is the first row of the @@ -43,7 +42,7 @@ function quantum_dimension(::NotAbelianStyle, s::SU) return Int(d) end -function GradedUnitRanges.dual(s::SU) +function dual(s::SU) l = sector_label(s) nl = reverse(cumsum((l[begin:(end - 1)] .- l[(begin + 1):end]..., l[end]))) return typeof(s)(nl) @@ -87,7 +86,7 @@ end # optimize implementation quantum_dimension(s::SU{2}) = sector_label(s)[1] + 1 -GradedUnitRanges.dual(s::SU{2}) = s +dual(s::SU{2}) = s function label_fusion_rule(::Type{<:SU{2}}, s1, s2) irreps = [SU{2}((i,)) for i in (abs(s1[1] - s2[1])):2:(s1[1] + s2[1])] diff --git a/src/SymmetrySectors/sector_definitions/su2k.jl b/src/sector_definitions/su2k.jl similarity index 86% rename from src/SymmetrySectors/sector_definitions/su2k.jl rename to src/sector_definitions/su2k.jl index 3e10ef5..7eca4e1 100644 --- a/src/SymmetrySectors/sector_definitions/su2k.jl +++ b/src/sector_definitions/su2k.jl @@ -3,7 +3,6 @@ # using HalfIntegers: Half -using ...GradedUnitRanges: GradedUnitRanges struct su2{k} <: AbstractSector j::Half{Int} @@ -11,7 +10,7 @@ end SymmetryStyle(::Type{<:su2}) = NotAbelianStyle() -GradedUnitRanges.dual(s::su2) = s +dual(s::su2) = s sector_label(s::su2) = s.j diff --git a/src/SymmetrySectors/sector_definitions/trivial.jl b/src/sector_definitions/trivial.jl similarity index 93% rename from src/SymmetrySectors/sector_definitions/trivial.jl rename to src/sector_definitions/trivial.jl index 78af09d..a711ed2 100644 --- a/src/SymmetrySectors/sector_definitions/trivial.jl +++ b/src/sector_definitions/trivial.jl @@ -3,8 +3,6 @@ # acts as a trivial sector for any AbstractSector # -using ...GradedUnitRanges: GradedUnitRanges - # Trivial is special as it does not have a label struct TrivialSector <: AbstractSector end @@ -12,7 +10,7 @@ SymmetryStyle(::Type{TrivialSector}) = AbelianStyle() trivial(::Type{TrivialSector}) = TrivialSector() -GradedUnitRanges.dual(::TrivialSector) = TrivialSector() +dual(::TrivialSector) = TrivialSector() # TrivialSector acts as trivial on any AbstractSector function fusion_rule(::NotAbelianStyle, ::TrivialSector, c::AbstractSector) diff --git a/src/SymmetrySectors/sector_definitions/u1.jl b/src/sector_definitions/u1.jl similarity index 87% rename from src/SymmetrySectors/sector_definitions/u1.jl rename to src/sector_definitions/u1.jl index 5e030e4..c0d3adb 100644 --- a/src/SymmetrySectors/sector_definitions/u1.jl +++ b/src/sector_definitions/u1.jl @@ -2,8 +2,6 @@ # U₁ group (circle group, or particle number, total Sz etc.) # -using ...GradedUnitRanges: GradedUnitRanges - # Parametric type to allow both integer label as well as # HalfInteger for easy conversion to/from SU(2) struct U1{T} <: AbstractSector @@ -14,7 +12,7 @@ SymmetryStyle(::Type{<:U1}) = AbelianStyle() sector_label(u::U1) = u.n set_sector_label(s::U1, sector_label) = typeof(s)(sector_label) -GradedUnitRanges.dual(s::U1) = set_sector_label(s, -sector_label(s)) +dual(s::U1) = set_sector_label(s, -sector_label(s)) trivial(::Type{U1}) = trivial(U1{Int}) trivial(::Type{U1{T}}) where {T} = U1(zero(T)) diff --git a/src/SymmetrySectors/sector_definitions/zn.jl b/src/sector_definitions/zn.jl similarity index 80% rename from src/SymmetrySectors/sector_definitions/zn.jl rename to src/sector_definitions/zn.jl index d3446a0..3b26085 100644 --- a/src/SymmetrySectors/sector_definitions/zn.jl +++ b/src/sector_definitions/zn.jl @@ -2,8 +2,6 @@ # Cyclic group Zₙ # -using ...GradedUnitRanges: GradedUnitRanges - struct Z{N} <: AbstractSector m::Int Z{N}(m) where {N} = new{N}(mod(m, N)) @@ -16,7 +14,7 @@ SymmetryStyle(::Type{<:Z}) = AbelianStyle() sector_label(c::Z) = c.m set_sector_label(s::Z, sector_label) = typeof(s)(sector_label) -GradedUnitRanges.dual(s::Z) = set_sector_label(s, -sector_label(s)) +dual(s::Z) = set_sector_label(s, -sector_label(s)) trivial(sector_type::Type{<:Z}) = sector_type(0) diff --git a/src/SymmetrySectors/sector_product.jl b/src/sector_product.jl similarity index 97% rename from src/SymmetrySectors/sector_product.jl rename to src/sector_product.jl index 7c27746..5c3b70d 100644 --- a/src/SymmetrySectors/sector_product.jl +++ b/src/sector_product.jl @@ -2,7 +2,6 @@ # e.g. U(1)×U(1), U(1)×SU2(2)×SU(3) using BlockArrays: blocklengths -using ..GradedUnitRanges: GradedUnitRanges, dual, map_blocklabels # ===================================== Definition ======================================= struct SectorProduct{Sectors} <: AbstractSector @@ -14,7 +13,7 @@ SectorProduct(c::SectorProduct) = _SectorProduct(arguments(c)) arguments(s::SectorProduct) = s.arguments -GradedUnitRanges.to_sector(nt::NamedTuple) = SectorProduct(nt) +to_sector(nt::NamedTuple) = SectorProduct(nt) # ================================= Sectors interface ==================================== function SymmetryStyle(T::Type{<:SectorProduct}) @@ -26,7 +25,7 @@ function quantum_dimension(::NotAbelianStyle, s::SectorProduct) end # use map instead of broadcast to support both Tuple and NamedTuple -GradedUnitRanges.dual(s::SectorProduct) = SectorProduct(map(dual, arguments(s))) +dual(s::SectorProduct) = SectorProduct(map(dual, arguments(s))) function trivial(type::Type{<:SectorProduct}) return SectorProduct(arguments_trivial(arguments_type(type))) diff --git a/src/GradedUnitRanges/sectorunitrange.jl b/src/sectorunitrange.jl similarity index 100% rename from src/GradedUnitRanges/sectorunitrange.jl rename to src/sectorunitrange.jl diff --git a/src/SymmetrySectors/symmetry_style.jl b/src/symmetry_style.jl similarity index 93% rename from src/SymmetrySectors/symmetry_style.jl rename to src/symmetry_style.jl index 275387e..cfb022e 100644 --- a/src/SymmetrySectors/symmetry_style.jl +++ b/src/symmetry_style.jl @@ -1,8 +1,6 @@ # This file defines SymmetryStyle, a trait to distinguish abelian groups, non-abelian groups # and non-group fusion categories. -using ..GradedUnitRanges: sector_type - abstract type SymmetryStyle end struct AbelianStyle <: SymmetryStyle end diff --git a/test/test_exports.jl b/test/test_exports.jl new file mode 100644 index 0000000..b0bcc6c --- /dev/null +++ b/test/test_exports.jl @@ -0,0 +1,20 @@ +using GradedArrays: GradedArrays + +using Test: @test, @testset +@testset "Test exports" begin + exports = [ + :GradedArrays, + :U1, + :Z, + :blocklabels, + :dag, + :dual, + :flip, + :gradedrange, + :isdual, + :sectorrange, + :sector_type, + :space_isequal, + ] + @test issetequal(names(GradedArrays), exports) +end diff --git a/test/test_gradedarray.jl b/test/test_gradedarray.jl index f23d095..e563d50 100644 --- a/test/test_gradedarray.jl +++ b/test/test_gradedarray.jl @@ -2,18 +2,17 @@ using BlockArrays: AbstractBlockArray, Block, BlockedOneTo, blockedrange, blocklengths, blocksize using BlockSparseArrays: BlockSparseArray, BlockSparseMatrix, BlockSparseVector, blockstoredlength -using GradedArrays: GradedArray, GradedMatrix, GradedVector -using GradedArrays.GradedUnitRanges: - GradedUnitRanges, +using GradedArrays: + GradedArray, + GradedMatrix, + GradedVector, GradedOneTo, GradedUnitRange, - GradedUnitRangeDual, + U1, dag, dual, gradedrange, isdual -using GradedArrays.LabelledNumbers: label -using GradedArrays.SymmetrySectors: U1 using SparseArraysBase: storedlength using LinearAlgebra: adjoint using Random: randn! diff --git a/test/test_gradedunitranges_basics.jl b/test/test_gradedunitranges_basics.jl index 64cc512..cc29448 100644 --- a/test/test_gradedunitranges_basics.jl +++ b/test/test_gradedunitranges_basics.jl @@ -19,7 +19,7 @@ using GradedArrays: gradedrange, nondual_sector, sector_type, - sectorunitrange, + sectorrange, space_isequal using Test: @test, @test_broken, @testset @@ -58,7 +58,7 @@ using Test: @test, @test_broken, @testset @test nondual_sector(blocks(a)[2]) == "y" @test a[Block(2)] isa SectorUnitRange - @test space_isequal(a[Block(2)], sectorunitrange("y", 3:5)) + @test space_isequal(a[Block(2)], sectorrange("y", 3:5)) @test a[4] == 4 @test blocklengths(a) == [2, 3] @@ -91,7 +91,7 @@ using Test: @test, @test_broken, @testset @test a == 1:2 @test blocklength(a) == 1 @test a isa SectorUnitRange - @test space_isequal(a, sectorunitrange("x" => 2)) + @test space_isequal(a, sectorrange("x" => 2)) g = gradedrange(["x" => 2, "y" => 3]) a = g[3:4] diff --git a/test/test_gradedunitranges_dual.jl b/test/test_gradedunitranges_dual.jl index 45b7ec3..ed0628c 100644 --- a/test/test_gradedunitranges_dual.jl +++ b/test/test_gradedunitranges_dual.jl @@ -14,24 +14,16 @@ using BlockArrays: combine_blockaxes using GradedArrays: AbstractGradedUnitRange, - GradedUnitRanges, - GradedUnitRangeDual, - LabelledUnitRangeDual, blocklabels, dag, dual, - dual_type, flip, gradedrange, isdual, - nondual, - nondual_type, sector_type, sectormergesortperm, sectorsortperm, space_isequal -using GradedArrays.LabelledNumbers: - LabelledInteger, LabelledUnitRange, label, label_type, labelled, labelled_isequal, unlabel using Test: @test, @test_broken, @testset using TensorProducts: OneToOne, tensor_product diff --git a/test/test_gradedunitranges_exports.jl b/test/test_gradedunitranges_exports.jl deleted file mode 100644 index ba18db2..0000000 --- a/test/test_gradedunitranges_exports.jl +++ /dev/null @@ -1,6 +0,0 @@ -using GradedArrays.GradedUnitRanges: GradedUnitRanges -using Test: @test, @testset -@testset "Test exports" begin - exports = [:GradedUnitRanges, :gradedrange] - @test issetequal(names(GradedUnitRanges), exports) -end diff --git a/test/test_labellednumbers_basics.jl b/test/test_labellednumbers_basics.jl deleted file mode 100644 index abf967b..0000000 --- a/test/test_labellednumbers_basics.jl +++ /dev/null @@ -1,121 +0,0 @@ -using LinearAlgebra: norm -using GradedArrays.LabelledNumbers: - LabelledInteger, LabelledUnitRange, islabelled, label, label_type, labelled, unlabel -using Test: @test, @testset - -@testset "LabelledNumbers" begin - @testset "Labelled number ($n)" for n in (2, 2.0) - x = labelled(2, "x") - @test typeof(x) == LabelledInteger{Int,String} - @test islabelled(x) - @test x == 2 - @test label(x) == "x" - @test label_type(x) === String - @test label_type(typeof(x)) === String - @test unlabel(x) == 2 - @test !islabelled(unlabel(x)) - - @test labelled(1, "x") < labelled(2, "x") - @test !(labelled(2, "x") < labelled(2, "x")) - @test !(labelled(3, "x") < labelled(2, "x")) - - @test !(labelled(1, "x") > labelled(2, "x")) - @test !(labelled(2, "x") > labelled(2, "x")) - @test labelled(3, "x") > labelled(2, "x") - - @test labelled(1, "x") <= labelled(2, "x") - @test labelled(2, "x") <= labelled(2, "x") - @test !(labelled(3, "x") <= labelled(2, "x")) - - @test !(labelled(1, "x") >= labelled(2, "x")) - @test labelled(2, "x") >= labelled(2, "x") - @test labelled(3, "x") >= labelled(2, "x") - - @test x * 2 == 4 - @test !islabelled(x * 2) - @test 2 * x == 4 - @test !islabelled(2 * x) - @test x * x == 4 - @test !islabelled(x * x) - - @test x + 3 == 5 - @test !islabelled(x + 3) - @test 3 + x == 5 - @test !islabelled(3 + x) - @test x + x == 4 - @test !islabelled(x + x) - - @test x - 3 == -1 - @test !islabelled(x - 3) - @test 3 - x == 1 - @test !islabelled(3 - x) - @test x - x == 0 - @test !islabelled(x - x) - - @test x / 2 == 1 - @test x / 2 isa AbstractFloat - @test x / 2 isa Float64 - @test !islabelled(x / 2) - @test x ÷ 2 == 1 - @test x ÷ 2 isa Integer - @test x ÷ 2 isa Int - @test !islabelled(x ÷ 2) - @test -x == -2 - @test hash(x) == hash(2) - @test zero(x) == false - @test label(zero(x)) == "x" - @test one(x) == true - @test !islabelled(one(x)) - @test oneunit(x) == true - @test label(oneunit(x)) == "x" - @test islabelled(oneunit(x)) - @test one(typeof(x)) == true - @test !islabelled(one(typeof(x))) - end - @testset "randn" begin - d = labelled(2, "x") - - a = randn(Float32, d, d) - @test eltype(a) === Float32 - @test size(a) == (2, 2) - @test norm(a) > 0 - - a = rand(Float32, d, d) - @test eltype(a) === Float32 - @test size(a) == (2, 2) - @test norm(a) > 0 - - a = randn(d, d) - @test eltype(a) === Float64 - @test size(a) == (2, 2) - @test norm(a) > 0 - - a = rand(d, d) - @test eltype(a) === Float64 - @test size(a) == (2, 2) - @test norm(a) > 0 - end - @testset "Labelled array ($a)" for a in (collect(2:5), 2:5) - x = labelled(a, "x") - @test eltype(x) == LabelledInteger{Int,String} - @test x == 2:5 - @test label(x) == "x" - @test unlabel(x) == 2:5 - @test first(iterate(x, 3)) == 4 - @test label(first(iterate(x, 3))) == "x" - @test collect(x) == 2:5 - @test label.(collect(x)) == fill("x", 4) - @test x[2] == 3 - @test label(x[2]) == "x" - @test x[2:3] == 3:4 - @test label(x[2:3]) == "x" - @test x[[2, 4]] == [3, 5] - @test label(x[[2, 4]]) == "x" - - if x isa AbstractUnitRange - @test step(x) == true - @test islabelled(step(x)) - @test label(step(x)) == "x" - end - end -end diff --git a/test/test_labellednumbers_blockarrays.jl b/test/test_labellednumbers_blockarrays.jl deleted file mode 100644 index 9a79d4b..0000000 --- a/test/test_labellednumbers_blockarrays.jl +++ /dev/null @@ -1,15 +0,0 @@ -using GradedArrays.LabelledNumbers: LabelledUnitRange, label, label_type, labelled -using BlockArrays: Block, blockaxes, blocklength, blocklengths - -@testset "LabelledNumbersBlockArraysExt" begin - x = labelled(1:2, "x") - @test blockaxes(x) == (Block.(1:1),) - @test blocklength(x) == 1 - @test blocklengths(x) == [2] - a = x[Block(1)] - @test a == 1:2 - @test a isa LabelledUnitRange - @test label(a) == "x" - @test label_type(a) === String - @test label_type(typeof(a)) === String -end diff --git a/test/test_symmetrysectors_fusion_rules.jl b/test/test_symmetrysectors_fusion_rules.jl index 2784886..d4671d5 100644 --- a/test/test_symmetrysectors_fusion_rules.jl +++ b/test/test_symmetrysectors_fusion_rules.jl @@ -1,6 +1,18 @@ -using GradedArrays: dual, space_isequal, gradedrange, flip, unmerged_tensor_product -using GradedArrays.SymmetrySectors: - O2, SU, SU2, TrivialSector, U1, Z, nsymbol, quantum_dimension, trivial +using GradedArrays: + O2, + SU, + SU2, + TrivialSector, + U1, + Z, + dual, + space_isequal, + gradedrange, + flip, + nsymbol, + quantum_dimension, + trivial, + unmerged_tensor_product using TensorProducts: ⊗, tensor_product using Test: @test, @testset, @test_throws using TestExtras: @constinferred diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index 2bcdf25..950613a 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -1,6 +1,18 @@ -using GradedArrays.SymmetrySectors: - ×, SectorProduct, SU, SU2, TrivialSector, U1, Z, quantum_dimension, arguments, trivial -using GradedArrays: dual, space_isequal, gradedrange, sector_type +using GradedArrays: + ×, + SectorProduct, + SU, + SU2, + TrivialSector, + U1, + Z, + arguments, + dual, + gradedrange, + quantum_dimension, + sector_type, + space_isequal, + trivial using TensorProducts: ⊗ using Test: @test, @testset, @test_throws using TestExtras: @constinferred diff --git a/test/test_symmetrysectors_simple_sectors.jl b/test/test_symmetrysectors_simple_sectors.jl index 579d38b..adf6344 100644 --- a/test/test_symmetrysectors_simple_sectors.jl +++ b/test/test_symmetrysectors_simple_sectors.jl @@ -1,5 +1,4 @@ -using GradedArrays: dual, sector_type -using GradedArrays.SymmetrySectors: +using GradedArrays: Fib, Ising, O2, @@ -8,9 +7,11 @@ using GradedArrays.SymmetrySectors: TrivialSector, U1, Z, + dual, quantum_dimension, fundamental, istrivial, + sector_type, trivial using Test: @test, @testset, @test_throws using TestExtras: @constinferred diff --git a/test/test_tensoralgebraext.jl b/test/test_tensoralgebraext.jl index b2b1b29..2a8ceff 100644 --- a/test/test_tensoralgebraext.jl +++ b/test/test_tensoralgebraext.jl @@ -1,7 +1,6 @@ using BlockArrays: Block, blocksize using BlockSparseArrays: BlockSparseArray, BlockSparseMatrix -using GradedArrays: GradedOneTo, blocklabels, dual, flip, gradedrange, space_isequal -using GradedArrays.SymmetrySectors: U1 +using GradedArrays: GradedOneTo, U1, blocklabels, dual, flip, gradedrange, space_isequal using Random: randn! using TensorAlgebra: contract, matricize, unmatricize using Test: @test, @test_broken, @testset From 514c3d3cdd35a6ed8bb70fe633af677ebd37f9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 18 Apr 2025 12:07:13 -0400 Subject: [PATCH 07/33] pass most tests --- src/abstractsector.jl | 12 +- src/fusion.jl | 56 +++- src/gradedunitrange.jl | 67 ++-- src/gradedunitrange_interface.jl | 21 ++ src/sector_product.jl | 17 +- src/sectorunitrange.jl | 2 - ...nges_basics.jl => test_gradedunitrange.jl} | 113 ++++--- test/test_gradedunitrange_interface.jl | 38 +++ test/test_gradedunitranges_dual.jl | 299 ------------------ test/test_gradedunitranges_tensor_product.jl | 106 +++---- ...orunitrange.jl => test_sectorunitrange.jl} | 9 +- test/test_symmetrysectors_exports.jl | 6 - test/test_symmetrysectors_sector_product.jl | 196 +----------- 13 files changed, 274 insertions(+), 668 deletions(-) rename test/{test_gradedunitranges_basics.jl => test_gradedunitrange.jl} (59%) create mode 100644 test/test_gradedunitrange_interface.jl delete mode 100644 test/test_gradedunitranges_dual.jl rename test/{test_gradedunitranges_sectorunitrange.jl => test_sectorunitrange.jl} (94%) delete mode 100644 test/test_symmetrysectors_exports.jl diff --git a/src/abstractsector.jl b/src/abstractsector.jl index 05b380b..0fbf012 100644 --- a/src/abstractsector.jl +++ b/src/abstractsector.jl @@ -39,7 +39,8 @@ quantum_dimension(::AbelianStyle, ::AbstractSector) = 1 # convert to range to_gradedrange(c::AbstractSector) = gradedrange([c => 1]) -to_gradedrange(g::AbstractUnitRange) = g +to_gradedrange(sr::SectorUnitRange) = axis_cat([sr]) +to_gradedrange(g::AbstractGradedUnitRange) = g function nsymbol(s1::AbstractSector, s2::AbstractSector, s3::AbstractSector) full_space = to_gradedrange(s1 ⊗ s2) @@ -74,14 +75,5 @@ function TensorProducts.tensor_product(c1::AbstractSector, c2::AbstractSector) return fusion_rule(c1, c2) end -# allow to fuse a Sector with a GradedUnitRange -function TensorProducts.tensor_product(c::AbstractSector, g::AbstractUnitRange) - return to_gradedrange(c) ⊗ g -end - -function TensorProducts.tensor_product(g::AbstractUnitRange, c::AbstractSector) - return g ⊗ to_gradedrange(c) -end - # ================================ GradedUnitRanges interface ================================== sector_type(S::Type{<:AbstractSector}) = S diff --git a/src/fusion.jl b/src/fusion.jl index 663cfe9..281760b 100644 --- a/src/fusion.jl +++ b/src/fusion.jl @@ -1,34 +1,70 @@ using BlockArrays: Block, blocklengths, blocks using SplitApplyCombine: groupcount -using TensorProducts: TensorProducts, ⊗, OneToOne +using TensorProducts: TensorProducts, ⊗, OneToOne, tensor_product flip_dual(r::AbstractUnitRange) = isdual(r) ? flip(r) : r # TensorProducts interface function TensorProducts.tensor_product(sr1::SectorUnitRange, sr2::SectorUnitRange) - # TBD dispatch on SymmetryStyle and return either SectorUnitRange or GradedUnitRange? - s = to_gradedrange(nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2))) + return tensor_product(combine_styles(SymmetryStyle(sr1), SymmetryStyle(sr2)), sr1, sr2) +end + +function TensorProducts.tensor_product( + ::AbelianStyle, sr1::SectorUnitRange, sr2::SectorUnitRange +) + s = nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2)) + return sectorrange(s, sector_multiplicity(sr1) * sector_multiplicity(sr2)) +end + +function TensorProducts.tensor_product( + ::NotAbelianStyle, sr1::SectorUnitRange, sr2::SectorUnitRange +) + g0 = nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2)) return gradedrange( - blocklabels(s) .=> - sector_multiplicity(sr1) * sector_multiplicity(sr2) .* sector_multiplicities(s), + blocklabels(g0) .=> + sector_multiplicity(sr1) * sector_multiplicity(sr2) .* sector_multiplicities(g0), ) end +# allow to fuse a Sector with a GradedUnitRange +function TensorProducts.tensor_product( + s::Union{AbstractSector,SectorUnitRange}, g::AbstractGradedUnitRange +) + return to_gradedrange(s) ⊗ g +end + +function TensorProducts.tensor_product( + g::AbstractGradedUnitRange, s::Union{AbstractSector,SectorUnitRange} +) + return g ⊗ to_gradedrange(s) +end + +function TensorProducts.tensor_product(sr::SectorUnitRange, s::AbstractSector) + return sr ⊗ sectorrange(s, 1) +end + +function TensorProducts.tensor_product(s::AbstractSector, sr::SectorUnitRange) + return sectorrange(s, 1) ⊗ sr +end + +# unmerged_tensor_product is a private function needed in GradedArraysTensorAlgebraExt +# to get block permutation +# it is not aimed for generic use and does not support all tensor_product methods (no dispatch on SymmetryStyle) unmerged_tensor_product() = OneToOne() unmerged_tensor_product(a) = a unmerged_tensor_product(a, ::OneToOne) = a unmerged_tensor_product(::OneToOne, a) = a unmerged_tensor_product(::OneToOne, ::OneToOne) = OneToOne() -unmerged_tensor_product(a1, a2) = a1 ⊗ a2 function unmerged_tensor_product(a1, a2, as...) return unmerged_tensor_product(unmerged_tensor_product(a1, a2), as...) end +# default to tensor_product +unmerged_tensor_product(a1, a2) = a1 ⊗ a2 + function unmerged_tensor_product(a1::AbstractGradedUnitRange, a2::AbstractGradedUnitRange) - new_axes = map( - splat(⊗), Iterators.flatten((Iterators.product(sector_axes(a1), sector_axes(a2)),)) - ) - return axis_cat(reduce(vcat, sector_axes.(new_axes))) + new_axes = map(splat(⊗), Iterators.flatten((Iterators.product(blocks(a1), blocks(a2)),))) + return axis_cat(new_axes) end # convention: sort dual GradedUnitRange according to nondual blocks diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 02cc017..578556f 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -24,12 +24,10 @@ using BlockArrays: sortedunion using BlockSparseArrays: BlockSparseArrays, - _blocks, blockedunitrange_findblock, blockedunitrange_findblockindex, blockedunitrange_getindices using Compat: allequal -using FillArrays: Fill abstract type AbstractGradedUnitRange{T,BlockLasts} <: AbstractBlockedUnitRange{T,BlockLasts} end @@ -67,8 +65,6 @@ unlabel_blocks(g::GradedUnitRange) = g.full_range # TBD use full_range? sector_multiplicities(g::GradedUnitRange) = sector_multiplicity.(sector_axes(g)) -sector_type(x) = sector_type(typeof(x)) -sector_type(::Type) = error("Not implemented") sector_type(::Type{<:GradedUnitRange{<:Any,SUR}}) where {SUR} = sector_type(SUR) # @@ -80,6 +76,10 @@ function axis_cat(sectors::AbstractVector{<:SectorOneTo}) return GradedUnitRange(sectors, brange) end +function axis_cat(gaxes::AbstractVector{<:GradedOneTo}) + return axis_cat(mapreduce(sector_axes, vcat, gaxes)) +end + function gradedrange( lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; dual::Bool=false ) @@ -90,35 +90,13 @@ end # GradedUnitRange interface dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), unlabel_blocks(g)) -struct NoLabel end -blocklabels(r::AbstractUnitRange) = Fill(NoLabel(), blocklength(r)) +isdual(g::GradedUnitRange) = isdual(first(sector_axes(g))) # crash for empty. Should not be an issue. -function blocklabels(a::AbstractBlockVector) - return map(BlockRange(a)) do block - return label(@view(a[block])) - end -end - -function blocklabels(g::AbstractBlockedUnitRange) +function blocklabels(g::AbstractGradedUnitRange) nondual_blocklabels = nondual_sector.(sector_axes(g)) return isdual(g) ? dual.(nondual_blocklabels) : nondual_blocklabels end -# The block labels of the corresponding slice. -function blocklabels(a::AbstractUnitRange, indices) - return map(_blocks(a, indices)) do block - return label(a[block]) - end -end - -# == is just a range comparison that ignores labels. Need dedicated function to check equality. -function space_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) - return (isdual(a1) == isdual(a2)) && - blocklabels(a1) == blocklabels(a2) && - blockisequal(a1, a2) -end - -map_blocklabels(::Any, a::AbstractUnitRange) = a function map_blocklabels(f, g::AbstractGradedUnitRange) # use labelled_blocks to preserve GradedUnitRange return GradedUnitRange(map_blocklabels.(f, sector_axes(g)), unlabel_blocks(g)) @@ -135,6 +113,9 @@ function Base.axes(ga::AbstractGradedUnitRange) return (GradedUnitRange(sector_axes(ga), blockedrange(blocklengths(ga))),) end +# preserve axes in SubArray +Base.axes(S::Base.Slice{<:AbstractGradedUnitRange}) = (S.indices,) + function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) println(io, typeof(g)) return print(io, join(repr.(blocks(g)), '\n')) @@ -247,18 +228,24 @@ function BlockSparseArrays.blockedunitrange_getindices( end function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::AbstractVector{<:Union{Block{1},BlockIndexRange{1}}} + g::AbstractGradedUnitRange, indices::AbstractVector{<:Block{1}} ) # Without converting `indices` to `Vector`, # mapping `indices` outputs a `BlockVector` # which is harder to reason about. - blocks = 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. - return mortar(blocks, length.(blocks)) + gblocks = map(index -> g[index], Vector(indices)) + # pass block labels to the axes of the output, + # such that `only(axes(a[indices])) isa `GradedUnitRange` + # if `a isa `GradedUnitRange` + newg = axis_cat(sectorrange.(nondual_sector.(gblocks) .=> length.(gblocks), isdual(g))) + return mortar(gblocks, (newg,)) +end + +# TBD dispacth on symmetry style? +function BlockSparseArrays.blockedunitrange_getindices( + g::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndexRange{1}} +) + return blockedunitrange_getindices(unlabel_blocks(g), indices) end function BlockSparseArrays.blockedunitrange_getindices( @@ -276,7 +263,7 @@ end function BlockSparseArrays.blockedunitrange_getindices( ga::AbstractGradedUnitRange, indices::BlockRange ) - return labelled_blocks(unlabel_blocks(ga)[indices], blocklabels(ga, indices)) + return GradedUnitRange(sector_axes(ga)[Int.(indices)], unlabel_blocks(ga)[indices]) end function BlockSparseArrays.blockedunitrange_getindices( @@ -348,6 +335,9 @@ function Base.getindex(a::AbstractGradedUnitRange, indices) return blockedunitrange_getindices(a, indices) end +# fix ambiguity +Base.getindex(g::AbstractGradedUnitRange, ::Colon) = g + function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer}) return blockedunitrange_getindices(a, indices) end @@ -378,9 +368,6 @@ function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) return GradedUnitRange(new_first, new_blocklasts) end -# preserve axes in SubArray -Base.axes(S::Base.Slice{<:AbstractGradedUnitRange}) = (S.indices,) - # Version of length that checks that all blocks have the same label # and returns a labelled length with that label. function labelled_length(a::AbstractBlockVector{<:Integer}) diff --git a/src/gradedunitrange_interface.jl b/src/gradedunitrange_interface.jl index 960c343..7138ef5 100644 --- a/src/gradedunitrange_interface.jl +++ b/src/gradedunitrange_interface.jl @@ -1,3 +1,6 @@ +using BlockArrays: AbstractBlockVector, BlockRange, blocklength +using FillArrays: Fill + """ dual(x) @@ -29,3 +32,21 @@ function dag(a::AbstractArray) a′ .= conj.(a) return a′ end + +map_blocklabels(::Any, a::AbstractUnitRange) = a + +to_sector(x) = x + +sector_type(x) = sector_type(typeof(x)) +sector_type(::Type) = error("Not implemented") + +struct NoLabel end +blocklabels(r::AbstractUnitRange) = Fill(NoLabel(), blocklength(r)) +blocklabels(v::AbstractBlockVector) = mapreduce(blocklabels, vcat, blocks(v)) + +# == is just a range comparison that ignores labels. Need dedicated function to check equality. +function space_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) + return (isdual(a1) == isdual(a2)) && + blocklabels(a1) == blocklabels(a2) && + blockisequal(a1, a2) +end diff --git a/src/sector_product.jl b/src/sector_product.jl index 5c3b70d..7d38c72 100644 --- a/src/sector_product.jl +++ b/src/sector_product.jl @@ -100,13 +100,18 @@ end ×(c1::NamedTuple, c2::AbstractSector) = ×(SectorProduct(c1), SectorProduct(c2)) ×(c1::AbstractSector, c2::NamedTuple) = ×(SectorProduct(c1), SectorProduct(c2)) -function ×(g1::AbstractUnitRange, g2::AbstractUnitRange) - # TBD SectorUnitRange? - v = map( - ((l1, l2),) -> l1 × l2, - Iterators.flatten((Iterators.product(blocklabels(g1), blocklabels(g2)),),), +function ×(sr1::SectorUnitRange, sr2::SectorUnitRange) + @assert isdual(sr1) == isdual(sr2) + return sectorrange( + nondual_sector(sr1) × nondual_sector(sr2), + sector_multiplicity(sr1) * sector_multiplicity(sr1), + isdual(sr1), ) - return gradedrange(v) +end + +function ×(g1::AbstractGradedUnitRange, g2::AbstractGradedUnitRange) + v = map(splat(×), Iterators.flatten((Iterators.product(blocks(g1), blocks(g2)),),)) + return axis_cat(v) end # ==================================== Fusion rules ====================================== diff --git a/src/sectorunitrange.jl b/src/sectorunitrange.jl index a44800e..1fe44d6 100644 --- a/src/sectorunitrange.jl +++ b/src/sectorunitrange.jl @@ -24,8 +24,6 @@ const SectorOneTo{T,Sector,Range} = SectorUnitRange{T,Sector,Base.OneTo{T}} # Constructors # -to_sector(x) = x - # sectorrange(SU2(1), 2:5) function sectorrange(s, r::AbstractUnitRange, b::Bool=false) return SectorUnitRange(to_sector(s), r, b) diff --git a/test/test_gradedunitranges_basics.jl b/test/test_gradedunitrange.jl similarity index 59% rename from test/test_gradedunitranges_basics.jl rename to test/test_gradedunitrange.jl index cc29448..09d57bc 100644 --- a/test/test_gradedunitranges_basics.jl +++ b/test/test_gradedunitrange.jl @@ -1,6 +1,5 @@ using BlockArrays: Block, - BlockRange, BlockSlice, BlockVector, blockedrange, @@ -12,11 +11,15 @@ using BlockArrays: combine_blockaxes, mortar using GradedArrays: + GradedArrays, GradedOneTo, GradedUnitRange, SectorUnitRange, blocklabels, + dual, + flip, gradedrange, + isdual, nondual_sector, sector_type, sectorrange, @@ -24,58 +27,61 @@ using GradedArrays: using Test: @test, @test_broken, @testset @testset "GradedUnitRanges basics" begin - a0 = Base.OneTo(1) - a = gradedrange(["x" => 2, "y" => 3]) - @test a isa GradedOneTo - @test a isa GradedUnitRange - @test sector_type(a) === String - @test space_isequal(a, a) - @test !space_isequal(a0, a) - @test !space_isequal(a, a0) - @test !space_isequal(a, 1:5) - for x in iterate(a) + r0 = Base.OneTo(1) + g = gradedrange(["x" => 2, "y" => 3]) + @test g isa GradedOneTo + @test g isa GradedUnitRange + @test sector_type(g) === String + @test space_isequal(g, g) + @test !space_isequal(r0, g) + @test !space_isequal(g, r0) + @test !space_isequal(g, 1:5) + for x in iterate(g) @test x == 1 end - for x in iterate(a, 1) + for x in iterate(g, 1) @test x == 2 end - for x in iterate(a, 2) + for x in iterate(g, 2) @test x == 3 end - for x in iterate(a, 3) + for x in iterate(g, 3) @test x == 4 end - for x in iterate(a, 4) + for x in iterate(g, 4) @test x == 5 end - @test isnothing(iterate(a, 5)) - @test length(a) == 5 - @test step(a) == 1 - @test length(blocks(a)) == 2 - @test blocks(a)[1] == 1:2 - @test nondual_sector(blocks(a)[1]) == "x" - @test blocks(a)[2] == 3:5 - @test nondual_sector(blocks(a)[2]) == "y" + @test isnothing(iterate(g, 5)) + @test length(g) == 5 + @test step(g) == 1 + @test length(blocks(g)) == 2 + @test blocks(g)[1] == 1:2 + @test nondual_sector(blocks(g)[1]) == "x" + @test blocks(g)[2] == 3:5 + @test nondual_sector(blocks(g)[2]) == "y" - @test a[Block(2)] isa SectorUnitRange - @test space_isequal(a[Block(2)], sectorrange("y", 3:5)) + @test g[Block(2)] isa SectorUnitRange + @test space_isequal(g[Block(2)], sectorrange("y", 3:5)) - @test a[4] == 4 - @test blocklengths(a) == [2, 3] - @test blocklabels(a) == ["x", "y"] - @test blockfirsts(a) == [1, 3] - @test first(a) == 1 - @test blocklasts(a) == [2, 5] - @test last(a) == 5 - @test blocklengths(only(axes(a))) == blocklengths(a) - @test blocklabels(only(axes(a))) == blocklabels(a) + @test g[4] == 4 + @test blocklengths(g) == [2, 3] + @test blocklabels(g) == ["x", "y"] + @test blockfirsts(g) == [1, 3] + @test first(g) == 1 + @test blocklasts(g) == [2, 5] + @test last(g) == 5 + @test blocklengths(only(axes(g))) == blocklengths(g) + @test blocklabels(only(axes(g))) == blocklabels(g) + @test !isdual(g) - @test axes(Base.Slice(a)) isa Tuple{typeof(a)} - @test AbstractUnitRange{Int}(a) == 1:5 - b = combine_blockaxes(a, a) # TODO + @test axes(Base.Slice(g)) isa Tuple{typeof(g)} + @test AbstractUnitRange{Int}(g) == 1:5 + @test_broken b = combine_blockaxes(g, g) # TODO + #= @test b isa GradedOneTo @test b == 1:5 - @test space_isequal(b, a) + @test space_isequal(b, g) + =# # Slicing operations g = gradedrange(["x" => 2, "y" => 3]) @@ -84,7 +90,7 @@ using Test: @test, @test_broken, @testset @test blockisequal(a, blockedrange([1, 1, 2])[Block.(2:3)]) @test g[[2, 4]] == [2, 4] - # Regression test for ambiguity error. + # Regression test for ambiguity errors. g = gradedrange(["x" => 2, "y" => 3]) a = g[BlockSlice(Block(1), Base.OneTo(2))] @test length(a) == 2 @@ -92,6 +98,7 @@ using Test: @test, @test_broken, @testset @test blocklength(a) == 1 @test a isa SectorUnitRange @test space_isequal(a, sectorrange("x" => 2)) + @test space_isequal(g, g[:]) g = gradedrange(["x" => 2, "y" => 3]) a = g[3:4] @@ -139,15 +146,14 @@ using Test: @test, @test_broken, @testset @test blocklengths(ax) == [4, 3] @test blocklabels(ax) == blocklabels(a) - x = gradedrange(["x" => 2, "y" => 3, "z" => 4]) - a = x[[Block(3)[2:3], Block(2)[2:3]]] + g = gradedrange(["x" => 2, "y" => 3, "z" => 4]) + a = g[[Block(3)[2:3], Block(2)[2:3]]] # drop labels @test a isa BlockVector @test length(a) == 4 @test blocklength(a) == 2 # TODO: `BlockArrays` doesn't define `blocklengths` # for `BlockVector`, should it? @test_broken blocklengths(a) == [2, 2] - @test blocklabels(a) == ["z", "y"] @test a[Block(1)] == 7:8 @test a[Block(2)] == 4:5 ax = only(axes(a)) @@ -157,17 +163,26 @@ using Test: @test, @test_broken, @testset # @test blocklengths(ax) == blocklengths(a) # once `blocklengths(::BlockVector)` is defined. @test blocklengths(ax) == [2, 2] - @test blocklabels(ax) == blocklabels(a) g = gradedrange(["x" => 2, "y" => 3]) I = mortar([Block(1)[1:1]]) a = g[I] @test length(a) == 1 - @test label(first(a)) == "x" - g = gradedrange(["x" => 2, "y" => 3])[1:5] - I = mortar([Block(1)[1:1]]) - a = g[I] - @test length(a) == 1 - @test label(first(a)) == "x" + gd = gradedrange(["x" => 2, "y" => 3], dual=true) + @test isdual(gd) + @test gd[Block(2)] isa SectorUnitRange + @test space_isequal(gd[Block(2)], sectorrange("y", 3:5, true)) + @test blocklabels(g) == blocklabels(gd) # string is self-dual + @test !space_isequal(gd, g) + @test space_isequal(gd, dual(g)) + @test space_isequal(gd, flip(g)) + @test space_isequal(flip(gd), g) + @test space_isequal(dual(gd), g) + + # label length > 1 + g = gradedrange(["x" => 2, "yy" => 3]) + @test length(g) == 8 + @test blocklengths(g) == [2, 6] + @test space_isequal(g[Block(2)], sectorrange("yy", 3:8)) end diff --git a/test/test_gradedunitrange_interface.jl b/test/test_gradedunitrange_interface.jl new file mode 100644 index 0000000..8b01238 --- /dev/null +++ b/test/test_gradedunitrange_interface.jl @@ -0,0 +1,38 @@ +using BlockArrays: BlockedOneTo, blockisequal + +using GradedArrays: NoLabel, blocklabels, dag, dual, flip, isdual, space_isequal +using Test: @test, @testset +using TensorProducts: OneToOne + +@testset "AbstractUnitRange" begin + a0 = OneToOne() + @test !isdual(a0) + @test dual(a0) isa OneToOne + @test space_isequal(a0, a0) + @test space_isequal(a0, dual(a0)) + @test only(blocklabels(a0)) == NoLabel() + + a = 1:3 + ad = dual(a) + af = flip(a) + @test !isdual(a) + @test !isdual(ad) + @test !isdual(dag(a)) + @test !isdual(af) + @test ad isa UnitRange + @test af isa UnitRange + @test space_isequal(ad, a) + @test space_isequal(af, a) + @test only(blocklabels(a)) == NoLabel() + + a = blockedrange([2, 3]) + ad = dual(a) + af = flip(a) + @test !isdual(a) + @test !isdual(ad) + @test ad isa BlockedOneTo + @test af isa BlockedOneTo + @test blockisequal(ad, a) + @test blockisequal(af, a) + @test blocklabels(a) == [NoLabel(), NoLabel()] +end diff --git a/test/test_gradedunitranges_dual.jl b/test/test_gradedunitranges_dual.jl deleted file mode 100644 index ed0628c..0000000 --- a/test/test_gradedunitranges_dual.jl +++ /dev/null @@ -1,299 +0,0 @@ -using BlockArrays: - Block, - BlockedOneTo, - blockaxes, - blockedrange, - blockfirsts, - blockisequal, - blocklasts, - blocklength, - blocklengths, - blocks, - findblock, - mortar, - combine_blockaxes -using GradedArrays: - AbstractGradedUnitRange, - blocklabels, - dag, - dual, - flip, - gradedrange, - isdual, - sector_type, - sectormergesortperm, - sectorsortperm, - space_isequal -using Test: @test, @test_broken, @testset -using TensorProducts: OneToOne, tensor_product - -struct U1 - n::Int -end -GradedUnitRanges.dual(c::U1) = U1(-c.n) -Base.isless(c1::U1, c2::U1) = c1.n < c2.n - -@testset "AbstractUnitRange" begin - a0 = OneToOne() - @test !isdual(a0) - @test dual(a0) isa OneToOne - @test space_isequal(a0, a0) - @test labelled_isequal(a0, a0) - @test space_isequal(a0, dual(a0)) - - a = 1:3 - ad = dual(a) - af = flip(a) - @test !isdual(a) - @test !isdual(ad) - @test !isdual(dag(a)) - @test !isdual(af) - @test ad isa UnitRange - @test af isa UnitRange - @test space_isequal(ad, a) - @test space_isequal(af, a) - - a = blockedrange([2, 3]) - ad = dual(a) - af = flip(a) - @test !isdual(a) - @test !isdual(ad) - @test ad isa BlockedOneTo - @test af isa BlockedOneTo - @test blockisequal(ad, a) - @test blockisequal(af, a) -end - -@testset "LabelledUnitRangeDual" begin - 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) - @test label_type(la) === U1 - @test sector_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) - @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) - @test isdual(dag(la)) - @test nondual(lad) === la - @test dual(lad) === la - @test label_type(lad) === U1 - @test sector_type(lad) === U1 - - @test dual_type(la) === typeof(lad) - @test dual_type(lad) === typeof(la) - @test nondual_type(lad) === typeof(la) - @test nondual_type(la) === typeof(la) - - @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 - @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') - lad = dual(la) - @test lad isa LabelledUnitRangeDual - @test label(lad) == 'x' - @test blocklabels(lad) == ['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) - @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 - for a in - [gradedrange([U1(0) => 2, U1(1) => 3]), gradedrange([U1(0) => 2, U1(1) => 3])[1:5]] - ad = dual(a) - @test ad isa GradedUnitRangeDual - @test ad isa AbstractGradedUnitRange - @test eltype(ad) == LabelledInteger{Int,U1} - @test blocklengths(ad) isa Vector - @test eltype(blocklengths(ad)) == eltype(blocklengths(a)) - @test sector_type(a) === U1 - - @test space_isequal(dual(ad), a) - @test space_isequal(nondual(ad), a) - @test space_isequal(nondual(a), a) - @test space_isequal(ad, ad) - @test !space_isequal(a, ad) - @test !space_isequal(ad, a) - - @test dual_type(a) === typeof(ad) - @test dual_type(ad) === typeof(a) - @test nondual_type(ad) === typeof(a) - @test nondual_type(a) === typeof(a) - - @test isdual(ad) - @test isdual(dag(a)) - @test isdual(only(axes(ad))) - @test !isdual(a) - @test axes(Base.Slice(a)) isa Tuple{typeof(a)} - @test AbstractUnitRange{Int}(ad) == 1:5 - b = combine_blockaxes(ad, ad) - @test b isa GradedUnitRangeDual - @test b == 1:5 - @test space_isequal(b, ad) - - for x in iterate(ad) - @test x == 1 - @test label(x) == U1(0) - end - for x in iterate(ad, labelled(3, U1(-1))) - @test x == 4 - @test label(x) == U1(-1) - end - - @test blockfirsts(ad) == [labelled(1, U1(0)), labelled(3, U1(-1))] - @test blocklasts(ad) == [labelled(2, U1(0)), labelled(5, U1(-1))] - @test blocklength(ad) == 2 - @test blocklengths(ad) == [2, 3] - @test blocklabels(ad) == [U1(0), U1(-1)] - @test label.(blocklengths(ad)) == [U1(0), U1(-1)] - @test findblock(ad, 4) == Block(2) - @test only(blockaxes(ad)) == Block(1):Block(2) - @test blocks(ad) == [labelled(1:2, U1(0)), labelled(3:5, U1(-1))] - @test ad[4] == 4 - @test label(ad[4]) == U1(-1) - @test ad[2:4] == 2:4 - @test ad[2:4] isa GradedUnitRangeDual - @test label(ad[2:4][Block(2)]) == U1(-1) - @test ad[[2, 4]] == [2, 4] - @test label(ad[[2, 4]][2]) == U1(-1) - @test ad[Block(2)] == 3:5 - @test label(ad[Block(2)]) == U1(-1) - @test ad[Block(1):Block(2)][Block(2)] == 3:5 - @test label(ad[Block(1):Block(2)][Block(2)]) == U1(-1) - @test ad[[Block(2), Block(1)]][Block(1)] == 3:5 - @test label(ad[[Block(2), Block(1)]][Block(1)]) == U1(-1) - @test ad[[Block(2)[1:2], Block(1)[1:2]]][Block(1)] == 3:4 - @test label(ad[[Block(2)[1:2], Block(1)[1:2]]][Block(1)]) == U1(-1) - @test sectorsortperm(a) == [Block(1), Block(2)] - @test sectorsortperm(ad) == [Block(1), Block(2)] - @test blocklength(sectormergesortperm(a)) == 2 - @test blocklength(sectormergesortperm(ad)) == 2 - @test sectormergesortperm(a) == [Block(1), Block(2)] - @test sectormergesortperm(ad) == [Block(1), Block(2)] - - @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) - - v = ad[[Block(2)[1:1]]] - @test v isa AbstractVector{LabelledInteger{Int64,U1}} - @test length(v) == 1 - @test label(first(v)) == U1(-1) - @test unlabel(first(v)) == 3 - @test isdual(v[Block(1)]) - @test isdual(axes(v, 1)) - @test blocklabels(axes(v, 1)) == [U1(-1)] - - v = ad[mortar([Block(2)[1:1]])] - @test v isa AbstractVector{LabelledInteger{Int64,U1}} - @test isdual(axes(v, 1)) # used in view(::BlockSparseVector, [Block(1)[1:1]]) - @test label(first(v)) == U1(-1) - @test unlabel(first(v)) == 3 - @test blocklabels(axes(v, 1)) == [U1(-1)] - - v = ad[[Block(2)]] - @test v isa AbstractVector{LabelledInteger{Int64,U1}} - @test isdual(axes(v, 1)) # used in view(::BlockSparseVector, [Block(1)]) - @test label(first(v)) == U1(-1) - @test unlabel(first(v)) == 3 - @test blocklabels(axes(v, 1)) == [U1(-1)] - - v = ad[mortar([[Block(2)], [Block(1)]])] - @test v isa AbstractVector{LabelledInteger{Int64,U1}} - @test isdual(axes(v, 1)) - @test label(first(v)) == U1(-1) - @test unlabel(first(v)) == 3 - @test blocklabels(axes(v, 1)) == [U1(-1), U1(0)] - end -end - -@testset "flip" begin - for a in - [gradedrange([U1(0) => 2, U1(1) => 3]), gradedrange([U1(0) => 2, U1(1) => 3])[1:5]] - ad = dual(a) - @test space_isequal(flip(a), dual(gradedrange([U1(0) => 2, U1(-1) => 3]))) - @test space_isequal(flip(ad), gradedrange([U1(0) => 2, U1(-1) => 3])) - - @test blocklabels(a) == [U1(0), U1(1)] - @test blocklabels(dual(a)) == [U1(0), U1(-1)] - @test blocklabels(flip(a)) == [U1(0), U1(1)] - @test blocklabels(flip(dual(a))) == [U1(0), U1(-1)] - @test blocklabels(dual(flip(a))) == [U1(0), U1(-1)] - - @test blocklengths(a) == [2, 3] - @test blocklengths(ad) == [2, 3] - @test blocklengths(flip(a)) == [2, 3] - @test blocklengths(flip(ad)) == [2, 3] - @test blocklengths(dual(flip(a))) == [2, 3] - - @test !isdual(a) - @test isdual(ad) - @test isdual(flip(a)) - @test !isdual(flip(ad)) - @test !isdual(dual(flip(a))) - end -end diff --git a/test/test_gradedunitranges_tensor_product.jl b/test/test_gradedunitranges_tensor_product.jl index fde681d..e793d6f 100644 --- a/test/test_gradedunitranges_tensor_product.jl +++ b/test/test_gradedunitranges_tensor_product.jl @@ -1,7 +1,9 @@ using BlockArrays: blocklength, blocklengths using GradedArrays: - GradedUnitRanges, + GradedArrays, GradedOneTo, + NotAbelianStyle, + U1, blocklabels, dual, unmerged_tensor_product, @@ -12,38 +14,32 @@ using GradedArrays: using TensorProducts: OneToOne, tensor_product using Test: @test, @testset -struct U1 - n::Int -end -GradedUnitRanges.dual(c::U1) = U1(-c.n) -Base.isless(c1::U1, c2::U1) = c1.n < c2.n -GradedUnitRanges.fuse_labels(x::U1, y::U1) = U1(x.n + y.n) -a0 = gradedrange([U1(1) => 1, U1(2) => 3, U1(1) => 1]) +GradedArrays.SymmetryStyle(::Type{<:String}) = NotAbelianStyle() +GradedArrays.tensor_product(s1::String, s2::String) = gradedrange([s1 * s2 => 1]) @testset "unmerged_tensor_product" begin - GradedUnitRanges.fuse_labels(x::String, y::String) = x * y - @test unmerged_tensor_product() isa OneToOne @test unmerged_tensor_product(OneToOne(), OneToOne()) isa OneToOne a = gradedrange(["x" => 2, "y" => 3]) - @test labelled_isequal(unmerged_tensor_product(a), a) + @test space_isequal(unmerged_tensor_product(a), a) b = unmerged_tensor_product(a, a) @test b isa GradedOneTo - @test length(b) == 25 + @test length(b) == 50 @test blocklength(b) == 4 - @test blocklengths(b) == [4, 6, 6, 9] - @test labelled_isequal(b, gradedrange(["xx" => 4, "yx" => 6, "xy" => 6, "yy" => 9])) + @test blocklengths(b) == [8, 12, 12, 18] + @test space_isequal(b, gradedrange(["xx" => 4, "yx" => 6, "xy" => 6, "yy" => 9])) c = unmerged_tensor_product(a, a, a) @test c isa GradedOneTo - @test length(c) == 125 + @test length(c) == 375 @test blocklength(c) == 8 @test blocklabels(c) == ["xxx", "yxx", "xyx", "yyx", "xxy", "yxy", "xyy", "yyy"] - @test labelled_isequal( - unmerged_tensor_product(a0, a0), + a = gradedrange([U1(1) => 1, U1(2) => 3, U1(1) => 1]) + @test space_isequal( + unmerged_tensor_product(a, a), gradedrange([ U1(2) => 1, U1(3) => 3, @@ -56,58 +52,46 @@ a0 = gradedrange([U1(1) => 1, U1(2) => 3, U1(1) => 1]) U1(2) => 1, ]), ) -end - -@testset "symmetric tensor_product" begin - for a in (a0, a0[1:5]) - @test labelled_isequal(unmerged_tensor_product(a), a) - @test labelled_isequal(unmerged_tensor_product(a, OneToOne()), a) - @test labelled_isequal(unmerged_tensor_product(OneToOne(), a), a) - @test labelled_isequal(tensor_product(a), gradedrange([U1(1) => 2, U1(2) => 3])) + @test space_isequal(unmerged_tensor_product(a), a) + @test space_isequal(unmerged_tensor_product(a, OneToOne()), a) + @test space_isequal(unmerged_tensor_product(OneToOne(), a), a) + @test space_isequal(tensor_product(a), gradedrange([U1(1) => 2, U1(2) => 3])) - @test labelled_isequal( - tensor_product(a, a), gradedrange([U1(2) => 4, U1(3) => 12, U1(4) => 9]) - ) - @test labelled_isequal( - tensor_product(a, OneToOne()), gradedrange([U1(1) => 2, U1(2) => 3]) - ) - @test labelled_isequal( - tensor_product(OneToOne(), a), gradedrange([U1(1) => 2, U1(2) => 3]) - ) + @test space_isequal( + tensor_product(a, a), gradedrange([U1(2) => 4, U1(3) => 12, U1(4) => 9]) + ) + @test space_isequal(tensor_product(a, OneToOne()), gradedrange([U1(1) => 2, U1(2) => 3])) + @test space_isequal(tensor_product(OneToOne(), a), gradedrange([U1(1) => 2, U1(2) => 3])) - d = tensor_product(a, a, a) - @test labelled_isequal( - d, gradedrange([U1(3) => 8, U1(4) => 36, U1(5) => 54, U1(6) => 27]) - ) - end + d = tensor_product(a, a, a) + @test space_isequal(d, gradedrange([U1(3) => 8, U1(4) => 36, U1(5) => 54, U1(6) => 27])) end @testset "dual and tensor_product" begin - for a in (a0, a0[1:5]) - ad = dual(a) + a = gradedrange([U1(1) => 1, U1(2) => 3, U1(1) => 1]) + ad = dual(a) - b = unmerged_tensor_product(ad) - @test isdual(b) - @test space_isequal(b, ad) - @test space_isequal(unmerged_tensor_product(ad, OneToOne()), ad) - @test space_isequal(unmerged_tensor_product(OneToOne(), ad), ad) + b = unmerged_tensor_product(ad) + @test isdual(b) + @test space_isequal(b, ad) + @test space_isequal(unmerged_tensor_product(ad, OneToOne()), ad) + @test space_isequal(unmerged_tensor_product(OneToOne(), ad), ad) - b = tensor_product(ad) - @test b isa GradedOneTo - @test !isdual(b) - @test space_isequal(b, gradedrange([U1(-2) => 3, U1(-1) => 2])) + b = tensor_product(ad) + @test b isa GradedOneTo + @test !isdual(b) + @test space_isequal(b, gradedrange([U1(-2) => 3, U1(-1) => 2])) - c = tensor_product(ad, ad) - @test c isa GradedOneTo - @test !isdual(c) - @test space_isequal(c, gradedrange([U1(-4) => 9, U1(-3) => 12, U1(-2) => 4])) + c = tensor_product(ad, ad) + @test c isa GradedOneTo + @test !isdual(c) + @test space_isequal(c, gradedrange([U1(-4) => 9, U1(-3) => 12, U1(-2) => 4])) - d = tensor_product(ad, a) - @test !isdual(d) - @test space_isequal(d, gradedrange([U1(-1) => 6, U1(0) => 13, U1(1) => 6])) + d = tensor_product(ad, a) + @test !isdual(d) + @test space_isequal(d, gradedrange([U1(-1) => 6, U1(0) => 13, U1(1) => 6])) - e = tensor_product(a, ad) - @test !isdual(d) - @test space_isequal(e, d) - end + e = tensor_product(a, ad) + @test !isdual(d) + @test space_isequal(e, d) end diff --git a/test/test_gradedunitranges_sectorunitrange.jl b/test/test_sectorunitrange.jl similarity index 94% rename from test/test_gradedunitranges_sectorunitrange.jl rename to test/test_sectorunitrange.jl index 87fe01b..7b1102d 100644 --- a/test/test_gradedunitranges_sectorunitrange.jl +++ b/test/test_sectorunitrange.jl @@ -2,7 +2,9 @@ using Test: @test, @test_throws, @testset using BlockArrays: Block, blocklength, blocklengths, blockisequal, blocks -using GradedArrays.GradedUnitRanges: +using GradedArrays: + AbstractSector, + SU, SectorUnitRange, blocklabels, dual, @@ -10,12 +12,12 @@ using GradedArrays.GradedUnitRanges: full_range, isdual, nondual_sector, + quantum_dimension, sector_multiplicities, sector_multiplicity, sector_type, sectorrange, space_isequal -using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @testset "SectorUnitRange" begin sr = sectorrange(SU((1, 0)), 2) @@ -68,6 +70,7 @@ using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @test !space_isequal(sr, sectorrange(SU((1, 1)), 2)) @test !space_isequal(sr, sectorrange(SU((1, 0)), 2:7)) @test !space_isequal(sr, sectorrange(SU((1, 1)), 2, true)) + @test !space_isequal(sr, sectorrange(SU((1, 0)), 2, true)) sr2 = copy(sr) @test sr2 isa SectorUnitRange @@ -91,7 +94,7 @@ using GradedArrays.SymmetrySectors: AbstractSector, SU, quantum_dimension @test sector_type(sr) === SU{3,2} @test sector_type(typeof(sr)) === SU{3,2} @test blocklabels(sr) == [SU((1, 0))] - @test sector_multipliciy(sr) == 2 + @test sector_multiplicity(sr) == 2 @test sector_multiplicities(sr) == [2] srd = dual(sr) diff --git a/test/test_symmetrysectors_exports.jl b/test/test_symmetrysectors_exports.jl deleted file mode 100644 index 112e153..0000000 --- a/test/test_symmetrysectors_exports.jl +++ /dev/null @@ -1,6 +0,0 @@ -using GradedArrays.SymmetrySectors: SymmetrySectors -using Test: @test, @testset -@testset "Test exports" begin - exports = [:SymmetrySectors, :U1, :Z, :dual] - @test issetequal(names(SymmetrySectors), exports) -end diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index 950613a..279f38b 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -43,15 +43,6 @@ using TestExtras: @constinferred @test arguments(s)[3] == U1(3) @test (@constinferred trivial(s)) == SectorProduct(U1(0), SU2(0), U1(0)) - s = U1(3) × SU2(1//2) × Fib("τ") - @test length(arguments(s)) == 3 - @test (@constinferred quantum_dimension(s)) == 1.0 + √5 - @test dual(s) == U1(-3) × SU2(1//2) × Fib("τ") - @test arguments(s)[1] == U1(3) - @test arguments(s)[2] == SU2(1//2) - @test arguments(s)[3] == Fib("τ") - @test (@constinferred trivial(s)) == SectorProduct(U1(0), SU2(0), Fib("1")) - s = TrivialSector() × U1(3) × SU2(1 / 2) @test length(arguments(s)) == 3 @test (@constinferred quantum_dimension(s)) == 2 @@ -150,69 +141,6 @@ using TestExtras: @constinferred ) end - @testset "Fusion of NonGroupCategory products" begin - ı = Fib("1") - τ = Fib("τ") - s = ı × ı - @test space_isequal(s ⊗ s, gradedrange([s => 1])) - - s = τ × τ - @test space_isequal( - s ⊗ s, gradedrange([(ı × ı) => 1, (τ × ı) => 1, (ı × τ) => 1, (τ × τ) => 1]) - ) - - σ = Ising("σ") - ψ = Ising("ψ") - s = τ × σ - g = gradedrange([ - (ı × Ising("1")) => 1, (τ × Ising("1")) => 1, (ı × ψ) => 1, (τ × ψ) => 1 - ]) - @test space_isequal(s ⊗ s, g) - end - - @testset "Fusion of mixed Abelian and NonAbelian products" begin - p2h = U1(2) × SU2(1//2) - p1h = U1(1) × SU2(1//2) - @test space_isequal( - p2h ⊗ p1h, gradedrange([(U1(3) × SU2(0)) => 1, (U1(3) × SU2(1)) => 1]) - ) - - p1h1 = U1(1) × SU2(1//2) × Z{2}(1) - @test space_isequal( - p1h1 ⊗ p1h1, - gradedrange([(U1(2) × SU2(0) × Z{2}(0)) => 1, (U1(2) × SU2(1) × Z{2}(0)) => 1]), - ) - end - - @testset "Fusion of fully mixed products" begin - s = U1(1) × SU2(1//2) × Ising("σ") - @test space_isequal( - s ⊗ s, - gradedrange([ - (U1(2) × SU2(0) × Ising("1")) => 1, - (U1(2) × SU2(1) × Ising("1")) => 1, - (U1(2) × SU2(0) × Ising("ψ")) => 1, - (U1(2) × SU2(1) × Ising("ψ")) => 1, - ]), - ) - - ı = Fib("1") - τ = Fib("τ") - s = SU2(1//2) × U1(1) × τ - @test space_isequal( - s ⊗ s, - gradedrange([ - (SU2(0) × U1(2) × ı) => 1, - (SU2(1) × U1(2) × ı) => 1, - (SU2(0) × U1(2) × τ) => 1, - (SU2(1) × U1(2) × τ) => 1, - ]), - ) - - s = U1(1) × ı × τ - @test space_isequal(s ⊗ s, gradedrange([(U1(2) × ı × ı) => 1, (U1(2) × ı × τ) => 1])) - end - @testset "Fusion of different length Categories" begin @test SectorProduct(U1(1) × U1(0)) ⊗ SectorProduct(U1(1)) == SectorProduct(U1(2) × U1(0)) @@ -237,14 +165,11 @@ using TestExtras: @constinferred end @testset "GradedUnitRange fusion rules" begin - s1 = U1(1) × SU2(1//2) × Ising("σ") - s2 = U1(0) × SU2(1//2) × Ising("1") + s1 = U1(1) × SU2(1//2) + s2 = U1(0) × SU2(1//2) g1 = gradedrange([s1 => 2]) g2 = gradedrange([s2 => 1]) - @test space_isequal( - g1 ⊗ g2, - gradedrange([U1(1) × SU2(0) × Ising("σ") => 2, U1(1) × SU2(1) × Ising("σ") => 2]), - ) + @test space_isequal(g1 ⊗ g2, gradedrange([U1(1) × SU2(0) => 2, U1(1) × SU2(1) => 2])) end end @@ -267,12 +192,6 @@ end @test (@constinferred trivial(s)) == (A=U1(0),) × (B=SU2(0),) @test s == (B=SU2(2),) × (A=U1(1),) - s = s × (C=Ising("ψ"),) - @test length(arguments(s)) == 3 - @test arguments(s)[:C] == Ising("ψ") - @test (@constinferred quantum_dimension(s)) == 5.0 - @test (@constinferred dual(s)) == (A=U1(-1),) × (B=SU2(2),) × (C=Ising("ψ"),) - s1 = (A=U1(1),) × (B=Z{2}(0),) s2 = (A=U1(1),) × (C=Z{2}(0),) @test_throws ArgumentError s1 × s2 @@ -290,11 +209,11 @@ end @test (@constinferred dual(s)) == SectorProduct("A" => U1(-2)) @test (@constinferred trivial(s)) == SectorProduct(; A=U1(0)) - s = SectorProduct("B" => Ising("ψ"), :C => Z{2}(1)) + s = SectorProduct("B" => SU2(1//2), :C => Z{2}(1)) @test length(arguments(s)) == 2 - @test arguments(s)[:B] == Ising("ψ") + @test arguments(s)[:B] == SU2(1//2) @test arguments(s)[:C] == Z{2}(1) - @test (@constinferred quantum_dimension(s)) == 1.0 + @test (@constinferred quantum_dimension(s)) == 2 end @testset "Comparisons with unspecified labels" begin @@ -343,29 +262,6 @@ end SectorProduct(; A=SU2(0), B=Z{2}(1), C=SU2(1//2)) => 1, ]) @test (@constinferred quantum_dimension(g)) == 4 - - # non group sectors - g_fib = gradedrange([SectorProduct(; A=Fib("1"), B=Fib("1")) => 1]) - g_ising = gradedrange([SectorProduct(; A=Ising("1"), B=Ising("1")) => 1]) - @test (@constinferred quantum_dimension(g_fib)) == 1.0 - @test (@constinferred quantum_dimension(g_ising)) == 1.0 - - # mixed product Abelian / NonAbelian / NonGroup - g = gradedrange([ - SectorProduct(; A=U1(2), B=SU2(0), C=Ising(1)) => 1, - SectorProduct(; A=U1(2), B=SU2(1), C=Ising(1)) => 1, - SectorProduct(; A=U1(2), B=SU2(0), C=Ising("ψ")) => 1, - SectorProduct(; A=U1(2), B=SU2(1), C=Ising("ψ")) => 1, - ]) - @test (@constinferred quantum_dimension(g)) == 8.0 - - g = gradedrange([ - SectorProduct(; A=Fib("1"), B=SU2(0), C=U1(2)) => 1, - SectorProduct(; A=Fib("1"), B=SU2(1), C=U1(2)) => 1, - SectorProduct(; A=Fib("τ"), B=SU2(0), C=U1(2)) => 1, - SectorProduct(; A=Fib("τ"), B=SU2(1), C=U1(2)) => 1, - ]) - @test (@constinferred quantum_dimension(g)) == 4.0 + 4.0quantum_dimension(Fib("τ")) end @testset "Fusion of Abelian products" begin @@ -414,35 +310,6 @@ end ) end - @testset "Fusion of NonGroupCategory products" begin - ı = Fib("1") - τ = Fib("τ") - s = SectorProduct(; A=ı, B=ı) - @test space_isequal(s ⊗ s, gradedrange([s => 1])) - - s = SectorProduct(; A=τ, B=τ) - @test space_isequal( - s ⊗ s, - gradedrange([ - SectorProduct(; A=ı, B=ı) => 1, - SectorProduct(; A=τ, B=ı) => 1, - SectorProduct(; A=ı, B=τ) => 1, - SectorProduct(; A=τ, B=τ) => 1, - ]), - ) - - σ = Ising("σ") - ψ = Ising("ψ") - s = SectorProduct(; A=τ, B=σ) - g = gradedrange([ - SectorProduct(; A=ı, B=Ising("1")) => 1, - SectorProduct(; A=τ, B=Ising("1")) => 1, - SectorProduct(; A=ı, B=ψ) => 1, - SectorProduct(; A=τ, B=ψ) => 1, - ]) - @test space_isequal(s ⊗ s, g) - end - @testset "Fusion of mixed Abelian and NonAbelian products" begin q0h = SectorProduct(; J=SU2(1//2)) q10 = (N=U1(1),) × (J=SU2(0),) @@ -460,46 +327,13 @@ end @test space_isequal(q11 ⊗ q11, gradedrange([q20 => 1, q21 => 1, q22 => 1])) end - @testset "Fusion of fully mixed products" begin - s = SectorProduct(; A=U1(1), B=SU2(1//2), C=Ising("σ")) - @test space_isequal( - s ⊗ s, - gradedrange([ - SectorProduct(; A=U1(2), B=SU2(0), C=Ising("1")) => 1, - SectorProduct(; A=U1(2), B=SU2(1), C=Ising("1")) => 1, - SectorProduct(; A=U1(2), B=SU2(0), C=Ising("ψ")) => 1, - SectorProduct(; A=U1(2), B=SU2(1), C=Ising("ψ")) => 1, - ]), - ) - - ı = Fib("1") - τ = Fib("τ") - s = SectorProduct(; A=SU2(1//2), B=U1(1), C=τ) - @test space_isequal( - s ⊗ s, - gradedrange([ - SectorProduct(; A=SU2(0), B=U1(2), C=ı) => 1, - SectorProduct(; A=SU2(1), B=U1(2), C=ı) => 1, - SectorProduct(; A=SU2(0), B=U1(2), C=τ) => 1, - SectorProduct(; A=SU2(1), B=U1(2), C=τ) => 1, - ]), - ) - - s = SectorProduct(; A=τ, B=U1(1), C=ı) - @test space_isequal( - s ⊗ s, - gradedrange([ - SectorProduct(; B=U1(2), A=ı, C=ı) => 1, SectorProduct(; B=U1(2), A=τ, C=ı) => 1 - ]), - ) - end @testset "GradedUnitRange fusion rules" begin - s1 = SectorProduct(; A=U1(1), B=SU2(1//2), C=Ising("σ")) - s2 = SectorProduct(; A=U1(0), B=SU2(1//2), C=Ising("1")) + s1 = SectorProduct(; A=U1(1), B=SU2(1//2)) + s2 = SectorProduct(; A=U1(0), B=SU2(1//2)) g1 = gradedrange([s1 => 2]) g2 = gradedrange([s2 => 1]) - s3 = SectorProduct(; A=U1(1), B=SU2(0), C=Ising("σ")) - s4 = SectorProduct(; A=U1(1), B=SU2(1), C=Ising("σ")) + s3 = SectorProduct(; A=U1(1), B=SU2(0)) + s4 = SectorProduct(; A=U1(1), B=SU2(1)) @test space_isequal(g1 ⊗ g2, gradedrange([s3 => 2, s4 => 2])) sA = SectorProduct(; A=U1(1)) @@ -559,20 +393,18 @@ end @test (@constinferred s ⊗ U1(1)) == st1 @test (@constinferred SU2(0) ⊗ s) == gradedrange([SectorProduct(SU2(0)) => 1]) @test (@constinferred s ⊗ SU2(0)) == gradedrange([SectorProduct(SU2(0)) => 1]) - @test (@constinferred Fib("τ") ⊗ s) == gradedrange([SectorProduct(Fib("τ")) => 1]) - @test (@constinferred s ⊗ Fib("τ")) == gradedrange([SectorProduct(Fib("τ")) => 1]) @test (@constinferred st1 ⊗ s) == st1 @test (@constinferred SectorProduct(SU2(0)) ⊗ s) == gradedrange([SectorProduct(SU2(0)) => 1]) - @test (@constinferred SectorProduct(Fib("τ"), SU2(1), U1(2)) ⊗ s) == - gradedrange([SectorProduct(Fib("τ"), SU2(1), U1(2)) => 1]) + @test (@constinferred SectorProduct(SU2(1), U1(2)) ⊗ s) == + gradedrange([SectorProduct(SU2(1), U1(2)) => 1]) @test (@constinferred sA1 ⊗ s) == sA1 @test (@constinferred SectorProduct(; A=SU2(0)) ⊗ s) == gradedrange([SectorProduct(; A=SU2(0)) => 1]) - @test (@constinferred SectorProduct(; A=Fib("τ"), B=SU2(1), C=U1(2)) ⊗ s) == - gradedrange([SectorProduct(; A=Fib("τ"), B=SU2(1), C=U1(2)) => 1]) + @test (@constinferred SectorProduct(; B=SU2(1), C=U1(2)) ⊗ s) == + gradedrange([SectorProduct(; B=SU2(1), C=U1(2)) => 1]) # Empty behaves as empty NamedTuple @test s != U1(0) From b9b51c558e7f1c9ca5d42d751cab685973581a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 18 Apr 2025 12:44:56 -0400 Subject: [PATCH 08/33] drop labels in combine_blockaxes --- src/gradedunitrange.jl | 29 +++++++---------------------- test/test_gradedunitrange.jl | 9 +++------ 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 578556f..e7f9727 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -342,30 +342,15 @@ function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<: return blockedunitrange_getindices(a, indices) end -# This fixes an issue that `combine_blockaxes` was promoting -# the element type of the axes to `Integer` in broadcasting operations -# that mixed dense and graded axes. -# TODO: Maybe come up with a more general solution. -function BlockArrays.combine_blockaxes( - a1::AbstractGradedUnitRange{T}, a2::AbstractUnitRange{T} -) where {T<:Integer} - combined_blocklasts = sort!(union(unlabel.(blocklasts(a1)), blocklasts(a2))) - return BlockedOneTo(combined_blocklasts) -end -function BlockArrays.combine_blockaxes( - a1::AbstractUnitRange{T}, a2::AbstractGradedUnitRange{T} -) where {T<:Integer} - return BlockArrays.combine_blockaxes(a2, a1) -end - -# preserve labels inside combine_blockaxes -function BlockArrays.combine_blockaxes(a::GradedOneTo, b::GradedOneTo) - return GradedUnitRange(sortedunion(blocklasts(a), blocklasts(b))) +function BlockArrays.combine_blockaxes(a1::AbstractGradedUnitRange, a2::AbstractUnitRange) + return BlockArrays.combine_blockaxes(a1, unlabel_blocks(a2)) end +function BlockArrays.combine_blockaxes(a1::AbstractUnitRange, a2::AbstractGradedUnitRange) + return BlockArrays.combine_blockaxes(a1, unlabel_blocks(a2)) +end + function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) - new_blocklasts = sortedunion(blocklasts(a), blocklasts(b)) - new_first = labelled(oneunit(eltype(new_blocklasts)), label(first(new_blocklasts))) - return GradedUnitRange(new_first, new_blocklasts) + return combine_blockaxes(unlabel_blocks(a), unlabel_blocks(b)) end # Version of length that checks that all blocks have the same label diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 09d57bc..766b677 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -76,12 +76,9 @@ using Test: @test, @test_broken, @testset @test axes(Base.Slice(g)) isa Tuple{typeof(g)} @test AbstractUnitRange{Int}(g) == 1:5 - @test_broken b = combine_blockaxes(g, g) # TODO - #= - @test b isa GradedOneTo - @test b == 1:5 - @test space_isequal(b, g) - =# + b = combine_blockaxes(g, g) + @test b isa BlockedOneTo + @test blockisequal(b, blockedrange([2, 3])) # Slicing operations g = gradedrange(["x" => 2, "y" => 3]) From f614922ca4832484cecadc44b0a8a63ef920bc73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 18 Apr 2025 14:00:48 -0400 Subject: [PATCH 09/33] test range cast convention --- test/test_gradedunitranges_tensor_product.jl | 82 ++++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/test/test_gradedunitranges_tensor_product.jl b/test/test_gradedunitranges_tensor_product.jl index e793d6f..9502ca5 100644 --- a/test/test_gradedunitranges_tensor_product.jl +++ b/test/test_gradedunitranges_tensor_product.jl @@ -3,15 +3,17 @@ using GradedArrays: GradedArrays, GradedOneTo, NotAbelianStyle, + SU2, U1, blocklabels, dual, unmerged_tensor_product, flip, gradedrange, + sectorrange, space_isequal, isdual -using TensorProducts: OneToOne, tensor_product +using TensorProducts: ⊗, OneToOne, tensor_product using Test: @test, @testset GradedArrays.SymmetryStyle(::Type{<:String}) = NotAbelianStyle() @@ -57,11 +59,9 @@ GradedArrays.tensor_product(s1::String, s2::String) = gradedrange([s1 * s2 => 1] @test space_isequal(unmerged_tensor_product(OneToOne(), a), a) @test space_isequal(tensor_product(a), gradedrange([U1(1) => 2, U1(2) => 3])) - @test space_isequal( - tensor_product(a, a), gradedrange([U1(2) => 4, U1(3) => 12, U1(4) => 9]) - ) - @test space_isequal(tensor_product(a, OneToOne()), gradedrange([U1(1) => 2, U1(2) => 3])) - @test space_isequal(tensor_product(OneToOne(), a), gradedrange([U1(1) => 2, U1(2) => 3])) + @test space_isequal(a ⊗ a, gradedrange([U1(2) => 4, U1(3) => 12, U1(4) => 9])) + @test space_isequal(a ⊗ OneToOne(), gradedrange([U1(1) => 2, U1(2) => 3])) + @test space_isequal(OneToOne() ⊗ a, gradedrange([U1(1) => 2, U1(2) => 3])) d = tensor_product(a, a, a) @test space_isequal(d, gradedrange([U1(3) => 8, U1(4) => 36, U1(5) => 54, U1(6) => 27])) @@ -95,3 +95,73 @@ end @test !isdual(d) @test space_isequal(e, d) end + +@testset "Abelian mixed axes and sectors" begin + s1 = U1(1) + s2 = U1(2) + sr1 = sectorrange(s1 => 1) + sr1d = dual(sr1) + g1 = gradedrange([s1 => 1]) + g1d = dual(g1) + + @test (@constinferred s1 ⊗ s1) isa AbstractSector + @test (@constinferred sr1 ⊗ sr1) isa SectorUnitRange + @test (@constinferred g1 ⊗ g1) isa GradedOneTo + + @test space_isequal(sr1 ⊗ sr1, sectorrange(s2 => 1)) + @test (@constinferred s1 ⊗ sr1) isa SectorUnitRange + @test space_isequal(s1 ⊗ sr1, sectorrange(s2 => 1)) + @test (@constinferred sr1 ⊗ s1) isa SectorUnitRange + @test space_isequal(sr1 ⊗ s1, sectorrange(s2 => 1)) + @test space_isequal(sr1d ⊗ s1, sectorrange(U1(0) => 1)) + @test space_isequal(sr1d ⊗ sr1, sectorrange(U1(0) => 1)) + @test space_isequal(sr1d ⊗ sr1d, sectorrange(U1(-2) => 1)) + + @test space_isequal(g1 ⊗ g1, gradedrange([s2 => 1])) + @test (@constinferred g1 ⊗ s1) isa GradedOneTo + @test space_isequal(g1 ⊗ s1, gradedrange([s2 => 1])) + @test (@constinferred s1 ⊗ g1) isa GradedOneTo + @test space_isequal(s1 ⊗ g1, gradedrange([s2 => 1])) + @test space_isequal(g1d ⊗ s1, gradedrange([U1(0) => 1])) + @test space_isequal(g1d ⊗ g1d, gradedrange([U1(-2) => 1])) + + @test (@constinferred g1 ⊗ sr1) isa GradedOneTo + @test space_isequal(g1 ⊗ sr1, gradedrange([s2 => 1])) + @test (@constinferred sr1 ⊗ g1) isa GradedOneTo + @test space_isequal(sr1 ⊗ g1, gradedrange([s2 => 1])) +end + +@testset "Non-abelian mixed axes and sectors" begin + s1 = SU2(1//2) + sr1 = sectorrange(s1 => 1) + sr1d = dual(sr1) + g1 = gradedrange([s1 => 1]) + g1d = dual(g1) + g2 = gradedrange([SU2(0) => 1, SU2(1) => 1]) + + @test (@constinferred s1 ⊗ s1) isa GradedOneTo + @test (@constinferred sr1 ⊗ sr1) isa GradedOneTo + @test (@constinferred g1 ⊗ g1) isa GradedOneTo + + @test space_isequal(sr1 ⊗ sr1, g2) + @test (@constinferred s1 ⊗ sr1) isa GradedOneTo + @test space_isequal(s1 ⊗ sr1, g2) + @test (@constinferred sr1 ⊗ s1) isa GradedOneTo + @test space_isequal(sr1 ⊗ s1, g2) + @test space_isequal(sr1d ⊗ s1, g2) + @test space_isequal(sr1d ⊗ sr1, g2) + @test space_isequal(sr1d ⊗ sr1d, g2) + + @test space_isequal(g1 ⊗ g1, g2) + @test (@constinferred g1 ⊗ s1) isa GradedOneTo + @test space_isequal(g1 ⊗ s1, g2) + @test (@constinferred s1 ⊗ g1) isa GradedOneTo + @test space_isequal(s1 ⊗ g1, g2) + @test space_isequal(g1d ⊗ s1, g2) + @test space_isequal(g1d ⊗ g1d, g2) + + @test (@constinferred g1 ⊗ sr1) isa GradedOneTo + @test space_isequal(g1 ⊗ sr1, g2) + @test (@constinferred sr1 ⊗ g1) isa GradedOneTo + @test space_isequal(sr1 ⊗ g1, g2) +end From f3b8c52d269b7422d4c4c913e9304a3f5e19bbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 18 Apr 2025 14:31:15 -0400 Subject: [PATCH 10/33] abelian slicing for SectorUnitRange --- src/sectorunitrange.jl | 7 +++++++ test/test_sectorunitrange.jl | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/sectorunitrange.jl b/src/sectorunitrange.jl index 1fe44d6..97ec292 100644 --- a/src/sectorunitrange.jl +++ b/src/sectorunitrange.jl @@ -60,9 +60,16 @@ Base.last(sr::SectorUnitRange) = last(full_range(sr)) # slicing Base.getindex(sr::SectorUnitRange, i::Integer) = full_range(sr)[i] + function Base.getindex(sr::SectorUnitRange, r::AbstractUnitRange{T}) where {T<:Integer} + return sr[SymmetryStyle(sr), r] +end +function Base.getindex(sr::SectorUnitRange, ::NotAbelianStyle, r::AbstractUnitRange) return full_range(sr)[r] end +function Base.getindex(sr::SectorUnitRange, ::AbelianStyle, r::AbstractUnitRange) + return sectorrange(nondual_sector(sr), full_range(sr)[r], isdual(sr)) +end # TODO replace (:,x) indexing with kronecker(:, x) Base.getindex(sr::SectorUnitRange, t::Tuple{Colon,<:Integer}) = sr[(:, last(t):last(t))] diff --git a/test/test_sectorunitrange.jl b/test/test_sectorunitrange.jl index 7b1102d..7e7325a 100644 --- a/test/test_sectorunitrange.jl +++ b/test/test_sectorunitrange.jl @@ -4,6 +4,7 @@ using BlockArrays: Block, blocklength, blocklengths, blockisequal, blocks using GradedArrays: AbstractSector, + U1, SU, SectorUnitRange, blocklabels, @@ -96,10 +97,12 @@ using GradedArrays: @test blocklabels(sr) == [SU((1, 0))] @test sector_multiplicity(sr) == 2 @test sector_multiplicities(sr) == [2] + @test quantum_dimension(sr) == 6 srd = dual(sr) @test nondual_sector(srd) == SU((1, 0)) @test space_isequal(srd, sectorrange(SU((1, 0)), 2, true)) + @test blocklabels(srd) == [SU((1, 1))] srf = flip(sr) @test nondual_sector(srf) == SU((1, 1)) @@ -108,17 +111,25 @@ using GradedArrays: # getindex @test_throws BoundsError sr[0] @test_throws BoundsError sr[7] + @test (@constinferred getindex(sr, 1)) isa Int64 for i in 1:6 @test sr[i] == i end @test sr[2:3] == 2:3 + @test (@constinferred getindex(sr, 2:3)) isa UnitRange @test sr[Block(1)] === sr @test_throws BlockBoundsError sr[Block(2)] - sr2 = sr[(:, 2)] + sr2 = (@constinferred getindex(sr, (:, 2))) @test sr2 isa SectorUnitRange @test space_isequal(sr2, sectorrange(SU((1, 0)), 4:6)) - sr3 = sr[(:, 1:2)] + sr3 = (@constinferred getindex(sr, (:, 1:2))) @test sr3 isa SectorUnitRange @test space_isequal(sr3, sectorrange(SU((1, 0)), 1:6)) + + # Abelian slicing + srab = sectorrange(U1(1), 3) + @test (@constinferred getindex(srab, 2:2)) isa SectorUnitRange + @test space_isequal(srab[2:2], sectorrange(U1(1), 2:2)) + @test space_isequal(dual(srab)[2:2], sectorrange(U1(1), 2:2, true)) end From 5ca9d6feb6ed06bf924fb8582cc6936df9704c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 18 Apr 2025 17:12:49 -0400 Subject: [PATCH 11/33] remove unused methods --- src/gradedunitrange.jl | 113 +++++++------------------------ src/gradedunitrange_interface.jl | 2 +- src/symmetry_style.jl | 2 +- test/test_gradedunitrange.jl | 3 + 4 files changed, 30 insertions(+), 90 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index e7f9727..091dd63 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -9,25 +9,19 @@ using BlockArrays: BlockSlice, BlockVector, BlockedOneTo, - BlockedUnitRange, block, blockedrange, - blockfirsts, - blockisequal, blocklasts, - blocklength, blocklengths, blocks, blockindex, - combine_blockaxes, - mortar, - sortedunion + findblock, + mortar using BlockSparseArrays: BlockSparseArrays, blockedunitrange_findblock, blockedunitrange_findblockindex, blockedunitrange_getindices -using Compat: allequal abstract type AbstractGradedUnitRange{T,BlockLasts} <: AbstractBlockedUnitRange{T,BlockLasts} end @@ -87,22 +81,21 @@ function gradedrange( return axis_cat(sectors) end -# GradedUnitRange interface +### GradedUnitRange interface dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), unlabel_blocks(g)) -isdual(g::GradedUnitRange) = isdual(first(sector_axes(g))) # crash for empty. Should not be an issue. +isdual(g::AbstractGradedUnitRange) = isdual(first(sector_axes(g))) # crash for empty. Should not be an issue. function blocklabels(g::AbstractGradedUnitRange) nondual_blocklabels = nondual_sector.(sector_axes(g)) return isdual(g) ? dual.(nondual_blocklabels) : nondual_blocklabels end -function map_blocklabels(f, g::AbstractGradedUnitRange) - # use labelled_blocks to preserve GradedUnitRange +function map_blocklabels(f, g::GradedUnitRange) return GradedUnitRange(map_blocklabels.(f, sector_axes(g)), unlabel_blocks(g)) end -# Base interface +### Base interface # needed in BlockSparseArrays function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} @@ -110,7 +103,7 @@ function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} end function Base.axes(ga::AbstractGradedUnitRange) - return (GradedUnitRange(sector_axes(ga), blockedrange(blocklengths(ga))),) + return (axis_cat(sector_axes(ga)),) end # preserve axes in SubArray @@ -126,53 +119,15 @@ function Base.show(io::IO, g::AbstractGradedUnitRange) return print(io, nameof(typeof(g)), '[', join(repr.(v), ", "), ']') end -Base.last(a::AbstractGradedUnitRange) = last(unlabel_blocks(a)) +Base.first(a::AbstractGradedUnitRange) = first(unlabel_blocks(a)) -# TODO: Use `TypeParameterAccessors`. -Base.eltype(::Type{<:GradedUnitRange{T}}) where {T} = T - -#= -function labelled_blocks(a::BlockedOneTo, labels) - # TODO: Use `blocklasts(a)`? That might - # cause a recursive loop. - return GradedOneTo(labelled.(a.lasts, labels)) -end -function labelled_blocks(a::BlockedUnitRange, labels) - # TODO: Use `first(a)` and `blocklasts(a)`? Those might - # cause a recursive loop. - return GradedUnitRange(labelled(a.first, labels[1]), labelled.(a.lasts, labels)) -end -=# - -function Base.first(a::AbstractGradedUnitRange) - return first(unlabel_blocks(a)) -end - -Base.iterate(a::AbstractGradedUnitRange) = iterate(unlabel_blocks(a)) -Base.iterate(a::AbstractGradedUnitRange, i) = iterate(unlabel_blocks(a), i) - -# BlockArrays interface - -function BlockArrays.findblock(a::AbstractGradedUnitRange, index::Integer) - return blockedunitrange_findblock(unlabel_blocks(a), index) -end - -function gradedunitrange_blockfirsts(a::AbstractGradedUnitRange) - return blockfirsts(unlabel_blocks(a)) -end -function BlockArrays.blockfirsts(a::AbstractGradedUnitRange) - return gradedunitrange_blockfirsts(a) -end +### BlockArrays interface function BlockArrays.blocklasts(a::AbstractGradedUnitRange) return blocklasts(unlabel_blocks(a)) end -function BlockArrays.blocklengths(a::AbstractGradedUnitRange) - return blocklengths(unlabel_blocks(a)) -end - -# BlockSparseArrays interface +### BlockSparseArrays interface function BlockSparseArrays.blockedunitrange_findblock( a::AbstractGradedUnitRange, index::Integer @@ -190,13 +145,6 @@ function BlockArrays.findblockindex(a::AbstractGradedUnitRange, index::Integer) return blockedunitrange_findblockindex(unlabel_blocks(a), index) end -## BlockedUnitRange interface - -# TBD remove -#function firstblockindices(a::AbstractGradedUnitRange) -# return labelled.(firstblockindices(unlabel_blocks(a)), blocklabels(a)) -#end - function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, index::Block{1} ) @@ -217,10 +165,6 @@ function BlockSparseArrays.blockedunitrange_getindices( return mortar(map(b -> a[b], blocks(indices))) end -function BlockSparseArrays.blockedunitrange_getindices(a::AbstractGradedUnitRange, index) - return labelled(unlabel_blocks(a)[index], get_label(a, index)) -end - function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, indices::BlockIndexRange{1} ) @@ -249,9 +193,20 @@ function BlockSparseArrays.blockedunitrange_getindices( end function BlockSparseArrays.blockedunitrange_getindices( - ga::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} + ::AbelianStyle, g::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} +) + r = blockedunitrange_getindices(NotAbelianStyle(), g, indices) + i = Int(findblock(g, first(indices))) + j = Int(findblock(g, last(indices))) + labels = nondual_sector.(sector_axes(g))[i:j] + new_axes = sectorrange.(labels .=> Base.oneto.(blocklengths(r)), isdual(g)) + return GradedUnitRange(new_axes, r) +end + +function BlockSparseArrays.blockedunitrange_getindices( + ::NotAbelianStyle, g::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} ) - return blockedunitrange_getindices(unlabel_blocks(ga), indices) + return blockedunitrange_getindices(unlabel_blocks(g), indices) end function BlockSparseArrays.blockedunitrange_getindices( @@ -272,6 +227,7 @@ function BlockSparseArrays.blockedunitrange_getindices( return a[block(indices)][blockindex(indices)] end +### Slicing function Base.getindex(a::AbstractGradedUnitRange, index::Integer) return unlabel_blocks(a)[index] end @@ -339,26 +295,7 @@ end Base.getindex(g::AbstractGradedUnitRange, ::Colon) = g function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer}) - return blockedunitrange_getindices(a, indices) -end - -function BlockArrays.combine_blockaxes(a1::AbstractGradedUnitRange, a2::AbstractUnitRange) - return BlockArrays.combine_blockaxes(a1, unlabel_blocks(a2)) -end -function BlockArrays.combine_blockaxes(a1::AbstractUnitRange, a2::AbstractGradedUnitRange) - return BlockArrays.combine_blockaxes(a1, unlabel_blocks(a2)) -end - -function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) - return combine_blockaxes(unlabel_blocks(a), unlabel_blocks(b)) -end - -# Version of length that checks that all blocks have the same label -# and returns a labelled length with that label. -function labelled_length(a::AbstractBlockVector{<:Integer}) - blocklabels = label.(blocks(a)) - @assert allequal(blocklabels) - return labelled(unlabel(length(a)), first(blocklabels)) + return blockedunitrange_getindices(SymmetryStyle(a), a, indices) end # TODO: Make sure this handles block labels (AbstractGradedUnitRange) correctly. diff --git a/src/gradedunitrange_interface.jl b/src/gradedunitrange_interface.jl index 7138ef5..4084c6a 100644 --- a/src/gradedunitrange_interface.jl +++ b/src/gradedunitrange_interface.jl @@ -1,4 +1,4 @@ -using BlockArrays: AbstractBlockVector, BlockRange, blocklength +using BlockArrays: AbstractBlockVector, BlockRange, blockisequal, blocklength using FillArrays: Fill """ diff --git a/src/symmetry_style.jl b/src/symmetry_style.jl index cfb022e..f79cbb7 100644 --- a/src/symmetry_style.jl +++ b/src/symmetry_style.jl @@ -7,7 +7,7 @@ struct AbelianStyle <: SymmetryStyle end struct NotAbelianStyle <: SymmetryStyle end SymmetryStyle(x) = SymmetryStyle(typeof(x)) -SymmetryStyle(T::Type) = error("method `SymmetryStyle` not defined for type $(T)") +SymmetryStyle(T::Type) = AbelianStyle() # default SymmetryStyle(G::Type{<:AbstractUnitRange}) = SymmetryStyle(sector_type(G)) combine_styles(::AbelianStyle, ::AbelianStyle) = AbelianStyle() diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 766b677..7d6d691 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -1,9 +1,12 @@ using BlockArrays: Block, BlockSlice, + BlockedOneTo, BlockVector, + BlockedUnitRange, blockedrange, blockfirsts, + blockisequal, blocklasts, blocklength, blocklengths, From 1d6e8cba4ca2cdf60bf7740b6823c2e1725696f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Mon, 21 Apr 2025 18:53:10 -0400 Subject: [PATCH 12/33] more extensive tests --- src/symmetry_style.jl | 6 +- test/test_gradedunitrange.jl | 339 ++++++++++++++++--------- test/test_gradedunitrange_interface.jl | 2 +- 3 files changed, 224 insertions(+), 123 deletions(-) diff --git a/src/symmetry_style.jl b/src/symmetry_style.jl index f79cbb7..17b7eee 100644 --- a/src/symmetry_style.jl +++ b/src/symmetry_style.jl @@ -7,7 +7,11 @@ struct AbelianStyle <: SymmetryStyle end struct NotAbelianStyle <: SymmetryStyle end SymmetryStyle(x) = SymmetryStyle(typeof(x)) -SymmetryStyle(T::Type) = AbelianStyle() # default + +# default SymmetryStyle to AbelianStyle +# allows for abelian-like slicing style for GradedUnitRange: assume length(::label) = 1 +# and preserve labels in any slicing operation +SymmetryStyle(T::Type) = AbelianStyle() SymmetryStyle(G::Type{<:AbstractUnitRange}) = SymmetryStyle(sector_type(G)) combine_styles(::AbelianStyle, ::AbelianStyle) = AbelianStyle() diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 7d6d691..81b9045 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -4,6 +4,7 @@ using BlockArrays: BlockedOneTo, BlockVector, BlockedUnitRange, + BlockedVector, blockedrange, blockfirsts, blockisequal, @@ -12,12 +13,16 @@ using BlockArrays: blocklengths, blocks, combine_blockaxes, + findblock, + findblockindex, mortar using GradedArrays: GradedArrays, GradedOneTo, GradedUnitRange, + SectorOneTo, SectorUnitRange, + SU, blocklabels, dual, flip, @@ -31,158 +36,250 @@ using Test: @test, @test_broken, @testset @testset "GradedUnitRanges basics" begin r0 = Base.OneTo(1) - g = gradedrange(["x" => 2, "y" => 3]) - @test g isa GradedOneTo - @test g isa GradedUnitRange - @test sector_type(g) === String - @test space_isequal(g, g) - @test !space_isequal(r0, g) - @test !space_isequal(g, r0) - @test !space_isequal(g, 1:5) - for x in iterate(g) - @test x == 1 - end - for x in iterate(g, 1) - @test x == 2 - end - for x in iterate(g, 2) - @test x == 3 + b0 = blockedrange([2, 3, 2]) + g1 = gradedrange(["x" => 2, "y" => 3, "z" => 2]) + @test g1 isa GradedOneTo + @test !isdual(g1) + + g2 = g1[1:7] + @test !(g2 isa GradedOneTo) + @test !isdual(g2) + + g1d = dual(g1) + @test g1d isa GradedOneTo + @test isdual(g1d) + @test space_isequal(dual(g1d), g1) + + for b1 in (g1, g1d, b0), b2 in (g1, g1d, b0) + a = combine_blockaxes(b1, b2) + @test a isa BlockedOneTo + @test blockisequal(a, b0) end - for x in iterate(g, 3) - @test x == 4 + + for b in (g1, g2, g1d, b0) + a = combine_blockaxes(g2, b) + @test a isa BlockedUnitRange + @test blockisequal(a, b0) end - for x in iterate(g, 4) - @test x == 5 + + for g in (g1, g2, g1d) + @test g isa GradedUnitRange + @test sector_type(g) === String + @test blockisequal(g, b0) + @test space_isequal(g, g) + @test !space_isequal(r0, g) + @test !space_isequal(g, r0) + @test !space_isequal(g, b0) + @test !space_isequal(g, 1:7) + @test !space_isequal(g, dual(g)) + @test g == 1:7 + for x in iterate(g) + @test x == 1 + end + for x in iterate(g, 1) + @test x == 2 + end + for x in iterate(g, 2) + @test x == 3 + end + for x in iterate(g, 3) + @test x == 4 + end + for x in iterate(g, 4) + @test x == 5 + end + for x in iterate(g, 5) + @test x == 6 + end + for x in iterate(g, 6) + @test x == 7 + end + @test isnothing(iterate(g, 7)) + @test length(g) == 7 + @test step(g) == 1 + @test blocklength(g) == 3 + @test length(blocks(g)) == 3 + + @test g[Block(1)] isa SectorUnitRange + @test !(g[Block(1)] isa SectorOneTo) + @test space_isequal(g[Block(1)], sectorrange("x", 1:2, isdual(g))) + @test space_isequal(g[Block(2)], sectorrange("y", 3:5, isdual(g))) + @test space_isequal(g[Block(3)], sectorrange("z", 6:7, isdual(g))) + + @test g[4] == 4 + @test blocklengths(g) == [2, 3, 2] + @test blocklabels(g) == ["x", "y", "z"] + @test blockfirsts(g) == [1, 3, 6] + @test first(g) == 1 + @test blocklasts(g) == [2, 5, 7] + @test last(g) == 7 + @test blocklengths(only(axes(g))) == blocklengths(g) + @test blocklabels(only(axes(g))) == blocklabels(g) + @test findblock(g, 2) == Block(1) + @test findblock(g, 3) == Block(2) + @test findblockindex(g, 3) == Block(2)[1] + + @test axes(Base.Slice(g)) isa Tuple{typeof(g)} + @test AbstractUnitRange{Int}(g) == 1:7 + + @test g[Block(1)[1]] == 1 + @test g[Block(2)[1]] == 3 + + # Abelian slicing operations + a = g[2:4] + @test a isa GradedUnitRange + @test !(a isa GradedOneTo) + @test blocklabels(a) == ["x", "y"] + @test blocklength(a) == 2 + @test space_isequal(first(blocks(a)), sectorrange("x", 2:2, isdual(g))) + @test space_isequal(last(blocks(a)), sectorrange("y", 3:4, isdual(g))) + @test g[[2, 4]] == [2, 4] + + # Regression test for ambiguity errors. + a = g[BlockSlice(Block(1), Base.OneTo(2))] + @test length(a) == 2 + @test a == 1:2 + @test blocklength(a) == 1 + @test a isa SectorUnitRange + @test space_isequal(a, sectorrange("x" => 2, isdual(g))) + @test space_isequal(g, g[:]) + @test typeof(g[:]) === typeof(g) + + a = g[Block(2)[2:3]] + @test a isa SectorUnitRange + @test space_isequal(a, sectorrange("y", 4:5, isdual(g))) + + a = g[Block(2):Block(3)] + @test a isa GradedUnitRange + @test !(a isa GradedOneTo) + @test blocklength(a) == 2 + @test space_isequal(a[Block(1)], sectorrange("y", 3:5, isdual(g))) + @test space_isequal(a[Block(2)], sectorrange("z", 6:7, isdual(g))) + + ax = only(axes(a)) + @test ax isa GradedOneTo + @test space_isequal(ax, gradedrange(["y" => 3, "z" => 2]; isdual=isdual(g))) + @test ax == 1:length(a) + @test length(ax) == length(a) + @test blocklengths(ax) == blocklengths(a) + @test blocklabels(ax) == blocklabels(a) + + a = g[[Block(3), Block(2)]] + @test a isa BlockVector + @test length(a) == 5 + @test blocklength(a) == 2 + # TODO: `BlockArrays` doesn't define `blocklengths` + # `blocklengths(::BlockVector)`, unbrake this test + # once it does. + @test_broken blocklengths(a) == [2, 3] + @test blocklabels(a) == ["z", "y"] + @test a[Block(1)] == 6:7 + @test a[Block(2)] == 3:5 + ax = only(axes(a)) + @test ax isa GradedOneTo + @test space_isequal(ax, gradedrange(["z" => 2, "y" => 3]; isdual=isdual(g))) + + a = g[[Block(3)[1:1], Block(2)[2:3]]] + @test a isa BlockVector + @test length(a) == 3 + @test blocklength(a) == 2 + # TODO: `BlockArrays` doesn't define `blocklengths` + # for `BlockVector`, should it? + @test_broken blocklengths(a) == [1, 2] + @test a[Block(1)] == 6:6 + @test a[Block(2)] == 4:5 + ax = only(axes(a)) + @test ax isa GradedOneTo + @test space_isequal(ax, gradedrange(["z" => 1, "y" => 2]; isdual=isdual(g))) + + I = mortar([Block(1)[1:1]]) + a = g[I] + @test a isa BlockedVector + @test length(a) == 1 + @test blocklength(a) == 1 end - @test isnothing(iterate(g, 5)) - @test length(g) == 5 - @test step(g) == 1 - @test length(blocks(g)) == 2 - @test blocks(g)[1] == 1:2 - @test nondual_sector(blocks(g)[1]) == "x" - @test blocks(g)[2] == 3:5 - @test nondual_sector(blocks(g)[2]) == "y" +end - @test g[Block(2)] isa SectorUnitRange - @test space_isequal(g[Block(2)], sectorrange("y", 3:5)) +@testset "Non abelian axis" begin + b0 = blockedrange([2, 6]) + g = gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 2]) - @test g[4] == 4 - @test blocklengths(g) == [2, 3] - @test blocklabels(g) == ["x", "y"] - @test blockfirsts(g) == [1, 3] - @test first(g) == 1 - @test blocklasts(g) == [2, 5] - @test last(g) == 5 - @test blocklengths(only(axes(g))) == blocklengths(g) - @test blocklabels(only(axes(g))) == blocklabels(g) + @test g isa GradedOneTo + @test length(g) == 8 @test !isdual(g) + @test blockisequal(g, b0) + @test blocklabels(g) == [SU((0, 0)), SU((1, 0))] + @test space_isequal(g[Block(1)], sectorrange(SU((0, 0)), 2)) + @test space_isequal(g[Block(2)], sectorrange(SU((1, 0)), 3:8)) - @test axes(Base.Slice(g)) isa Tuple{typeof(g)} - @test AbstractUnitRange{Int}(g) == 1:5 - b = combine_blockaxes(g, g) - @test b isa BlockedOneTo - @test blockisequal(b, blockedrange([2, 3])) + @test sector_type(g) === SU{3,2} + @test space_isequal(g, g) + @test g == 1:8 + @test space_isequal(dual(g), gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 2]; isdual=true)) + @test !space_isequal(dual(g), g) + @test space_isequal(flip(g), gradedrange([SU((0, 0)) => 2, SU((1, 1)) => 2]; isdual=true)) - # Slicing operations - g = gradedrange(["x" => 2, "y" => 3]) + iterate(g) == (1, 1) + for i in 1:7 + @test iterate(g, i) == (i + 1, i + 1) + end + @test isnothing(iterate(g, 8)) + @test step(g) == 1 + + @test g[4] == 4 + @test g[Block(1)[1]] == 1 + @test g[Block(2)[1]] == 3 + + # Non-abelian slicing operations a = g[2:4] @test a isa BlockedUnitRange - @test blockisequal(a, blockedrange([1, 1, 2])[Block.(2:3)]) - @test g[[2, 4]] == [2, 4] + @test blockisequal(a, blockedrange([1, 1, 2])[Block(2):Block(3)]) # Regression test for ambiguity errors. - g = gradedrange(["x" => 2, "y" => 3]) a = g[BlockSlice(Block(1), Base.OneTo(2))] - @test length(a) == 2 - @test a == 1:2 - @test blocklength(a) == 1 @test a isa SectorUnitRange - @test space_isequal(a, sectorrange("x" => 2)) + @test space_isequal(a, sectorrange(SU((0, 0)) => 2)) @test space_isequal(g, g[:]) + @test typeof(g[:]) === typeof(g) - g = gradedrange(["x" => 2, "y" => 3]) - a = g[3:4] - @test a isa BlockedUnitRange - @test blockisequal(a, blockedrange([2, 2])[Block.(2:2)]) - - g = gradedrange(["x" => 2, "y" => 3]) a = g[Block(2)[2:3]] @test a isa UnitRange @test a == 4:5 - g = gradedrange(["x" => 2, "y" => 3, "z" => 4]) - a = g[Block(2):Block(3)] + a = g[Block(2):Block(2)] @test a isa GradedUnitRange - @test length(a) == 7 - @test blocklength(a) == 2 - @test blocklengths(a) == [3, 4] - @test blocklabels(a) == ["y", "z"] - @test a[Block(1)] == 3:5 - @test a[Block(2)] == 6:9 + @test !(a isa GradedOneTo) + @test space_isequal(only(blocks(a)), sectorrange(SU((1, 0)), 3:8)) ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - @test blocklengths(ax) == blocklengths(a) - @test blocklabels(ax) == blocklabels(a) + @test ax isa GradedOneTo + @test space_isequal(ax, gradedrange([SU((1, 0)) => 2])) - g = gradedrange(["x" => 2, "y" => 3, "z" => 4]) - a = g[[Block(3), Block(2)]] + a = g[[Block(2), Block(1)]] @test a isa BlockVector - @test length(a) == 7 + @test_broken length(a) == 8 @test blocklength(a) == 2 - # TODO: `BlockArrays` doesn't define `blocklengths` - # `blocklengths(::BlockVector)`, unbrake this test - # once it does. - @test_broken blocklengths(a) == [4, 3] - @test blocklabels(a) == ["z", "y"] - @test a[Block(1)] == 6:9 - @test a[Block(2)] == 3:5 + @test blocklabels(a) == [SU((1, 0)), SU((0, 0))] + @test_broken length.(blocks(g)) == (6, 2) + + @test space_isequal(a[Block(1)], sectorrange(SU((1, 0)), 3:8)) + @test space_isequal(a[Block(2)], sectorrange(SU((0, 0)), 2)) ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - # TODO: Change to: - # @test blocklengths(ax) == blocklengths(a) - # once `blocklengths(::BlockVector)` is defined. - @test blocklengths(ax) == [4, 3] - @test blocklabels(ax) == blocklabels(a) - - g = gradedrange(["x" => 2, "y" => 3, "z" => 4]) - a = g[[Block(3)[2:3], Block(2)[2:3]]] # drop labels + @test ax isa GradedOneTo + @test_broken space_isequal(ax, gradedrange([SU((1, 0)) => 2, SU((0, 0)) => 3])) + + a = g[[Block(2)[1:3], Block(1)[2:2]]] @test a isa BlockVector @test length(a) == 4 @test blocklength(a) == 2 - # TODO: `BlockArrays` doesn't define `blocklengths` - # for `BlockVector`, should it? - @test_broken blocklengths(a) == [2, 2] - @test a[Block(1)] == 7:8 - @test a[Block(2)] == 4:5 + @test a[Block(1)] == 3:5 + @test a[Block(2)] == 2:2 ax = only(axes(a)) - @test ax == 1:length(a) - @test length(ax) == length(a) - # TODO: Change to: - # @test blocklengths(ax) == blocklengths(a) - # once `blocklengths(::BlockVector)` is defined. - @test blocklengths(ax) == [2, 2] - - g = gradedrange(["x" => 2, "y" => 3]) + @test ax isa BlockedOneTo + @test blockisequal(ax, blockedrange([3, 1])) + I = mortar([Block(1)[1:1]]) a = g[I] + @test a isa BlockedVector @test length(a) == 1 - - gd = gradedrange(["x" => 2, "y" => 3], dual=true) - @test isdual(gd) - @test gd[Block(2)] isa SectorUnitRange - @test space_isequal(gd[Block(2)], sectorrange("y", 3:5, true)) - @test blocklabels(g) == blocklabels(gd) # string is self-dual - @test !space_isequal(gd, g) - @test space_isequal(gd, dual(g)) - @test space_isequal(gd, flip(g)) - @test space_isequal(flip(gd), g) - @test space_isequal(dual(gd), g) - - # label length > 1 - g = gradedrange(["x" => 2, "yy" => 3]) - @test length(g) == 8 - @test blocklengths(g) == [2, 6] - @test space_isequal(g[Block(2)], sectorrange("yy", 3:8)) + @test blocklength(a) == 1 end diff --git a/test/test_gradedunitrange_interface.jl b/test/test_gradedunitrange_interface.jl index 8b01238..5dda4cb 100644 --- a/test/test_gradedunitrange_interface.jl +++ b/test/test_gradedunitrange_interface.jl @@ -4,7 +4,7 @@ using GradedArrays: NoLabel, blocklabels, dag, dual, flip, isdual, space_isequal using Test: @test, @testset using TensorProducts: OneToOne -@testset "AbstractUnitRange" begin +@testset "GradedUnitRange interface for AbstractUnitRange" begin a0 = OneToOne() @test !isdual(a0) @test dual(a0) isa OneToOne From 53d94cfe6eb3b2781ed6b34aac5aeadc1acd449a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 11:06:16 -0400 Subject: [PATCH 13/33] working GradedUnitRange --- src/gradedarray.jl | 19 +- src/gradedunitrange.jl | 185 ++++++++++++------- test/test_gradedarray.jl | 119 ++++++------ test/test_gradedunitrange.jl | 71 +++++-- test/test_gradedunitrange_interface.jl | 2 +- test/test_gradedunitranges_tensor_product.jl | 4 +- test/test_sectorunitrange.jl | 14 +- test/test_symmetrysectors_fusion_rules.jl | 6 +- test/test_symmetrysectors_sector_product.jl | 1 + test/test_tensoralgebraext.jl | 27 ++- 10 files changed, 288 insertions(+), 160 deletions(-) diff --git a/src/gradedarray.jl b/src/gradedarray.jl index 11a283c..6c02a01 100644 --- a/src/gradedarray.jl +++ b/src/gradedarray.jl @@ -13,8 +13,12 @@ using TypeParameterAccessors: similartype, unwrap_array_type const GradedArray{T,M,A,Blocks,Axes} = BlockSparseArray{ T,M,A,Blocks,Axes } where {Axes<:Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}}} -const GradedMatrix{T,A,Blocks,Axes} = GradedArray{T,2,A,Blocks,Axes} -const GradedVector{T,A,Blocks,Axes} = GradedArray{T,1,A,Blocks,Axes} +const GradedMatrix{T,A,Blocks,Axes} = GradedArray{ + T,2,A,Blocks,Axes +} where {Axes<:Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}}} +const GradedVector{T,A,Blocks,Axes} = GradedArray{ + T,1,A,Blocks,Axes +} where {Axes<:Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}}} # TODO: Handle this through some kind of trait dispatch, maybe # a `SymmetryStyle`-like trait to check if the block sparse @@ -41,11 +45,12 @@ function similar_blocksparse( ) # TODO: Probably need to unwrap the type of `a` in certain cases # to make a proper block type. - return BlockSparseArray{ + aaa = BlockSparseArray{ elt,length(axes),similartype(unwrap_array_type(blocktype(a)), elt, axes) }( undef, axes ) + return aaa end function Base.similar( @@ -64,7 +69,7 @@ function Base.similar( ) return similar_blocksparse(a, elt, axes) end - +#= # TBD needed? => delete # Fix ambiguity error with `BlockArrays.jl`. function Base.similar( a::StridedArray, @@ -74,9 +79,10 @@ function Base.similar( }, ) return similar_blocksparse(a, elt, axes) -end +end=# # Fix ambiguity error with `BlockSparseArrays.jl`. +# TBD DerivableInterfaces? function Base.similar( a::AnyAbstractBlockSparseArray, elt::Type, @@ -84,6 +90,7 @@ function Base.similar( ) return similar_blocksparse(a, elt, axes) end +#= # TBD needed? => delete function Base.similar( a::AnyAbstractBlockSparseArray, elt::Type, @@ -92,7 +99,7 @@ function Base.similar( }, ) return similar_blocksparse(a, elt, axes) -end +end=# function Base.zeros( elt::Type, ax::Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}} diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 091dd63..61d4a4d 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -15,6 +15,7 @@ using BlockArrays: blocklengths, blocks, blockindex, + combine_blockaxes, findblock, mortar using BlockSparseArrays: @@ -22,6 +23,7 @@ using BlockSparseArrays: blockedunitrange_findblock, blockedunitrange_findblockindex, blockedunitrange_getindices +using Compat: allequal abstract type AbstractGradedUnitRange{T,BlockLasts} <: AbstractBlockedUnitRange{T,BlockLasts} end @@ -36,6 +38,8 @@ struct GradedUnitRange{T,SUR<:SectorOneTo{T},BR<:AbstractUnitRange{T},BlockLasts ) where {T,SUR,BR,BlockLasts} length.(sector_axes) == blocklengths(full_range) || throw(ArgumentError("sectors and range are not compatible")) + allequal(isdual.(sector_axes)) || + throw(ArgumentError("all blocks must have same duality")) typeof(blocklasts(full_range)) == BlockLasts || throw(TypeError(:BlockLasts, "", blocklasts(full_range))) return new{T,SUR,BR,BlockLasts}(sector_axes, full_range) @@ -55,7 +59,7 @@ end # Accessors sector_axes(g::GradedUnitRange) = g.sector_axes -unlabel_blocks(g::GradedUnitRange) = g.full_range # TBD use full_range? +unlabel_blocks(g::GradedUnitRange) = g.full_range # TBD rename full_range? sector_multiplicities(g::GradedUnitRange) = sector_multiplicity.(sector_axes(g)) @@ -75,9 +79,9 @@ function axis_cat(gaxes::AbstractVector{<:GradedOneTo}) end function gradedrange( - lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; dual::Bool=false + lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; isdual::Bool=false ) - sectors = sectorrange.(lblocklengths, dual) + sectors = sectorrange.(lblocklengths, isdual) return axis_cat(sectors) end @@ -86,6 +90,10 @@ dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), unlabel_blocks isdual(g::AbstractGradedUnitRange) = isdual(first(sector_axes(g))) # crash for empty. Should not be an issue. +# TBD remove? No use if change blocklabels convention +nondual(g::AbstractGradedUnitRange) = isdual(g) ? dual(g) : g + +# TBD: change convention to nondual sectors? function blocklabels(g::AbstractGradedUnitRange) nondual_blocklabels = nondual_sector.(sector_axes(g)) return isdual(g) ? dual.(nondual_blocklabels) : nondual_blocklabels @@ -95,6 +103,44 @@ function map_blocklabels(f, g::GradedUnitRange) return GradedUnitRange(map_blocklabels.(f, sector_axes(g)), unlabel_blocks(g)) end +### GradedUnitRange specific slicing +function gradedunitrange_getindices( + ::AbelianStyle, g::AbstractUnitRange, indices::AbstractVector{<:BlockIndexRange{1}} +) + gblocks = map(index -> g[index], Vector(indices)) + # pass block labels to the axes of the output, + # such that `only(axes(g[indices])) isa `GradedOneTo` + newg = axis_cat(sectorrange.(nondual_sector.(gblocks) .=> length.(gblocks), isdual(g))) + return mortar(gblocks, (newg,)) +end + +function gradedunitrange_getindices( + ::AbelianStyle, g::AbstractUnitRange, indices::AbstractUnitRange{<:Integer} +) + r = blockedunitrange_getindices(unlabel_blocks(g), indices) + i = Int(findblock(g, first(indices))) + j = Int(findblock(g, last(indices))) + labels = nondual_sector.(sector_axes(g))[i:j] + new_axes = sectorrange.(labels .=> Base.oneto.(blocklengths(r)), isdual(g)) + return GradedUnitRange(new_axes, r) +end + +function gradedunitrange_getindices( + ::AbelianStyle, g::AbstractGradedUnitRange, indices::BlockVector{<:BlockIndex{1}} +) + newg = gradedrange( + map(b -> nondual_sector(g[b]), block.(indices)) .=> length.(blocks(indices)); + isdual=isdual(g), + ) + v = mortar(map(b -> g[b], blocks(indices)), (newg,)) + return v +end + +# need to drop label in some non-abelian slicing +function gradedunitrange_getindices(::NotAbelianStyle, g::AbstractUnitRange, indices) + return blockedunitrange_getindices(unlabel_blocks(g), indices) +end + ### Base interface # needed in BlockSparseArrays @@ -127,24 +173,30 @@ function BlockArrays.blocklasts(a::AbstractGradedUnitRange) return blocklasts(unlabel_blocks(a)) end -### BlockSparseArrays interface - -function BlockSparseArrays.blockedunitrange_findblock( - a::AbstractGradedUnitRange, index::Integer -) - return blockedunitrange_findblock(unlabel_blocks(a), index) +function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) + # avoid mixing different labels + # better to throw explicit error than silently dropping labels + !space_isequal(a, b) && throw(ArgumentError("axes are not compatible")) + #!space_isequal(a, b) && return combine_blockaxes(unlabel_blocks(a), unlabel_blocks(b)) + # preserve BlockArrays convention for BlockedUnitRange / BlockedOneTo + return GradedUnitRange( + sector_axes(a), combine_blockaxes(unlabel_blocks(a), unlabel_blocks(b)) + ) end -function BlockSparseArrays.blockedunitrange_findblockindex( - a::AbstractGradedUnitRange, index::Integer -) - return blockedunitrange_findblockindex(unlabel_blocks(a), index) +# preserve BlockedOneTo when possible +function BlockArrays.combine_blockaxes(a::AbstractGradedUnitRange, b::AbstractUnitRange) + return combine_blockaxes(unlabel_blocks(a), b) end - -function BlockArrays.findblockindex(a::AbstractGradedUnitRange, index::Integer) - return blockedunitrange_findblockindex(unlabel_blocks(a), index) +function BlockArrays.combine_blockaxes(a::AbstractUnitRange, b::AbstractGradedUnitRange) + return combine_blockaxes(a, unlabel_blocks(b)) end +### BlockSparseArrays interface + +# BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex +# and blockedunitrange_getindices must be defined + function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, index::Block{1} ) @@ -152,28 +204,33 @@ function BlockSparseArrays.blockedunitrange_getindices( return sectorrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) end +# NEVER CALLED TBD REMOVE function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, indices::Vector{<:Integer} ) - return map(index -> a[index], indices) + return gradedunitrange_getindice(SymmetryStyle(a), a, indices) end +# used in BlockSparseArrays function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, + g::AbstractGradedUnitRange, indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, ) - return mortar(map(b -> a[b], blocks(indices))) + return gradedunitrange_getindices(SymmetryStyle(g), g, indices) end -function BlockSparseArrays.blockedunitrange_getindices( +# a priori pas besoin, on peut utiliser la version generique +#=function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, indices::BlockIndexRange{1} ) return a[block(indices)][only(indices.indices)] -end +end=# function BlockSparseArrays.blockedunitrange_getindices( g::AbstractGradedUnitRange, indices::AbstractVector{<:Block{1}} ) + # full block slicing is always possible for any fusion category + # Without converting `indices` to `Vector`, # mapping `indices` outputs a `BlockVector` # which is harder to reason about. @@ -181,34 +238,20 @@ function BlockSparseArrays.blockedunitrange_getindices( # pass block labels to the axes of the output, # such that `only(axes(a[indices])) isa `GradedUnitRange` # if `a isa `GradedUnitRange` - newg = axis_cat(sectorrange.(nondual_sector.(gblocks) .=> length.(gblocks), isdual(g))) + new_sectoraxes = sectorrange.( + nondual_sector.(gblocks), Base.oneto.(length.(gblocks)), isdual(g) + ) + newg = axis_cat(new_sectoraxes) return mortar(gblocks, (newg,)) end -# TBD dispacth on symmetry style? function BlockSparseArrays.blockedunitrange_getindices( g::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndexRange{1}} ) - return blockedunitrange_getindices(unlabel_blocks(g), indices) -end - -function BlockSparseArrays.blockedunitrange_getindices( - ::AbelianStyle, g::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} -) - r = blockedunitrange_getindices(NotAbelianStyle(), g, indices) - i = Int(findblock(g, first(indices))) - j = Int(findblock(g, last(indices))) - labels = nondual_sector.(sector_axes(g))[i:j] - new_axes = sectorrange.(labels .=> Base.oneto.(blocklengths(r)), isdual(g)) - return GradedUnitRange(new_axes, r) -end - -function BlockSparseArrays.blockedunitrange_getindices( - ::NotAbelianStyle, g::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} -) - return blockedunitrange_getindices(unlabel_blocks(g), indices) + return gradedunitrange_getindices(SymmetryStyle(g), g, indices) end +# fix ambiguity function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, indices::BlockSlice ) @@ -216,18 +259,27 @@ function BlockSparseArrays.blockedunitrange_getindices( end function BlockSparseArrays.blockedunitrange_getindices( - ga::AbstractGradedUnitRange, indices::BlockRange + ga::GradedUnitRange, indices::BlockRange ) return GradedUnitRange(sector_axes(ga)[Int.(indices)], unlabel_blocks(ga)[indices]) end +# TBD remove? Not used in BlockSparseArray slicing +#= function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, indices::BlockIndex{1} ) return a[block(indices)][blockindex(indices)] +end=# + +function BlockSparseArrays.blockedunitrange_getindices( + a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} +) + return gradedunitrange_getindices(SymmetryStyle(a), a, indices) end ### Slicing +# TBD REMOVE? not needed to get result function Base.getindex(a::AbstractGradedUnitRange, index::Integer) return unlabel_blocks(a)[index] end @@ -240,7 +292,11 @@ function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndexRange{1}) return blockedunitrange_getindices(a, indices) end -#getindex(::GradedUnitRanges.AbstractGradedUnitRange, ::BlockArrays.BlockIndexRange{1, R, I} where {R<:Tuple{AbstractUnitRange{<:Integer}}, I<:Tuple{Integer}}) +function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer}) + # BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex + # and blockedunitrange_getindices must be defined + return blockedunitrange_getindices(a, indices) +end # fix ambiguities function Base.getindex( @@ -249,15 +305,16 @@ function Base.getindex( return blockedunitrange_getindices(a, indices) end function Base.getindex( - a::AbstractGradedUnitRange, indices::BlockRange{1,<:Tuple{AbstractUnitRange{Int}}} + a::AbstractGradedUnitRange, indices::BlockRange{1,<:Tuple{AbstractUnitRange{<:Integer}}} ) return blockedunitrange_getindices(a, indices) end +# TBD REMOVE: blockedunitrange_getindices not used # Fix ambiguity error with BlockArrays.jl. -function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndex{1}) - return blockedunitrange_getindices(a, indices) -end +#function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndex{1}) +# return blockedunitrange_getindices(a, indices) +#end # Fixes ambiguity issues with: # ```julia @@ -275,7 +332,6 @@ function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractVector{<:Blo return blockedunitrange_getindices(a, indices) end -# Fix ambiguity error with BlockArrays.jl. function Base.getindex( a::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndexRange{1}} ) @@ -283,20 +339,13 @@ function Base.getindex( end # Fix ambiguity error with BlockArrays.jl. -function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndex{1}}) - return blockedunitrange_getindices(a, indices) +#=function Base.getindex(g::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndex{1}}) + return unlabel_blocks(g)[indices] end - += # function Base.getindex(a::AbstractGradedUnitRange, indices) return blockedunitrange_getindices(a, indices) -end - -# fix ambiguity -Base.getindex(g::AbstractGradedUnitRange, ::Colon) = g - -function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer}) - return blockedunitrange_getindices(SymmetryStyle(a), a, indices) -end +end=# # TODO: Make sure this handles block labels (AbstractGradedUnitRange) correctly. # TODO: Make a special case for `BlockedVector{<:Block{1},<:BlockRange{1}}`? @@ -305,14 +354,16 @@ end # blocklengths = map(bs -> sum(b -> length(a[b]), bs), blocks(indices)) # return blockedrange(blocklengths) # ``` + +# used in BlockSparseArray slicing function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}} + g::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}} ) - blks = map(bs -> mortar(map(b -> a[b], bs)), blocks(indices)) - # We pass `length.(blks)` 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. - return mortar(blks, labelled_length.(blks)) + blks = map(bs -> mortar(map(b -> g[b], bs)), blocks(indices)) + new_labels = map(b -> blocklabels(nondual(g))[Int.(b)], blocks(indices)) + @assert all(allequal.(new_labels)) + new_lengths = length.(blks) + new_sector_axes = sectorrange.(first.(new_labels), Base.oneto.(new_lengths), isdual(g)) + newg = axis_cat(new_sector_axes) + return mortar(blks, (newg,)) end diff --git a/test/test_gradedarray.jl b/test/test_gradedarray.jl index e563d50..31758a8 100644 --- a/test/test_gradedarray.jl +++ b/test/test_gradedarray.jl @@ -1,5 +1,11 @@ using BlockArrays: - AbstractBlockArray, Block, BlockedOneTo, blockedrange, blocklengths, blocksize + AbstractBlockArray, + Block, + BlockedOneTo, + BlockedUnitRange, + blockedrange, + blocklengths, + blocksize using BlockSparseArrays: BlockSparseArray, BlockSparseMatrix, BlockSparseVector, blockstoredlength using GradedArrays: @@ -12,11 +18,13 @@ using GradedArrays: dag, dual, gradedrange, - isdual + isdual, + sectorrange, + space_isequal using SparseArraysBase: storedlength using LinearAlgebra: adjoint using Random: randn! -using Test: @test, @testset +using Test: @test, @test_broken, @testset function randn_blockdiagonal(elt::Type, axes::Tuple) a = BlockSparseArray{elt}(undef, axes) @@ -32,8 +40,6 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @testset "GradedArray (eltype=$elt)" for elt in elts @testset "definitions" begin r = gradedrange([U1(0) => 2, U1(1) => 2]) - a = BlockSparseArray{elt}(undef) - @test !(a isa GradedArray) # no type piracy v = BlockSparseArray{elt}(undef, r) @test v isa GradedArray @test v isa GradedVector @@ -42,7 +48,26 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test m isa GradedMatrix a = BlockSparseArray{elt}(undef, r, r, r) @test a isa GradedArray + + a0 = BlockSparseArray{elt}(undef) + @test !(a0 isa GradedArray) # no type piracy + + b0 = blockedrange([2, 2]) + v = BlockSparseArray{elt}(undef, b0) + @test !(v isa GradedArray) + @test !(v isa GradedVector) + m = BlockSparseArray{elt}(undef, b0, r) + @test !(m isa GradedArray) + @test !(m isa GradedMatrix) + m = BlockSparseArray{elt}(undef, r, b0) + @test !(m isa GradedArray) + @test !(m isa GradedMatrix) + a = BlockSparseArray{elt}(undef, b0, r, r) + @test !(a isa GradedArray) + a = BlockSparseArray{elt}(undef, r, b0, r) + @test !(a isa GradedArray) end + @testset "map" begin d1 = gradedrange([U1(0) => 2, U1(1) => 2]) d2 = gradedrange([U1(0) => 2, U1(1) => 2]) @@ -50,9 +75,6 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test axes(a, 1) isa GradedOneTo @test axes(view(a, 1:4, 1:4, 1:4, 1:4), 1) isa GradedOneTo - d1 = gradedrange([U1(0) => 2, U1(1) => 2]) - d2 = gradedrange([U1(0) => 2, U1(1) => 2]) - a = randn_blockdiagonal(elt, (d1, d2, d1, d2)) for b in (a + a, 2 * a) @test size(b) == (4, 4, 4, 4) @test blocksize(b) == (2, 2, 2, 2) @@ -62,8 +84,8 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) for i in 1:ndims(a) @test axes(b, i) isa GradedOneTo end - @test label(axes(b, 1)[Block(1)]) == U1(0) - @test label(axes(b, 1)[Block(2)]) == U1(1) + @test space_isequal(axes(b, 1)[Block(1)], sectorrange(U1(0), 1:2)) + @test space_isequal(axes(b, 1)[Block(2)], sectorrange(U1(1), 3:4)) @test Array(b) isa Array{elt} @test Array(b) == b @test 2 * Array(a) == b @@ -109,7 +131,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test storedlength(b) == 256 @test blockstoredlength(b) == 16 for i in 1:ndims(a) - @test axes(b, i) isa BlockedOneTo{Int} + @test axes(b, i) isa BlockedUnitRange{Int} end @test Array(a) isa Array{elt} @test Array(a) == a @@ -127,8 +149,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) for i in 1:ndims(a) @test axes(b, i) isa GradedOneTo end - @test label(axes(b, 1)[Block(1)]) == U1(0) - @test label(axes(b, 1)[Block(2)]) == U1(1) + @test space_isequal(axes(b, 1), gradedrange([U1(0) => 1, U1(1) => 1])) @test Array(a) isa Array{elt} @test Array(a) == a end @@ -192,8 +213,9 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) end I = [Block(1)[1:1]] - @test a[I, :] isa AbstractBlockArray - @test a[:, I] isa AbstractBlockArray + @test a[I, :] isa GradedMatrix + @test a[:, I] isa GradedMatrix + @test a[I, I] isa GradedMatrix @test size(a[I, I]) == (1, 1) @test !isdual(axes(a[I, I], 1)) end @@ -213,11 +235,11 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) end I = [Block(1)[1:1]] - @test a[I, :] isa AbstractBlockArray + @test a[I, :] isa GradedMatrix @test axes(a[I, :], 1) isa GradedOneTo @test axes(a[I, :], 2) isa GradedUnitRange - @test a[:, I] isa AbstractBlockArray + @test a[:, I] isa GradedMatrix @test axes(a[:, I], 2) isa GradedOneTo @test axes(a[:, I], 1) isa GradedUnitRange @test size(a[I, I]) == (1, 1) @@ -235,12 +257,12 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test blockstoredlength(b) == 2 @test Array(b) == 2 * Array(a) for i in 1:2 - @test axes(b, i) isa GradedUnitRangeDual - @test axes(a[:, :], i) isa GradedUnitRangeDual + @test axes(b, i) isa GradedUnitRange + @test axes(a[:, :], i) isa GradedUnitRange end I = [Block(1)[1:1]] - @test a[I, :] isa AbstractBlockArray - @test a[:, I] isa AbstractBlockArray + @test a[I, :] isa GradedMatrix + @test a[:, I] isa GradedMatrix @test size(a[I, I]) == (1, 1) @test isdual(axes(a[I, :], 2)) @test isdual(axes(a[:, I], 1)) @@ -260,13 +282,13 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test blockstoredlength(b) == 2 @test Array(b) == 2 * Array(a) for i in 1:2 - @test axes(b, i) isa GradedUnitRangeDual - @test axes(a[:, :], i) isa GradedUnitRangeDual + @test axes(b, i) isa GradedUnitRange + @test axes(a[:, :], i) isa GradedUnitRange end I = [Block(1)[1:1]] - @test a[I, :] isa AbstractBlockArray - @test a[:, I] isa AbstractBlockArray + @test a[I, :] isa GradedMatrix + @test a[:, I] isa GradedMatrix @test size(a[I, I]) == (1, 1) @test isdual(axes(a[I, :], 2)) @test isdual(axes(a[:, I], 1)) @@ -276,28 +298,6 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test isdual(axes(a[I, I], 2)) end - @testset "dual BlockedUnitRange" begin # self dual - r = blockedrange([2, 2]) - a = BlockSparseArray{elt}(undef, dual(r), dual(r)) - @views for i in [Block(1, 1), Block(2, 2)] - a[i] = randn(elt, size(a[i])) - end - b = 2 * a - @test blockstoredlength(b) == 2 - @test Array(b) == 2 * Array(a) - @test a[:, :] isa BlockSparseArray - for i in 1:2 - @test axes(b, i) isa BlockedOneTo - @test axes(a[:, :], i) isa BlockedOneTo - end - - I = [Block(1)[1:1]] - @test a[I, :] isa BlockSparseArray - @test a[:, I] isa BlockSparseArray - @test size(a[I, I]) == (1, 1) - @test !isdual(axes(a[I, I], 1)) - end - # Test case when all axes are dual from taking the adjoint. for r in ( gradedrange([U1(0) => 2, U1(1) => 2]), @@ -359,7 +359,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test iszero(b[Block(2, 1)]) @test iszero(b[Block(1, 2)]) @test b[Block(2, 2)] == a2 - @test all(GradedUnitRanges.space_isequal.(axes(b), (r, dual(r)))) + @test all(space_isequal.(axes(b), (r, dual(r)))) # Regression test for Vector, which caused # an ambiguity error with Base. @@ -373,7 +373,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test blockstoredlength(b) == 1 @test b[Block(1)] == a1 @test iszero(b[Block(2)]) - @test all(GradedUnitRanges.space_isequal.(axes(b), (r,))) + @test all(space_isequal.(axes(b), (r,))) # Regression test for BitArray r = gradedrange([U1(0) => 2, U1(1) => 3]) @@ -388,10 +388,23 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test iszero(b[Block(2, 1)]) @test iszero(b[Block(1, 2)]) @test b[Block(2, 2)] == a2 - @test all(GradedUnitRanges.space_isequal.(axes(b), (r, dual(r)))) + @test all(space_isequal.(axes(b), (r, dual(r)))) end end +@testset "misc indexing" begin + g = gradedrange([U1(0) => 2, U1(1) => 3]) + v = zeros(g) + v2 = v[g] + @test space_isequal(only(axes(v2)), g) + @test v2 == v + gd = dual(g) + v = zeros(gd) + v2 = v[gd] + @test space_isequal(only(axes(v2)), gd) + @test v2 == v +end + @testset "dag" begin elt = ComplexF64 r = gradedrange([U1(0) => 2, U1(1) => 3]) @@ -399,7 +412,7 @@ end a[Block(1, 1)] = randn(elt, 2, 2) a[Block(2, 2)] = randn(elt, 3, 3) @test isdual.(axes(a)) == (false, true) - ad = dag(a) - @test Array(ad) == conj(Array(a)) - @test isdual.(axes(ad)) == (true, false) + @test_broken ad = dag(a) + #@test Array(ad) == conj(Array(a)) + #@test isdual.(axes(ad)) == (true, false) end diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 81b9045..12e9242 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -23,6 +23,7 @@ using GradedArrays: SectorOneTo, SectorUnitRange, SU, + axis_cat, blocklabels, dual, flip, @@ -32,7 +33,7 @@ using GradedArrays: sector_type, sectorrange, space_isequal -using Test: @test, @test_broken, @testset +using Test: @test, @test_broken, @test_throws, @testset @testset "GradedUnitRanges basics" begin r0 = Base.OneTo(1) @@ -45,22 +46,9 @@ using Test: @test, @test_broken, @testset @test !(g2 isa GradedOneTo) @test !isdual(g2) - g1d = dual(g1) + g1d = gradedrange(["x" => 2, "y" => 3, "z" => 2]; isdual=true) @test g1d isa GradedOneTo @test isdual(g1d) - @test space_isequal(dual(g1d), g1) - - for b1 in (g1, g1d, b0), b2 in (g1, g1d, b0) - a = combine_blockaxes(b1, b2) - @test a isa BlockedOneTo - @test blockisequal(a, b0) - end - - for b in (g1, g2, g1d, b0) - a = combine_blockaxes(g2, b) - @test a isa BlockedUnitRange - @test blockisequal(a, b0) - end for g in (g1, g2, g1d) @test g isa GradedUnitRange @@ -72,6 +60,7 @@ using Test: @test, @test_broken, @testset @test !space_isequal(g, b0) @test !space_isequal(g, 1:7) @test !space_isequal(g, dual(g)) + @test space_isequal(combine_blockaxes(g, g), g) @test g == 1:7 for x in iterate(g) @test x == 1 @@ -121,6 +110,9 @@ using Test: @test, @test_broken, @testset @test axes(Base.Slice(g)) isa Tuple{typeof(g)} @test AbstractUnitRange{Int}(g) == 1:7 + ge = eachindex(g) + @test ge isa GradedOneTo + @test space_isequal(g, ge) @test g[Block(1)[1]] == 1 @test g[Block(2)[1]] == 3 @@ -155,7 +147,6 @@ using Test: @test, @test_broken, @testset @test blocklength(a) == 2 @test space_isequal(a[Block(1)], sectorrange("y", 3:5, isdual(g))) @test space_isequal(a[Block(2)], sectorrange("z", 6:7, isdual(g))) - ax = only(axes(a)) @test ax isa GradedOneTo @test space_isequal(ax, gradedrange(["y" => 3, "z" => 2]; isdual=isdual(g))) @@ -164,6 +155,10 @@ using Test: @test, @test_broken, @testset @test blocklengths(ax) == blocklengths(a) @test blocklabels(ax) == blocklabels(a) + a = g[Block.(Base.oneto(2))] + @test (a isa GradedOneTo) == (g isa GradedOneTo) + @test space_isequal(a, g[Block(1):Block(2)]) + a = g[[Block(3), Block(2)]] @test a isa BlockVector @test length(a) == 5 @@ -197,7 +192,41 @@ using Test: @test, @test_broken, @testset @test a isa BlockedVector @test length(a) == 1 @test blocklength(a) == 1 + + v = mortar([[Block(2), Block(2)], [Block(1)]]) + a = g[v] + @test a isa BlockVector + @test only(axes(a)) isa GradedOneTo + @test space_isequal(only(axes(a)), gradedrange(["y" => 6, "x" => 2]; isdual=isdual(g))) end + + @test space_isequal(g1d, dual(g1)) + @test space_isequal(dual(g1d), g1) + + for a in ( + combine_blockaxes(g1, b0), + combine_blockaxes(g1d, b0), + combine_blockaxes(b0, g1), + combine_blockaxes(b0, g1d), + ) + @test a isa BlockedOneTo + @test blockisequal(a, b0) + end + @test_throws ArgumentError combine_blockaxes(g1, g1d) + + a = combine_blockaxes(g2, b0) + @test a isa BlockedUnitRange + @test blockisequal(a, b0) + + a = combine_blockaxes(g2, g1) + @test a isa GradedUnitRange + @test !(a isa GradedOneTo) + @test space_isequal(a, g2) + + sr1 = sectorrange("x", 2) + sr2 = sectorrange("y", 3) + @test space_isequal(g1[Block(1):Block(2)], axis_cat([sr1, sr2])) + @test_throws ArgumentError axis_cat([sr1, dual(sr2)]) end @testset "Non abelian axis" begin @@ -254,18 +283,22 @@ end @test ax isa GradedOneTo @test space_isequal(ax, gradedrange([SU((1, 0)) => 2])) + a = g[Block.(Base.oneto(2))] + @test a isa GradedOneTo + @test space_isequal(a, g) + a = g[[Block(2), Block(1)]] @test a isa BlockVector - @test_broken length(a) == 8 + @test length(a) == 8 @test blocklength(a) == 2 @test blocklabels(a) == [SU((1, 0)), SU((0, 0))] - @test_broken length.(blocks(g)) == (6, 2) + @test length.(blocks(g)) == [2, 6] @test space_isequal(a[Block(1)], sectorrange(SU((1, 0)), 3:8)) @test space_isequal(a[Block(2)], sectorrange(SU((0, 0)), 2)) ax = only(axes(a)) @test ax isa GradedOneTo - @test_broken space_isequal(ax, gradedrange([SU((1, 0)) => 2, SU((0, 0)) => 3])) + @test space_isequal(ax, gradedrange([SU((1, 0)) => 2, SU((0, 0)) => 2])) a = g[[Block(2)[1:3], Block(1)[2:2]]] @test a isa BlockVector diff --git a/test/test_gradedunitrange_interface.jl b/test/test_gradedunitrange_interface.jl index 5dda4cb..242b811 100644 --- a/test/test_gradedunitrange_interface.jl +++ b/test/test_gradedunitrange_interface.jl @@ -1,4 +1,4 @@ -using BlockArrays: BlockedOneTo, blockisequal +using BlockArrays: BlockedOneTo, blockedrange, blockisequal using GradedArrays: NoLabel, blocklabels, dag, dual, flip, isdual, space_isequal using Test: @test, @testset diff --git a/test/test_gradedunitranges_tensor_product.jl b/test/test_gradedunitranges_tensor_product.jl index 9502ca5..f1ec52c 100644 --- a/test/test_gradedunitranges_tensor_product.jl +++ b/test/test_gradedunitranges_tensor_product.jl @@ -3,6 +3,7 @@ using GradedArrays: GradedArrays, GradedOneTo, NotAbelianStyle, + SectorUnitRange, SU2, U1, blocklabels, @@ -15,6 +16,7 @@ using GradedArrays: isdual using TensorProducts: ⊗, OneToOne, tensor_product using Test: @test, @testset +using TestExtras: @constinferred GradedArrays.SymmetryStyle(::Type{<:String}) = NotAbelianStyle() GradedArrays.tensor_product(s1::String, s2::String) = gradedrange([s1 * s2 => 1]) @@ -104,7 +106,7 @@ end g1 = gradedrange([s1 => 1]) g1d = dual(g1) - @test (@constinferred s1 ⊗ s1) isa AbstractSector + @test (@constinferred s1 ⊗ s1) isa U1 @test (@constinferred sr1 ⊗ sr1) isa SectorUnitRange @test (@constinferred g1 ⊗ g1) isa GradedOneTo diff --git a/test/test_sectorunitrange.jl b/test/test_sectorunitrange.jl index 7e7325a..abfa1d8 100644 --- a/test/test_sectorunitrange.jl +++ b/test/test_sectorunitrange.jl @@ -1,9 +1,19 @@ using Test: @test, @test_throws, @testset -using BlockArrays: Block, blocklength, blocklengths, blockisequal, blocks +using BlockArrays: + Block, + BlockBoundsError, + BlockRange, + blockaxes, + blocklasts, + blocklength, + blocklengths, + blockisequal, + blocks, + findblock +using TestExtras: @constinferred using GradedArrays: - AbstractSector, U1, SU, SectorUnitRange, diff --git a/test/test_symmetrysectors_fusion_rules.jl b/test/test_symmetrysectors_fusion_rules.jl index d4671d5..c861b66 100644 --- a/test/test_symmetrysectors_fusion_rules.jl +++ b/test/test_symmetrysectors_fusion_rules.jl @@ -5,17 +5,19 @@ using GradedArrays: TrivialSector, U1, Z, + blocklengths, dual, - space_isequal, - gradedrange, flip, + gradedrange, nsymbol, quantum_dimension, + space_isequal, trivial, unmerged_tensor_product using TensorProducts: ⊗, tensor_product using Test: @test, @testset, @test_throws using TestExtras: @constinferred +using BlockArrays: blocklengths @testset "Simple SymmetrySector fusion rules" begin @testset "Z{2} fusion rules" begin diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index 279f38b..b6b0ada 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -16,6 +16,7 @@ using GradedArrays: using TensorProducts: ⊗ using Test: @test, @testset, @test_throws using TestExtras: @constinferred +using BlockArrays: blocklengths @testset "Test Ordered Products" begin @testset "Ordered Constructor" begin diff --git a/test/test_tensoralgebraext.jl b/test/test_tensoralgebraext.jl index 2a8ceff..ed4a45b 100644 --- a/test/test_tensoralgebraext.jl +++ b/test/test_tensoralgebraext.jl @@ -1,6 +1,15 @@ using BlockArrays: Block, blocksize -using BlockSparseArrays: BlockSparseArray, BlockSparseMatrix -using GradedArrays: GradedOneTo, U1, blocklabels, dual, flip, gradedrange, space_isequal +using BlockSparseArrays: BlockSparseArray +using GradedArrays: + GradedArray, + GradedMatrix, + GradedOneTo, + U1, + blocklabels, + dual, + flip, + gradedrange, + space_isequal using Random: randn! using TensorAlgebra: contract, matricize, unmatricize using Test: @test, @test_broken, @testset @@ -22,7 +31,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) d2 = gradedrange([U1(0) => 1, U1(1) => 1]) a = randn_blockdiagonal(elt, (d1, d2, dual(d1), dual(d2))) m = matricize(a, (1, 2), (3, 4)) - @test m isa BlockSparseMatrix + @test m isa GradedMatrix @test space_isequal(axes(m, 1), gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 1])) @test space_isequal( axes(m, 2), flip(gradedrange([U1(0) => 1, U1(-1) => 2, U1(-2) => 1])) @@ -49,13 +58,13 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) d1234 = gradedrange([U1(-2) => 1, U1(-1) => 4, U1(0) => 6, U1(1) => 4, U1(2) => 1]) m = matricize(a, (1, 2, 3, 4), ()) - @test m isa BlockSparseMatrix + @test m isa GradedMatrix @test space_isequal(axes(m, 1), d1234) @test space_isequal(axes(m, 2), flip(gradedrange([U1(0) => 1]))) @test a == unmatricize(m, (d1, d2, dual(d1), dual(d2)), ()) m = matricize(a, (), (1, 2, 3, 4)) - @test m isa BlockSparseMatrix + @test m isa GradedMatrix @test space_isequal(axes(m, 1), gradedrange([U1(0) => 1])) @test space_isequal(axes(m, 2), dual(d1234)) @test a == unmatricize(m, (), (d1, d2, dual(d1), dual(d2))) @@ -77,7 +86,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) ) @test dimnames_dest == dimnames_dest_dense @test size(a_dest) == size(a_dest_dense) - @test a_dest isa BlockSparseArray + @test a_dest isa GradedArray @test a_dest ≈ a_dest_dense # matrix vector @@ -85,7 +94,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) a_dest_dense, dimnames_dest_dense = contract(a1_dense, (2, -1, -2, 1), a3_dense, (1, 2)) @test dimnames_dest == dimnames_dest_dense @test size(a_dest) == size(a_dest_dense) - @test a_dest isa BlockSparseArray + @test a_dest isa GradedArray @test a_dest ≈ a_dest_dense # vector matrix @@ -93,7 +102,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) a_dest_dense, dimnames_dest_dense = contract(a3_dense, (1, 2), a1_dense, (2, -1, -2, 1)) @test dimnames_dest == dimnames_dest_dense @test size(a_dest) == size(a_dest_dense) - @test a_dest isa BlockSparseArray + @test a_dest isa GradedArray @test a_dest ≈ a_dest_dense # vector vector @@ -109,7 +118,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) a_dest_dense, dimnames_dest_dense = contract(a3_dense, (1, 2), a3_dense, (3, 4)) @test dimnames_dest == dimnames_dest_dense @test size(a_dest) == size(a_dest_dense) - @test a_dest isa BlockSparseArray + @test a_dest isa GradedArray @test a_dest ≈ a_dest_dense end end From 36a3526e68194032a27d38ebf4678e59ab284e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 12:21:40 -0400 Subject: [PATCH 14/33] remove unused methods --- src/gradedarray.jl | 24 +--------- src/gradedunitrange.jl | 92 +++++++++++------------------------- test/test_gradedunitrange.jl | 10 +++- 3 files changed, 37 insertions(+), 89 deletions(-) diff --git a/src/gradedarray.jl b/src/gradedarray.jl index 6c02a01..a0dde74 100644 --- a/src/gradedarray.jl +++ b/src/gradedarray.jl @@ -45,12 +45,11 @@ function similar_blocksparse( ) # TODO: Probably need to unwrap the type of `a` in certain cases # to make a proper block type. - aaa = BlockSparseArray{ + return BlockSparseArray{ elt,length(axes),similartype(unwrap_array_type(blocktype(a)), elt, axes) }( undef, axes ) - return aaa end function Base.similar( @@ -69,17 +68,6 @@ function Base.similar( ) return similar_blocksparse(a, elt, axes) end -#= # TBD needed? => delete -# Fix ambiguity error with `BlockArrays.jl`. -function Base.similar( - a::StridedArray, - elt::Type, - axes::Tuple{ - AbstractGradedUnitRange,AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange} - }, -) - return similar_blocksparse(a, elt, axes) -end=# # Fix ambiguity error with `BlockSparseArrays.jl`. # TBD DerivableInterfaces? @@ -90,16 +78,6 @@ function Base.similar( ) return similar_blocksparse(a, elt, axes) end -#= # TBD needed? => delete -function Base.similar( - a::AnyAbstractBlockSparseArray, - elt::Type, - axes::Tuple{ - AbstractGradedUnitRange,AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange} - }, -) - return similar_blocksparse(a, elt, axes) -end=# function Base.zeros( elt::Type, ax::Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}} diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 61d4a4d..d7ac7ba 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -117,19 +117,19 @@ end function gradedunitrange_getindices( ::AbelianStyle, g::AbstractUnitRange, indices::AbstractUnitRange{<:Integer} ) - r = blockedunitrange_getindices(unlabel_blocks(g), indices) - i = Int(findblock(g, first(indices))) - j = Int(findblock(g, last(indices))) - labels = nondual_sector.(sector_axes(g))[i:j] - new_axes = sectorrange.(labels .=> Base.oneto.(blocklengths(r)), isdual(g)) - return GradedUnitRange(new_axes, r) + new_range = blockedunitrange_getindices(unlabel_blocks(g), indices) + bf = findblock(g, first(indices)) + bl = findblock(g, last(indices)) + labels = blocklabels(nondual(g)[bf:bl]) + new_sector_axes = sectorrange.(labels .=> Base.oneto.(blocklengths(new_range)), isdual(g)) + return GradedUnitRange(new_sector_axes, new_range) end function gradedunitrange_getindices( - ::AbelianStyle, g::AbstractGradedUnitRange, indices::BlockVector{<:BlockIndex{1}} + ::AbelianStyle, g::AbstractUnitRange, indices::BlockVector{<:BlockIndex{1}} ) newg = gradedrange( - map(b -> nondual_sector(g[b]), block.(indices)) .=> length.(blocks(indices)); + map(b -> nondual_sector(g[b]), block.(indices)) .=> blocklength(indices); isdual=isdual(g), ) v = mortar(map(b -> g[b], blocks(indices)), (newg,)) @@ -204,13 +204,6 @@ function BlockSparseArrays.blockedunitrange_getindices( return sectorrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) end -# NEVER CALLED TBD REMOVE -function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::Vector{<:Integer} -) - return gradedunitrange_getindice(SymmetryStyle(a), a, indices) -end - # used in BlockSparseArrays function BlockSparseArrays.blockedunitrange_getindices( g::AbstractGradedUnitRange, @@ -219,13 +212,6 @@ function BlockSparseArrays.blockedunitrange_getindices( return gradedunitrange_getindices(SymmetryStyle(g), g, indices) end -# a priori pas besoin, on peut utiliser la version generique -#=function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::BlockIndexRange{1} -) - return a[block(indices)][only(indices.indices)] -end=# - function BlockSparseArrays.blockedunitrange_getindices( g::AbstractGradedUnitRange, indices::AbstractVector{<:Block{1}} ) @@ -264,13 +250,18 @@ function BlockSparseArrays.blockedunitrange_getindices( return GradedUnitRange(sector_axes(ga)[Int.(indices)], unlabel_blocks(ga)[indices]) end -# TBD remove? Not used in BlockSparseArray slicing -#= +# used in BlockSparseArray slicing function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::BlockIndex{1} + g::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}} ) - return a[block(indices)][blockindex(indices)] -end=# + blks = map(bs -> mortar(map(b -> g[b], bs)), blocks(indices)) + new_labels = map(b -> blocklabels(nondual(g))[Int.(b)], blocks(indices)) + @assert all(allequal.(new_labels)) + new_lengths = length.(blks) + new_sector_axes = sectorrange.(first.(new_labels), Base.oneto.(new_lengths), isdual(g)) + newg = axis_cat(new_sector_axes) + return mortar(blks, (newg,)) +end function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} @@ -279,11 +270,6 @@ function BlockSparseArrays.blockedunitrange_getindices( end ### Slicing -# TBD REMOVE? not needed to get result -function Base.getindex(a::AbstractGradedUnitRange, index::Integer) - return unlabel_blocks(a)[index] -end - function Base.getindex(a::AbstractGradedUnitRange, index::Block{1}) return blockedunitrange_getindices(a, index) end @@ -292,6 +278,15 @@ function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndexRange{1}) return blockedunitrange_getindices(a, indices) end +# impose Base.getindex and blockedunitrange_getindices to return the same output +# this version of blockedunitrange_getindices is used in BlockSparseArray slicing +function Base.getindex( + g::AbstractGradedUnitRange, + indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, +) + return blockedunitrange_getindices(g, indices) +end + function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer}) # BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex # and blockedunitrange_getindices must be defined @@ -300,7 +295,7 @@ end # fix ambiguities function Base.getindex( - a::AbstractGradedUnitRange, indices::BlockArrays.BlockRange{1,<:Tuple{Base.OneTo}} + a::AbstractGradedUnitRange, indices::BlockRange{1,<:Tuple{Base.OneTo}} ) return blockedunitrange_getindices(a, indices) end @@ -310,7 +305,6 @@ function Base.getindex( return blockedunitrange_getindices(a, indices) end -# TBD REMOVE: blockedunitrange_getindices not used # Fix ambiguity error with BlockArrays.jl. #function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndex{1}) # return blockedunitrange_getindices(a, indices) @@ -337,33 +331,3 @@ function Base.getindex( ) return blockedunitrange_getindices(a, indices) end - -# Fix ambiguity error with BlockArrays.jl. -#=function Base.getindex(g::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndex{1}}) - return unlabel_blocks(g)[indices] -end -= # -function Base.getindex(a::AbstractGradedUnitRange, indices) - return blockedunitrange_getindices(a, indices) -end=# - -# TODO: Make sure this handles block labels (AbstractGradedUnitRange) correctly. -# TODO: Make a special case for `BlockedVector{<:Block{1},<:BlockRange{1}}`? -# For example: -# ```julia -# blocklengths = map(bs -> sum(b -> length(a[b]), bs), blocks(indices)) -# return blockedrange(blocklengths) -# ``` - -# used in BlockSparseArray slicing -function BlockSparseArrays.blockedunitrange_getindices( - g::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}} -) - blks = map(bs -> mortar(map(b -> g[b], bs)), blocks(indices)) - new_labels = map(b -> blocklabels(nondual(g))[Int.(b)], blocks(indices)) - @assert all(allequal.(new_labels)) - new_lengths = length.(blks) - new_sector_axes = sectorrange.(first.(new_labels), Base.oneto.(new_lengths), isdual(g)) - newg = axis_cat(new_sector_axes) - return mortar(blks, (newg,)) -end diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 12e9242..e30b79a 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -189,9 +189,12 @@ using Test: @test, @test_broken, @test_throws, @testset I = mortar([Block(1)[1:1]]) a = g[I] - @test a isa BlockedVector + @test a isa BlockVector @test length(a) == 1 @test blocklength(a) == 1 + ax = only(axes(a)) + @test ax isa GradedOneTo + @test space_isequal(ax, gradedrange(["x" => 1]; isdual=isdual(g))) v = mortar([[Block(2), Block(2)], [Block(1)]]) a = g[v] @@ -312,7 +315,10 @@ end I = mortar([Block(1)[1:1]]) a = g[I] - @test a isa BlockedVector + @test a isa BlockVector @test length(a) == 1 @test blocklength(a) == 1 + ax = only(axes(a)) + @test ax isa BlockedOneTo + @test blockisequal(ax, blockedrange([1])) end From a68b4608094cb878777b36d6723d8949efa78836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 12:23:39 -0400 Subject: [PATCH 15/33] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index da10647..0cd11c5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "GradedArrays" uuid = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2" authors = ["ITensor developers and contributors"] -version = "0.3.1" +version = "0.4.0" [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" From aa323a7b55248b06e4ca0bf427a22d791e9cb46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 12:25:25 -0400 Subject: [PATCH 16/33] bump doc and test versions --- docs/Project.toml | 2 +- test/Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index 41d580d..867f8e5 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -5,5 +5,5 @@ Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" [compat] Documenter = "1" -GradedArrays = "0.3" +GradedArrays = "0.4" Literate = "2" diff --git a/test/Project.toml b/test/Project.toml index a5eedd4..b2032d6 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -17,7 +17,7 @@ TestExtras = "5ed8adda-3752-4e41-b88a-e8b09835ee3a" Aqua = "0.8.11" BlockArrays = "1.6.0" BlockSparseArrays = "0.4.2" -GradedArrays = "0.3.0" +GradedArrays = "0.4" LinearAlgebra = "1.10.0" Random = "1.10.0" SafeTestsets = "0.1.0" From fa89dfa58d8b9dcd4ede4faf8827d8f5aae4d8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 12:29:32 -0400 Subject: [PATCH 17/33] update doc references --- docs/src/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/reference.md b/docs/src/reference.md index cd9b6bd..9c2924d 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -1,5 +1,5 @@ # Reference ```@autodocs -Modules = [GradedArrays, GradedArrays.LabelledNumbers, GradedArrays.GradedUnitRanges, GradedArrays.SymmetrySectors] +Modules = [GradedArrays] ``` From 15583c9cb5e72d7527b2c80b60537e78822161cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 13:50:28 -0400 Subject: [PATCH 18/33] fix getindex BlockIndexRange --- src/gradedunitrange.jl | 6 +++--- test/test_gradedunitrange.jl | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index d7ac7ba..ac48065 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -128,11 +128,11 @@ end function gradedunitrange_getindices( ::AbelianStyle, g::AbstractUnitRange, indices::BlockVector{<:BlockIndex{1}} ) + blks = blocks(indices) newg = gradedrange( - map(b -> nondual_sector(g[b]), block.(indices)) .=> blocklength(indices); - isdual=isdual(g), + map(b -> nondual_sector(g[b]), block.(blks)) .=> length.(blks); isdual=isdual(g) ) - v = mortar(map(b -> g[b], blocks(indices)), (newg,)) + v = mortar(map(b -> g[b], blks), (newg,)) return v end diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index e30b79a..23b93ce 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -187,14 +187,14 @@ using Test: @test, @test_broken, @test_throws, @testset @test ax isa GradedOneTo @test space_isequal(ax, gradedrange(["z" => 1, "y" => 2]; isdual=isdual(g))) - I = mortar([Block(1)[1:1]]) + I = mortar([Block(1)[1:1], Block(1)[1:2], Block(2)[1:2]]) a = g[I] @test a isa BlockVector - @test length(a) == 1 - @test blocklength(a) == 1 + @test length(a) == 5 + @test blocklength(a) == 3 ax = only(axes(a)) @test ax isa GradedOneTo - @test space_isequal(ax, gradedrange(["x" => 1]; isdual=isdual(g))) + @test space_isequal(ax, gradedrange(["x" => 1, "x" => 2, "y" => 2]; isdual=isdual(g))) v = mortar([[Block(2), Block(2)], [Block(1)]]) a = g[v] From ccbfe1f9a60bf4e15ea28c0fdeb7412bf7580bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 14:31:43 -0400 Subject: [PATCH 19/33] reorder methods, remove unneeded --- src/gradedunitrange.jl | 63 ++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index ac48065..2b729fa 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -197,11 +197,11 @@ end # BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex # and blockedunitrange_getindices must be defined +# fix ambiguity function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, index::Block{1} + a::AbstractGradedUnitRange, indices::BlockSlice ) - sr = sector_axes(a)[Int(index)] - return sectorrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) + return a[indices.block] end # used in BlockSparseArrays @@ -213,22 +213,9 @@ function BlockSparseArrays.blockedunitrange_getindices( end function BlockSparseArrays.blockedunitrange_getindices( - g::AbstractGradedUnitRange, indices::AbstractVector{<:Block{1}} + a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} ) - # full block slicing is always possible for any fusion category - - # Without converting `indices` to `Vector`, - # mapping `indices` outputs a `BlockVector` - # which is harder to reason about. - gblocks = map(index -> g[index], Vector(indices)) - # pass block labels to the axes of the output, - # such that `only(axes(a[indices])) isa `GradedUnitRange` - # if `a isa `GradedUnitRange` - new_sectoraxes = sectorrange.( - nondual_sector.(gblocks), Base.oneto.(length.(gblocks)), isdual(g) - ) - newg = axis_cat(new_sectoraxes) - return mortar(gblocks, (newg,)) + return gradedunitrange_getindices(SymmetryStyle(a), a, indices) end function BlockSparseArrays.blockedunitrange_getindices( @@ -237,11 +224,11 @@ function BlockSparseArrays.blockedunitrange_getindices( return gradedunitrange_getindices(SymmetryStyle(g), g, indices) end -# fix ambiguity function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::BlockSlice + a::AbstractGradedUnitRange, index::Block{1} ) - return a[indices.block] + sr = sector_axes(a)[Int(index)] + return sectorrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) end function BlockSparseArrays.blockedunitrange_getindices( @@ -250,6 +237,25 @@ function BlockSparseArrays.blockedunitrange_getindices( return GradedUnitRange(sector_axes(ga)[Int.(indices)], unlabel_blocks(ga)[indices]) end +function BlockSparseArrays.blockedunitrange_getindices( + g::AbstractGradedUnitRange, indices::AbstractVector{<:Block{1}} +) + # full block slicing is always possible for any fusion category + + # Without converting `indices` to `Vector`, + # mapping `indices` outputs a `BlockVector` + # which is harder to reason about. + gblocks = map(index -> g[index], Vector(indices)) + # pass block labels to the axes of the output, + # such that `only(axes(a[indices])) isa `GradedUnitRange` + # if `a isa `GradedUnitRange` + new_sectoraxes = sectorrange.( + nondual_sector.(gblocks), Base.oneto.(length.(gblocks)), isdual(g) + ) + newg = axis_cat(new_sectoraxes) + return mortar(gblocks, (newg,)) +end + # used in BlockSparseArray slicing function BlockSparseArrays.blockedunitrange_getindices( g::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}} @@ -263,21 +269,11 @@ function BlockSparseArrays.blockedunitrange_getindices( return mortar(blks, (newg,)) end -function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} -) - return gradedunitrange_getindices(SymmetryStyle(a), a, indices) -end - ### Slicing function Base.getindex(a::AbstractGradedUnitRange, index::Block{1}) return blockedunitrange_getindices(a, index) end -function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndexRange{1}) - return blockedunitrange_getindices(a, indices) -end - # impose Base.getindex and blockedunitrange_getindices to return the same output # this version of blockedunitrange_getindices is used in BlockSparseArray slicing function Base.getindex( @@ -305,11 +301,6 @@ function Base.getindex( return blockedunitrange_getindices(a, indices) end -# Fix ambiguity error with BlockArrays.jl. -#function Base.getindex(a::AbstractGradedUnitRange, indices::BlockIndex{1}) -# return blockedunitrange_getindices(a, indices) -#end - # Fixes ambiguity issues with: # ```julia # getindex(::BlockedUnitRange, ::BlockSlice) From f1252c08a3c8533b725b693659b2a39c84cbfdb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Wed, 23 Apr 2025 14:47:20 -0400 Subject: [PATCH 20/33] use macro to avoid code dupplication --- src/gradedunitrange.jl | 98 +++++++++++------------------------------- 1 file changed, 26 insertions(+), 72 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 2b729fa..7f38dce 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -167,6 +167,24 @@ end Base.first(a::AbstractGradedUnitRange) = first(unlabel_blocks(a)) +# BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex +# and blockedunitrange_getindices must be defined. +# Also impose Base.getindex and blockedunitrange_getindices to return the same output +for T in [ + :(AbstractUnitRange{<:Integer}), + :(AbstractVector{<:Block{1}}), + :(AbstractVector{<:BlockIndexRange{1}}), + :(Block{1}), + :(BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}), + :(BlockRange{1,<:Tuple{Base.OneTo}}), + :(BlockRange{1,<:Tuple{AbstractUnitRange{<:Integer}}}), + :(BlockSlice), +] + @eval Base.getindex(g::AbstractGradedUnitRange, indices::$T) = blockedunitrange_getindices( + g, indices + ) +end + ### BlockArrays interface function BlockArrays.blocklasts(a::AbstractGradedUnitRange) @@ -204,24 +222,14 @@ function BlockSparseArrays.blockedunitrange_getindices( return a[indices.block] end -# used in BlockSparseArrays -function BlockSparseArrays.blockedunitrange_getindices( - g::AbstractGradedUnitRange, - indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, -) - return gradedunitrange_getindices(SymmetryStyle(g), g, indices) -end - -function BlockSparseArrays.blockedunitrange_getindices( - a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer} -) - return gradedunitrange_getindices(SymmetryStyle(a), a, indices) -end - -function BlockSparseArrays.blockedunitrange_getindices( - g::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndexRange{1}} -) - return gradedunitrange_getindices(SymmetryStyle(g), g, indices) +for T in [ + :(AbstractUnitRange{<:Integer}), + :(AbstractVector{<:BlockIndexRange{1}}), + :(BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}), +] + @eval BlockSparseArrays.blockedunitrange_getindices(g::AbstractGradedUnitRange, indices::$T) = gradedunitrange_getindices( + SymmetryStyle(g), g, indices + ) end function BlockSparseArrays.blockedunitrange_getindices( @@ -268,57 +276,3 @@ function BlockSparseArrays.blockedunitrange_getindices( newg = axis_cat(new_sector_axes) return mortar(blks, (newg,)) end - -### Slicing -function Base.getindex(a::AbstractGradedUnitRange, index::Block{1}) - return blockedunitrange_getindices(a, index) -end - -# impose Base.getindex and blockedunitrange_getindices to return the same output -# this version of blockedunitrange_getindices is used in BlockSparseArray slicing -function Base.getindex( - g::AbstractGradedUnitRange, - indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}, -) - return blockedunitrange_getindices(g, indices) -end - -function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractUnitRange{<:Integer}) - # BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex - # and blockedunitrange_getindices must be defined - return blockedunitrange_getindices(a, indices) -end - -# fix ambiguities -function Base.getindex( - a::AbstractGradedUnitRange, indices::BlockRange{1,<:Tuple{Base.OneTo}} -) - return blockedunitrange_getindices(a, indices) -end -function Base.getindex( - a::AbstractGradedUnitRange, indices::BlockRange{1,<:Tuple{AbstractUnitRange{<:Integer}}} -) - return blockedunitrange_getindices(a, indices) -end - -# Fixes ambiguity issues with: -# ```julia -# getindex(::BlockedUnitRange, ::BlockSlice) -# getindex(::GradedUnitRange, ::AbstractUnitRange{<:Integer}) -# getindex(::GradedUnitRange, ::Any) -# getindex(::AbstractUnitRange, ::AbstractUnitRange{<:Integer}) -# ``` -function Base.getindex(a::AbstractGradedUnitRange, indices::BlockSlice) - return blockedunitrange_getindices(a, indices) -end - -# Fix ambiguity error with BlockArrays.jl. -function Base.getindex(a::AbstractGradedUnitRange, indices::AbstractVector{<:Block{1}}) - return blockedunitrange_getindices(a, indices) -end - -function Base.getindex( - a::AbstractGradedUnitRange, indices::AbstractVector{<:BlockIndexRange{1}} -) - return blockedunitrange_getindices(a, indices) -end From 4ba41af909fc4003a23d743239e46b736abb13a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 24 Apr 2025 12:12:58 -0400 Subject: [PATCH 21/33] use ungrade and sectors --- src/abstractsector.jl | 4 +- src/fusion.jl | 12 +-- src/gradedarray.jl | 12 +-- src/gradedunitrange.jl | 87 +++++++++----------- src/gradedunitrange_interface.jl | 24 +++--- src/sector_product.jl | 6 +- src/sectorunitrange.jl | 79 +++++++++--------- test/test_gradedarray.jl | 10 ++- test/test_gradedunitrange.jl | 53 ++++-------- test/test_gradedunitrange_interface.jl | 15 +++- test/test_gradedunitranges_tensor_product.jl | 8 +- test/test_sectorunitrange.jl | 40 ++++----- 12 files changed, 172 insertions(+), 178 deletions(-) diff --git a/src/abstractsector.jl b/src/abstractsector.jl index 0fbf012..2a6e1ab 100644 --- a/src/abstractsector.jl +++ b/src/abstractsector.jl @@ -44,7 +44,7 @@ to_gradedrange(g::AbstractGradedUnitRange) = g function nsymbol(s1::AbstractSector, s2::AbstractSector, s3::AbstractSector) full_space = to_gradedrange(s1 ⊗ s2) - i = findfirst(==(s3), blocklabels(full_space)) + i = findfirst(==(s3), sectors(full_space)) isnothing(i) && return 0 return sector_multiplicities(full_space)[i] end @@ -61,7 +61,7 @@ end # abelian case: return Sector function fusion_rule(::AbelianStyle, c1::C, c2::C) where {C<:AbstractSector} - return only(blocklabels(fusion_rule(NotAbelianStyle(), c1, c2))) + return only(sectors(fusion_rule(NotAbelianStyle(), c1, c2))) end function label_fusion_rule(sector_type::Type{<:AbstractSector}, l1, l2) diff --git a/src/fusion.jl b/src/fusion.jl index 281760b..7b121e2 100644 --- a/src/fusion.jl +++ b/src/fusion.jl @@ -12,16 +12,16 @@ end function TensorProducts.tensor_product( ::AbelianStyle, sr1::SectorUnitRange, sr2::SectorUnitRange ) - s = nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2)) + s = sector(flip_dual(sr1)) ⊗ sector(flip_dual(sr2)) return sectorrange(s, sector_multiplicity(sr1) * sector_multiplicity(sr2)) end function TensorProducts.tensor_product( ::NotAbelianStyle, sr1::SectorUnitRange, sr2::SectorUnitRange ) - g0 = nondual_sector(flip_dual(sr1)) ⊗ nondual_sector(flip_dual(sr2)) + g0 = sector(flip_dual(sr1)) ⊗ sector(flip_dual(sr2)) return gradedrange( - blocklabels(g0) .=> + sectors(g0) .=> sector_multiplicity(sr1) * sector_multiplicity(sr2) .* sector_multiplicities(g0), ) end @@ -69,7 +69,7 @@ end # convention: sort dual GradedUnitRange according to nondual blocks function sectorsortperm(a::AbstractUnitRange) - return Block.(sortperm(blocklabels(nondual(a)))) + return Block.(sortperm(sectors(a))) end # Get the permutation for sorting, then group by common elements. @@ -85,14 +85,14 @@ end # Get the permutation for sorting, then group by common elements. # groupsortperm([2, 1, 2, 3]) == [[2], [1, 3], [4]] function sectormergesortperm(a::AbstractUnitRange) - return Block.(groupsortperm(blocklabels(nondual(a)))) + return Block.(groupsortperm(sectors(a))) end # Used by `TensorAlgebra.unmatricize` in `GradedArraysTensorAlgebraExt`. invblockperm(a::Vector{<:Block{1}}) = Block.(invperm(Int.(a))) function sectormergesort(g::AbstractGradedUnitRange) - glabels = blocklabels(g) + glabels = sectors(g) multiplicities = sector_multiplicities(g) new_blocklengths = map(sort(unique(glabels))) do la return la => sum(multiplicities[findall(==(la), glabels)]; init=0) diff --git a/src/gradedarray.jl b/src/gradedarray.jl index a0dde74..8e3d898 100644 --- a/src/gradedarray.jl +++ b/src/gradedarray.jl @@ -6,7 +6,8 @@ using BlockSparseArrays: BlockSparseArray, BlockSparseMatrix, BlockSparseVector, - blocktype + blocktype, + sparsemortar using LinearAlgebra: Adjoint using TypeParameterAccessors: similartype, unwrap_array_type @@ -15,10 +16,9 @@ const GradedArray{T,M,A,Blocks,Axes} = BlockSparseArray{ } where {Axes<:Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}}} const GradedMatrix{T,A,Blocks,Axes} = GradedArray{ T,2,A,Blocks,Axes -} where {Axes<:Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}}} -const GradedVector{T,A,Blocks,Axes} = GradedArray{ - T,1,A,Blocks,Axes -} where {Axes<:Tuple{AbstractGradedUnitRange,Vararg{AbstractGradedUnitRange}}} +} where {Axes<:Tuple{AbstractGradedUnitRange,AbstractGradedUnitRange}} +const GradedVector{T,A,Blocks,Axes} = + GradedArray{T,1,A,Blocks,Axes} where {Axes<:Tuple{AbstractGradedUnitRange}} # TODO: Handle this through some kind of trait dispatch, maybe # a `SymmetryStyle`-like trait to check if the block sparse @@ -119,3 +119,5 @@ function Base.getindex( ) return getindex_blocksparse(a, I1, I2) end + +ungrade(a::GradedArray) = sparsemortar(blocks(a), ungrade.(axes(a))) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 7f38dce..1129eaa 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -25,6 +25,8 @@ using BlockSparseArrays: blockedunitrange_getindices using Compat: allequal +# ==================================== Definitions ======================================= + abstract type AbstractGradedUnitRange{T,BlockLasts} <: AbstractBlockedUnitRange{T,BlockLasts} end @@ -57,17 +59,16 @@ function GradedUnitRange(sector_axes::AbstractVector, full_range::AbstractUnitRa ) end -# Accessors +# ===================================== Accessors ======================================== + sector_axes(g::GradedUnitRange) = g.sector_axes -unlabel_blocks(g::GradedUnitRange) = g.full_range # TBD rename full_range? +ungrade(g::GradedUnitRange) = g.full_range sector_multiplicities(g::GradedUnitRange) = sector_multiplicity.(sector_axes(g)) sector_type(::Type{<:GradedUnitRange{<:Any,SUR}}) where {SUR} = sector_type(SUR) -# -# Constructors -# +# ==================================== Constructors ====================================== function axis_cat(sectors::AbstractVector{<:SectorOneTo}) brange = blockedrange(length.(sectors)) @@ -79,28 +80,23 @@ function axis_cat(gaxes::AbstractVector{<:GradedOneTo}) end function gradedrange( - lblocklengths::AbstractVector{<:Pair{<:Any,<:Integer}}; isdual::Bool=false + sectors_lengths::AbstractVector{<:Pair{<:Any,<:Integer}}; isdual::Bool=false ) - sectors = sectorrange.(lblocklengths, isdual) - return axis_cat(sectors) + gsector_axes = sectorrange.(sectors_lengths, isdual) + return axis_cat(gsector_axes) end -### GradedUnitRange interface -dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), unlabel_blocks(g)) +# ============================= GradedUnitRanges interface =============================== +dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), ungrade(g)) isdual(g::AbstractGradedUnitRange) = isdual(first(sector_axes(g))) # crash for empty. Should not be an issue. -# TBD remove? No use if change blocklabels convention -nondual(g::AbstractGradedUnitRange) = isdual(g) ? dual(g) : g - -# TBD: change convention to nondual sectors? -function blocklabels(g::AbstractGradedUnitRange) - nondual_blocklabels = nondual_sector.(sector_axes(g)) - return isdual(g) ? dual.(nondual_blocklabels) : nondual_blocklabels +function sectors(g::AbstractGradedUnitRange) + return sector.(sector_axes(g)) end -function map_blocklabels(f, g::GradedUnitRange) - return GradedUnitRange(map_blocklabels.(f, sector_axes(g)), unlabel_blocks(g)) +function map_sectors(f, g::GradedUnitRange) + return GradedUnitRange(map_sectors.(f, sector_axes(g)), ungrade(g)) end ### GradedUnitRange specific slicing @@ -110,18 +106,20 @@ function gradedunitrange_getindices( gblocks = map(index -> g[index], Vector(indices)) # pass block labels to the axes of the output, # such that `only(axes(g[indices])) isa `GradedOneTo` - newg = axis_cat(sectorrange.(nondual_sector.(gblocks) .=> length.(gblocks), isdual(g))) + newg = axis_cat(sectorrange.(sector.(gblocks) .=> length.(gblocks), isdual(g))) return mortar(gblocks, (newg,)) end function gradedunitrange_getindices( ::AbelianStyle, g::AbstractUnitRange, indices::AbstractUnitRange{<:Integer} ) - new_range = blockedunitrange_getindices(unlabel_blocks(g), indices) + new_range = blockedunitrange_getindices(ungrade(g), indices) bf = findblock(g, first(indices)) bl = findblock(g, last(indices)) - labels = blocklabels(nondual(g)[bf:bl]) - new_sector_axes = sectorrange.(labels .=> Base.oneto.(blocklengths(new_range)), isdual(g)) + new_sectors = sectors(g)[Int.(bf:bl)] + new_sector_axes = sectorrange.( + new_sectors .=> Base.oneto.(blocklengths(new_range)), isdual(g) + ) return GradedUnitRange(new_sector_axes, new_range) end @@ -130,7 +128,7 @@ function gradedunitrange_getindices( ) blks = blocks(indices) newg = gradedrange( - map(b -> nondual_sector(g[b]), block.(blks)) .=> length.(blks); isdual=isdual(g) + map(b -> sector(g[b]), block.(blks)) .=> length.(blks); isdual=isdual(g) ) v = mortar(map(b -> g[b], blks), (newg,)) return v @@ -138,14 +136,14 @@ end # need to drop label in some non-abelian slicing function gradedunitrange_getindices(::NotAbelianStyle, g::AbstractUnitRange, indices) - return blockedunitrange_getindices(unlabel_blocks(g), indices) + return blockedunitrange_getindices(ungrade(g), indices) end -### Base interface +# ================================== Base interface ====================================== # needed in BlockSparseArrays function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} - return unlabel_blocks(a) + return ungrade(a) end function Base.axes(ga::AbstractGradedUnitRange) @@ -161,11 +159,11 @@ function Base.show(io::IO, ::MIME"text/plain", g::AbstractGradedUnitRange) end function Base.show(io::IO, g::AbstractGradedUnitRange) - v = blocklabels(g) .=> blocklengths(g) + v = sectors(g) .=> blocklengths(g) return print(io, nameof(typeof(g)), '[', join(repr.(v), ", "), ']') end -Base.first(a::AbstractGradedUnitRange) = first(unlabel_blocks(a)) +Base.first(a::AbstractGradedUnitRange) = first(ungrade(a)) # BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex # and blockedunitrange_getindices must be defined. @@ -185,32 +183,30 @@ for T in [ ) end -### BlockArrays interface +# ================================ BlockArrays interface ================================= function BlockArrays.blocklasts(a::AbstractGradedUnitRange) - return blocklasts(unlabel_blocks(a)) + return blocklasts(ungrade(a)) end function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) # avoid mixing different labels # better to throw explicit error than silently dropping labels !space_isequal(a, b) && throw(ArgumentError("axes are not compatible")) - #!space_isequal(a, b) && return combine_blockaxes(unlabel_blocks(a), unlabel_blocks(b)) + #!space_isequal(a, b) && return combine_blockaxes(ungrade(a), ungrade(b)) # preserve BlockArrays convention for BlockedUnitRange / BlockedOneTo - return GradedUnitRange( - sector_axes(a), combine_blockaxes(unlabel_blocks(a), unlabel_blocks(b)) - ) + return GradedUnitRange(sector_axes(a), combine_blockaxes(ungrade(a), ungrade(b))) end # preserve BlockedOneTo when possible function BlockArrays.combine_blockaxes(a::AbstractGradedUnitRange, b::AbstractUnitRange) - return combine_blockaxes(unlabel_blocks(a), b) + return combine_blockaxes(ungrade(a), b) end function BlockArrays.combine_blockaxes(a::AbstractUnitRange, b::AbstractGradedUnitRange) - return combine_blockaxes(a, unlabel_blocks(b)) + return combine_blockaxes(a, ungrade(b)) end -### BlockSparseArrays interface +# ============================ BlockSparseArrays interface =============================== # BlockSparseArray explicitly calls blockedunitrange_getindices, both Base.getindex # and blockedunitrange_getindices must be defined @@ -236,13 +232,13 @@ function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, index::Block{1} ) sr = sector_axes(a)[Int(index)] - return sectorrange(nondual_sector(sr), unlabel_blocks(a)[index], isdual(sr)) + return sectorrange(sector(sr), ungrade(a)[index], isdual(sr)) end function BlockSparseArrays.blockedunitrange_getindices( ga::GradedUnitRange, indices::BlockRange ) - return GradedUnitRange(sector_axes(ga)[Int.(indices)], unlabel_blocks(ga)[indices]) + return GradedUnitRange(sector_axes(ga)[Int.(indices)], ungrade(ga)[indices]) end function BlockSparseArrays.blockedunitrange_getindices( @@ -257,9 +253,7 @@ function BlockSparseArrays.blockedunitrange_getindices( # pass block labels to the axes of the output, # such that `only(axes(a[indices])) isa `GradedUnitRange` # if `a isa `GradedUnitRange` - new_sectoraxes = sectorrange.( - nondual_sector.(gblocks), Base.oneto.(length.(gblocks)), isdual(g) - ) + new_sectoraxes = sectorrange.(sector.(gblocks), Base.oneto.(length.(gblocks)), isdual(g)) newg = axis_cat(new_sectoraxes) return mortar(gblocks, (newg,)) end @@ -268,11 +262,12 @@ end function BlockSparseArrays.blockedunitrange_getindices( g::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}} ) + #TODO use one map blks = map(bs -> mortar(map(b -> g[b], bs)), blocks(indices)) - new_labels = map(b -> blocklabels(nondual(g))[Int.(b)], blocks(indices)) - @assert all(allequal.(new_labels)) + new_sectors = map(b -> sectors(g)[Int.(b)], blocks(indices)) + @assert all(allequal.(new_sectors)) new_lengths = length.(blks) - new_sector_axes = sectorrange.(first.(new_labels), Base.oneto.(new_lengths), isdual(g)) + new_sector_axes = sectorrange.(first.(new_sectors), Base.oneto.(new_lengths), isdual(g)) newg = axis_cat(new_sector_axes) return mortar(blks, (newg,)) end diff --git a/src/gradedunitrange_interface.jl b/src/gradedunitrange_interface.jl index 4084c6a..5d1033e 100644 --- a/src/gradedunitrange_interface.jl +++ b/src/gradedunitrange_interface.jl @@ -10,10 +10,9 @@ is self-dual. """ dual(x) = x -nondual(r::AbstractUnitRange) = r isdual(::AbstractUnitRange) = false -flip(a::AbstractUnitRange) = dual(map_blocklabels(dual, a)) +flip(a::AbstractUnitRange) = dual(map_sectors(dual, a)) """ dag(r::AbstractUnitRange) @@ -29,24 +28,29 @@ Complex conjugates `a` and takes the dual of the axes. """ function dag(a::AbstractArray) a′ = similar(a, dual.(axes(a))) - a′ .= conj.(a) + a′ .= conj.(dual_axes(a)) return a′ end -map_blocklabels(::Any, a::AbstractUnitRange) = a +""" + ungrade(a::AbstractArray) + +Return an array without attached sectors. Avoid copying data when possible. +""" +ungrade(a::AbstractArray) = a + +map_sectors(::Any, a::AbstractUnitRange) = a to_sector(x) = x sector_type(x) = sector_type(typeof(x)) sector_type(::Type) = error("Not implemented") -struct NoLabel end -blocklabels(r::AbstractUnitRange) = Fill(NoLabel(), blocklength(r)) -blocklabels(v::AbstractBlockVector) = mapreduce(blocklabels, vcat, blocks(v)) +struct NoSector end +sectors(r::AbstractUnitRange) = Fill(NoSector(), blocklength(r)) +sectors(v::AbstractBlockVector) = mapreduce(sectors, vcat, blocks(v)) # == is just a range comparison that ignores labels. Need dedicated function to check equality. function space_isequal(a1::AbstractUnitRange, a2::AbstractUnitRange) - return (isdual(a1) == isdual(a2)) && - blocklabels(a1) == blocklabels(a2) && - blockisequal(a1, a2) + return (isdual(a1) == isdual(a2)) && sectors(a1) == sectors(a2) && blockisequal(a1, a2) end diff --git a/src/sector_product.jl b/src/sector_product.jl index 7d38c72..49b0b23 100644 --- a/src/sector_product.jl +++ b/src/sector_product.jl @@ -103,7 +103,7 @@ end function ×(sr1::SectorUnitRange, sr2::SectorUnitRange) @assert isdual(sr1) == isdual(sr2) return sectorrange( - nondual_sector(sr1) × nondual_sector(sr2), + sector(sr1) × sector(sr2), sector_multiplicity(sr1) * sector_multiplicity(sr1), isdual(sr1), ) @@ -134,7 +134,7 @@ end # Abelian case: fusion returns SectorProduct function fusion_rule(::AbelianStyle, s1::SectorProduct, s2::SectorProduct) - return only(blocklabels((fusion_rule(NotAbelianStyle(), s1, s2)))) + return only(sectors((fusion_rule(NotAbelianStyle(), s1, s2)))) end # lift ambiguities for TrivialSector @@ -234,5 +234,5 @@ arguments_diff(nt1::NamedTuple, nt2::NamedTuple) = symdiff_keys(nt1, nt2) function shared_arguments_fusion_rule(shared1::NT, shared2::NT) where {NT<:NamedTuple} tuple_fused = shared_arguments_fusion_rule(values(shared1), values(shared2)) - return map_blocklabels(SectorProduct ∘ NT ∘ arguments ∘ SectorProduct, tuple_fused) + return map_sectors(SectorProduct ∘ NT ∘ arguments ∘ SectorProduct, tuple_fused) end diff --git a/src/sectorunitrange.jl b/src/sectorunitrange.jl index 97ec292..8de435e 100644 --- a/src/sectorunitrange.jl +++ b/src/sectorunitrange.jl @@ -1,15 +1,18 @@ +# This files defines SectorUnitRange, a unit range associated with a sector and an arrow using BlockArrays: BlockArrays using TensorProducts: tensor_product +# ===================================== Definition ======================================= + # This implementation contains the "full range" # it does not check that such a range is consistent with the sector quantum_dimension # when sliced directly, the label is dropped # when sliced between multiplicities with sr[(:,1:1)], it returns another SectorUnitRange # TBD impose some compatibility constraints between range and quantum_dimension? struct SectorUnitRange{T,Sector,Range<:AbstractUnitRange{T}} <: AbstractUnitRange{T} - nondual_sector::Sector + sector::Sector full_range::Range isdual::Bool @@ -20,9 +23,7 @@ end const SectorOneTo{T,Sector,Range} = SectorUnitRange{T,Sector,Base.OneTo{T}} -# -# Constructors -# +# ==================================== Constructors ====================================== # sectorrange(SU2(1), 2:5) function sectorrange(s, r::AbstractUnitRange, b::Bool=false) @@ -39,82 +40,76 @@ function sectorrange(p::Pair, b::Bool=false) return sectorrange(first(p), last(p), b) end -# -# accessors -# -nondual_sector(sr::SectorUnitRange) = sr.nondual_sector -full_range(sr::SectorUnitRange) = sr.full_range +# ===================================== Accessors ======================================== + +sector(sr::SectorUnitRange) = sr.sector +ungrade(sr::SectorUnitRange) = sr.full_range isdual(sr::SectorUnitRange) = sr.isdual -# -# Base interface -# -Base.first(sr::SectorUnitRange) = first(full_range(sr)) +# ================================== Base interface ====================================== + +Base.first(sr::SectorUnitRange) = first(ungrade(sr)) -Base.iterate(sr::SectorUnitRange) = iterate(full_range(sr)) -Base.iterate(sr::SectorUnitRange, i::Integer) = iterate(full_range(sr), i) +Base.iterate(sr::SectorUnitRange) = iterate(ungrade(sr)) +Base.iterate(sr::SectorUnitRange, i::Integer) = iterate(ungrade(sr), i) -Base.length(sr::SectorUnitRange) = length(full_range(sr)) +Base.length(sr::SectorUnitRange) = length(ungrade(sr)) -Base.last(sr::SectorUnitRange) = last(full_range(sr)) +Base.last(sr::SectorUnitRange) = last(ungrade(sr)) # slicing -Base.getindex(sr::SectorUnitRange, i::Integer) = full_range(sr)[i] +Base.getindex(sr::SectorUnitRange, i::Integer) = ungrade(sr)[i] function Base.getindex(sr::SectorUnitRange, r::AbstractUnitRange{T}) where {T<:Integer} return sr[SymmetryStyle(sr), r] end function Base.getindex(sr::SectorUnitRange, ::NotAbelianStyle, r::AbstractUnitRange) - return full_range(sr)[r] + return ungrade(sr)[r] end function Base.getindex(sr::SectorUnitRange, ::AbelianStyle, r::AbstractUnitRange) - return sectorrange(nondual_sector(sr), full_range(sr)[r], isdual(sr)) + return sectorrange(sector(sr), ungrade(sr)[r], isdual(sr)) end # TODO replace (:,x) indexing with kronecker(:, x) Base.getindex(sr::SectorUnitRange, t::Tuple{Colon,<:Integer}) = sr[(:, last(t):last(t))] function Base.getindex(sr::SectorUnitRange, t::Tuple{Colon,<:AbstractUnitRange}) r = last(t) - new_range = - ((first(r) - 1) * length(nondual_sector(sr)) + 1):(last(r) * length(nondual_sector(sr))) - return sectorrange(nondual_sector(sr), full_range(sr)[new_range], isdual(sr)) + new_range = ((first(r) - 1) * length(sector(sr)) + 1):(last(r) * length(sector(sr))) + return sectorrange(sector(sr), ungrade(sr)[new_range], isdual(sr)) end function Base.show(io::IO, sr::SectorUnitRange) print(io, nameof(typeof(sr)), " ") if isdual(sr) - print(io, "dual(", nondual_sector(sr), ")") + print(io, "dual(", sector(sr), ")") else - print(io, nondual_sector(sr)) + print(io, sector(sr)) end - return print(io, " => ", full_range(sr)) + return print(io, " => ", ungrade(sr)) end -# -# BlockArrays interface -# +# ================================ BlockArrays interface ================================= -# -# GradedUnitRanges interface -# -function blocklabels(sr::SectorUnitRange) - return isdual(sr) ? [dual(nondual_sector(sr))] : [nondual_sector(sr)] -end +# generic -# TBD error for non-integer? -sector_multiplicity(sr::SectorUnitRange) = length(sr) ÷ length(nondual_sector(sr)) -sector_multiplicities(sr::SectorUnitRange) = [sector_multiplicity(sr)] # TBD remove? +# ============================= GradedUnitRanges interface =============================== + +sectors(sr::SectorUnitRange) = [sector(sr)] function dual(sr::SectorUnitRange) - return sectorrange(nondual_sector(sr), full_range(sr), !isdual(sr)) + return sectorrange(sector(sr), ungrade(sr), !isdual(sr)) end function flip(sr::SectorUnitRange) - return sectorrange(dual(nondual_sector(sr)), full_range(sr), !isdual(sr)) + return sectorrange(dual(sector(sr)), ungrade(sr), !isdual(sr)) end -function map_blocklabels(f, sr::SectorUnitRange) - return sectorrange(f(nondual_sector(sr)), full_range(sr), isdual(sr)) +function map_sectors(f, sr::SectorUnitRange) + return sectorrange(f(sector(sr)), ungrade(sr), isdual(sr)) end sector_type(::Type{<:SectorUnitRange{T,Sector}}) where {T,Sector} = Sector + +# TBD error for non-integer? +sector_multiplicity(sr::SectorUnitRange) = length(sr) ÷ length(sector(sr)) +sector_multiplicities(sr::SectorUnitRange) = [sector_multiplicity(sr)] # TBD remove? diff --git a/test/test_gradedarray.jl b/test/test_gradedarray.jl index 31758a8..b43b9ef 100644 --- a/test/test_gradedarray.jl +++ b/test/test_gradedarray.jl @@ -20,7 +20,8 @@ using GradedArrays: gradedrange, isdual, sectorrange, - space_isequal + space_isequal, + ungrade using SparseArraysBase: storedlength using LinearAlgebra: adjoint using Random: randn! @@ -72,9 +73,16 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) d1 = gradedrange([U1(0) => 2, U1(1) => 2]) d2 = gradedrange([U1(0) => 2, U1(1) => 2]) a = randn_blockdiagonal(elt, (d1, d2, d1, d2)) + @test a isa GradedArray{elt,4} @test axes(a, 1) isa GradedOneTo @test axes(view(a, 1:4, 1:4, 1:4, 1:4), 1) isa GradedOneTo + a0 = ungrade(a) + @test !(a0 isa GradedArray) + @test a0 isa BlockSparseArray{elt,4} + @test axes(a0) isa NTuple{4,BlockedOneTo{Int}} + @test a0 == a + for b in (a + a, 2 * a) @test size(b) == (4, 4, 4, 4) @test blocksize(b) == (2, 2, 2, 2) diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 23b93ce..a35e119 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -24,16 +24,16 @@ using GradedArrays: SectorUnitRange, SU, axis_cat, - blocklabels, dual, flip, gradedrange, isdual, - nondual_sector, + sector, sector_type, + sectors, sectorrange, space_isequal -using Test: @test, @test_broken, @test_throws, @testset +using Test: @test, @test_throws, @testset @testset "GradedUnitRanges basics" begin r0 = Base.OneTo(1) @@ -55,6 +55,7 @@ using Test: @test, @test_broken, @test_throws, @testset @test sector_type(g) === String @test blockisequal(g, b0) @test space_isequal(g, g) + @test space_isequal(copy(g), g) @test !space_isequal(r0, g) @test !space_isequal(g, r0) @test !space_isequal(g, b0) @@ -65,23 +66,10 @@ using Test: @test, @test_broken, @test_throws, @testset for x in iterate(g) @test x == 1 end - for x in iterate(g, 1) - @test x == 2 - end - for x in iterate(g, 2) - @test x == 3 - end - for x in iterate(g, 3) - @test x == 4 - end - for x in iterate(g, 4) - @test x == 5 - end - for x in iterate(g, 5) - @test x == 6 - end - for x in iterate(g, 6) - @test x == 7 + for i in 1:6 + for x in iterate(g, i) + @test x == i + 1 + end end @test isnothing(iterate(g, 7)) @test length(g) == 7 @@ -97,13 +85,13 @@ using Test: @test, @test_broken, @test_throws, @testset @test g[4] == 4 @test blocklengths(g) == [2, 3, 2] - @test blocklabels(g) == ["x", "y", "z"] + @test sectors(g) == ["x", "y", "z"] @test blockfirsts(g) == [1, 3, 6] @test first(g) == 1 @test blocklasts(g) == [2, 5, 7] @test last(g) == 7 @test blocklengths(only(axes(g))) == blocklengths(g) - @test blocklabels(only(axes(g))) == blocklabels(g) + @test sectors(only(axes(g))) == sectors(g) @test findblock(g, 2) == Block(1) @test findblock(g, 3) == Block(2) @test findblockindex(g, 3) == Block(2)[1] @@ -121,7 +109,7 @@ using Test: @test, @test_broken, @test_throws, @testset a = g[2:4] @test a isa GradedUnitRange @test !(a isa GradedOneTo) - @test blocklabels(a) == ["x", "y"] + @test sectors(a) == ["x", "y"] @test blocklength(a) == 2 @test space_isequal(first(blocks(a)), sectorrange("x", 2:2, isdual(g))) @test space_isequal(last(blocks(a)), sectorrange("y", 3:4, isdual(g))) @@ -153,7 +141,7 @@ using Test: @test, @test_broken, @test_throws, @testset @test ax == 1:length(a) @test length(ax) == length(a) @test blocklengths(ax) == blocklengths(a) - @test blocklabels(ax) == blocklabels(a) + @test sectors(ax) == sectors(a) a = g[Block.(Base.oneto(2))] @test (a isa GradedOneTo) == (g isa GradedOneTo) @@ -163,11 +151,8 @@ using Test: @test, @test_broken, @test_throws, @testset @test a isa BlockVector @test length(a) == 5 @test blocklength(a) == 2 - # TODO: `BlockArrays` doesn't define `blocklengths` - # `blocklengths(::BlockVector)`, unbrake this test - # once it does. - @test_broken blocklengths(a) == [2, 3] - @test blocklabels(a) == ["z", "y"] + @test length.(blocks(a)) == [2, 3] + @test sectors(a) == ["z", "y"] @test a[Block(1)] == 6:7 @test a[Block(2)] == 3:5 ax = only(axes(a)) @@ -178,9 +163,7 @@ using Test: @test, @test_broken, @test_throws, @testset @test a isa BlockVector @test length(a) == 3 @test blocklength(a) == 2 - # TODO: `BlockArrays` doesn't define `blocklengths` - # for `BlockVector`, should it? - @test_broken blocklengths(a) == [1, 2] + @test length.(blocks(a)) == [1, 2] @test a[Block(1)] == 6:6 @test a[Block(2)] == 4:5 ax = only(axes(a)) @@ -240,7 +223,7 @@ end @test length(g) == 8 @test !isdual(g) @test blockisequal(g, b0) - @test blocklabels(g) == [SU((0, 0)), SU((1, 0))] + @test sectors(g) == [SU((0, 0)), SU((1, 0))] @test space_isequal(g[Block(1)], sectorrange(SU((0, 0)), 2)) @test space_isequal(g[Block(2)], sectorrange(SU((1, 0)), 3:8)) @@ -251,7 +234,7 @@ end @test !space_isequal(dual(g), g) @test space_isequal(flip(g), gradedrange([SU((0, 0)) => 2, SU((1, 1)) => 2]; isdual=true)) - iterate(g) == (1, 1) + @test iterate(g) == (1, 1) for i in 1:7 @test iterate(g, i) == (i + 1, i + 1) end @@ -294,7 +277,7 @@ end @test a isa BlockVector @test length(a) == 8 @test blocklength(a) == 2 - @test blocklabels(a) == [SU((1, 0)), SU((0, 0))] + @test sectors(a) == [SU((1, 0)), SU((0, 0))] @test length.(blocks(g)) == [2, 6] @test space_isequal(a[Block(1)], sectorrange(SU((1, 0)), 3:8)) diff --git a/test/test_gradedunitrange_interface.jl b/test/test_gradedunitrange_interface.jl index 242b811..1a8df80 100644 --- a/test/test_gradedunitrange_interface.jl +++ b/test/test_gradedunitrange_interface.jl @@ -1,6 +1,7 @@ using BlockArrays: BlockedOneTo, blockedrange, blockisequal -using GradedArrays: NoLabel, blocklabels, dag, dual, flip, isdual, space_isequal +using GradedArrays: + NoSector, dag, dual, flip, isdual, map_sectors, sectors, space_isequal, ungrade using Test: @test, @testset using TensorProducts: OneToOne @@ -10,7 +11,9 @@ using TensorProducts: OneToOne @test dual(a0) isa OneToOne @test space_isequal(a0, a0) @test space_isequal(a0, dual(a0)) - @test only(blocklabels(a0)) == NoLabel() + @test only(sectors(a0)) == NoSector() + @test ungrade(a0) === a0 + @test map_sectors(identity, a0) === a0 a = 1:3 ad = dual(a) @@ -23,7 +26,9 @@ using TensorProducts: OneToOne @test af isa UnitRange @test space_isequal(ad, a) @test space_isequal(af, a) - @test only(blocklabels(a)) == NoLabel() + @test only(sectors(a)) == NoSector() + @test ungrade(a) === a + @test map_sectors(identity, a) === a a = blockedrange([2, 3]) ad = dual(a) @@ -34,5 +39,7 @@ using TensorProducts: OneToOne @test af isa BlockedOneTo @test blockisequal(ad, a) @test blockisequal(af, a) - @test blocklabels(a) == [NoLabel(), NoLabel()] + @test sectors(a) == [NoSector(), NoSector()] + @test ungrade(a) === a + @test map_sectors(identity, a) === a end diff --git a/test/test_gradedunitranges_tensor_product.jl b/test/test_gradedunitranges_tensor_product.jl index f1ec52c..98e4dd9 100644 --- a/test/test_gradedunitranges_tensor_product.jl +++ b/test/test_gradedunitranges_tensor_product.jl @@ -6,14 +6,14 @@ using GradedArrays: SectorUnitRange, SU2, U1, - blocklabels, dual, - unmerged_tensor_product, flip, gradedrange, + isdual, sectorrange, + sectors, space_isequal, - isdual + unmerged_tensor_product using TensorProducts: ⊗, OneToOne, tensor_product using Test: @test, @testset using TestExtras: @constinferred @@ -39,7 +39,7 @@ GradedArrays.tensor_product(s1::String, s2::String) = gradedrange([s1 * s2 => 1] @test c isa GradedOneTo @test length(c) == 375 @test blocklength(c) == 8 - @test blocklabels(c) == ["xxx", "yxx", "xyx", "yyx", "xxy", "yxy", "xyy", "yyy"] + @test sectors(c) == ["xxx", "yxx", "xyx", "yyx", "xxy", "yxy", "xyy", "yyy"] a = gradedrange([U1(1) => 1, U1(2) => 3, U1(1) => 1]) @test space_isequal( diff --git a/test/test_sectorunitrange.jl b/test/test_sectorunitrange.jl index abfa1d8..ab51a6e 100644 --- a/test/test_sectorunitrange.jl +++ b/test/test_sectorunitrange.jl @@ -17,27 +17,27 @@ using GradedArrays: U1, SU, SectorUnitRange, - blocklabels, dual, flip, - full_range, isdual, - nondual_sector, quantum_dimension, + sector, sector_multiplicities, sector_multiplicity, sector_type, sectorrange, - space_isequal + sectors, + space_isequal, + ungrade @testset "SectorUnitRange" begin sr = sectorrange(SU((1, 0)), 2) @test sr isa SectorUnitRange # accessors - @test nondual_sector(sr) == SU((1, 0)) - @test full_range(sr) isa Base.OneTo - @test full_range(sr) == 1:6 + @test sector(sr) == SU((1, 0)) + @test ungrade(sr) isa Base.OneTo + @test ungrade(sr) == 1:6 @test !isdual(sr) # Base interface @@ -58,23 +58,23 @@ using GradedArrays: sr = sectorrange(SU((1, 0)) => 2) @test sr isa SectorUnitRange - @test nondual_sector(sr) == SU((1, 0)) - @test full_range(sr) isa Base.OneTo - @test full_range(sr) == 1:6 + @test sector(sr) == SU((1, 0)) + @test ungrade(sr) isa Base.OneTo + @test ungrade(sr) == 1:6 @test !isdual(sr) sr = sectorrange(SU((1, 0)) => 2, true) @test sr isa SectorUnitRange - @test nondual_sector(sr) == SU((1, 0)) - @test full_range(sr) isa Base.OneTo - @test full_range(sr) == 1:6 + @test sector(sr) == SU((1, 0)) + @test ungrade(sr) isa Base.OneTo + @test ungrade(sr) == 1:6 @test isdual(sr) sr = sectorrange(SU((1, 0)), 4:10, true) @test sr isa SectorUnitRange - @test nondual_sector(sr) == SU((1, 0)) - @test full_range(sr) isa UnitRange - @test full_range(sr) == 4:10 + @test sector(sr) == SU((1, 0)) + @test ungrade(sr) isa UnitRange + @test ungrade(sr) == 4:10 @test isdual(sr) sr = sectorrange(SU((1, 0)), 2) @@ -104,18 +104,18 @@ using GradedArrays: # GradedUnitRanges interface @test sector_type(sr) === SU{3,2} @test sector_type(typeof(sr)) === SU{3,2} - @test blocklabels(sr) == [SU((1, 0))] + @test sectors(sr) == [SU((1, 0))] @test sector_multiplicity(sr) == 2 @test sector_multiplicities(sr) == [2] @test quantum_dimension(sr) == 6 srd = dual(sr) - @test nondual_sector(srd) == SU((1, 0)) + @test sector(srd) == SU((1, 0)) @test space_isequal(srd, sectorrange(SU((1, 0)), 2, true)) - @test blocklabels(srd) == [SU((1, 1))] + @test sectors(srd) == [SU((1, 0))] srf = flip(sr) - @test nondual_sector(srf) == SU((1, 1)) + @test sector(srf) == SU((1, 1)) @test space_isequal(srf, sectorrange(SU((1, 1)), 2, true)) # getindex From 08ce6305ec5fd21240897626a4410294f0f5af63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 24 Apr 2025 17:03:09 -0400 Subject: [PATCH 22/33] use mortar_axis and eachblockaxis --- src/abstractsector.jl | 2 +- src/fusion.jl | 2 +- src/gradedunitrange.jl | 61 ++++++++++++++++++----------------- src/sector_product.jl | 2 +- test/test_gradedunitrange.jl | 6 ++-- test/test_tensoralgebraext.jl | 12 ++----- 6 files changed, 40 insertions(+), 45 deletions(-) diff --git a/src/abstractsector.jl b/src/abstractsector.jl index 2a6e1ab..2ac4a73 100644 --- a/src/abstractsector.jl +++ b/src/abstractsector.jl @@ -39,7 +39,7 @@ quantum_dimension(::AbelianStyle, ::AbstractSector) = 1 # convert to range to_gradedrange(c::AbstractSector) = gradedrange([c => 1]) -to_gradedrange(sr::SectorUnitRange) = axis_cat([sr]) +to_gradedrange(sr::SectorUnitRange) = mortar_axis([sr]) to_gradedrange(g::AbstractGradedUnitRange) = g function nsymbol(s1::AbstractSector, s2::AbstractSector, s3::AbstractSector) diff --git a/src/fusion.jl b/src/fusion.jl index 7b121e2..d58380a 100644 --- a/src/fusion.jl +++ b/src/fusion.jl @@ -64,7 +64,7 @@ unmerged_tensor_product(a1, a2) = a1 ⊗ a2 function unmerged_tensor_product(a1::AbstractGradedUnitRange, a2::AbstractGradedUnitRange) new_axes = map(splat(⊗), Iterators.flatten((Iterators.product(blocks(a1), blocks(a2)),))) - return axis_cat(new_axes) + return mortar_axis(new_axes) end # convention: sort dual GradedUnitRange according to nondual blocks diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 1129eaa..078368b 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -32,71 +32,74 @@ abstract type AbstractGradedUnitRange{T,BlockLasts} <: struct GradedUnitRange{T,SUR<:SectorOneTo{T},BR<:AbstractUnitRange{T},BlockLasts} <: AbstractGradedUnitRange{T,BlockLasts} - sector_axes::Vector{SUR} + eachblockaxis::Vector{SUR} full_range::BR function GradedUnitRange{T,SUR,BR,BlockLasts}( - sector_axes::AbstractVector{SUR}, full_range::AbstractUnitRange{T} + eachblockaxis::AbstractVector{SUR}, full_range::AbstractUnitRange{T} ) where {T,SUR,BR,BlockLasts} - length.(sector_axes) == blocklengths(full_range) || + length.(eachblockaxis) == blocklengths(full_range) || throw(ArgumentError("sectors and range are not compatible")) - allequal(isdual.(sector_axes)) || + allequal(isdual.(eachblockaxis)) || throw(ArgumentError("all blocks must have same duality")) typeof(blocklasts(full_range)) == BlockLasts || throw(TypeError(:BlockLasts, "", blocklasts(full_range))) - return new{T,SUR,BR,BlockLasts}(sector_axes, full_range) + return new{T,SUR,BR,BlockLasts}(eachblockaxis, full_range) end end const GradedOneTo{T,SUR,BR,BlockLasts} = GradedUnitRange{T,SUR,BR,BlockLasts} where {BR<:BlockedOneTo} -function GradedUnitRange(sector_axes::AbstractVector, full_range::AbstractUnitRange) +function GradedUnitRange(eachblockaxis::AbstractVector, full_range::AbstractUnitRange) return GradedUnitRange{ - eltype(full_range),eltype(sector_axes),typeof(full_range),typeof(blocklasts(full_range)) + eltype(full_range), + eltype(eachblockaxis), + typeof(full_range), + typeof(blocklasts(full_range)), }( - sector_axes, full_range + eachblockaxis, full_range ) end # ===================================== Accessors ======================================== -sector_axes(g::GradedUnitRange) = g.sector_axes +eachblockaxis(g::GradedUnitRange) = g.eachblockaxis ungrade(g::GradedUnitRange) = g.full_range -sector_multiplicities(g::GradedUnitRange) = sector_multiplicity.(sector_axes(g)) +sector_multiplicities(g::GradedUnitRange) = sector_multiplicity.(eachblockaxis(g)) sector_type(::Type{<:GradedUnitRange{<:Any,SUR}}) where {SUR} = sector_type(SUR) # ==================================== Constructors ====================================== -function axis_cat(sectors::AbstractVector{<:SectorOneTo}) +function mortar_axis(sectors::AbstractVector{<:SectorOneTo}) brange = blockedrange(length.(sectors)) return GradedUnitRange(sectors, brange) end -function axis_cat(gaxes::AbstractVector{<:GradedOneTo}) - return axis_cat(mapreduce(sector_axes, vcat, gaxes)) +function mortar_axis(gaxes::AbstractVector{<:GradedOneTo}) + return mortar_axis(mapreduce(eachblockaxis, vcat, gaxes)) end function gradedrange( sectors_lengths::AbstractVector{<:Pair{<:Any,<:Integer}}; isdual::Bool=false ) - gsector_axes = sectorrange.(sectors_lengths, isdual) - return axis_cat(gsector_axes) + geachblockaxis = sectorrange.(sectors_lengths, isdual) + return mortar_axis(geachblockaxis) end # ============================= GradedUnitRanges interface =============================== -dual(g::GradedUnitRange) = GradedUnitRange(dual.(sector_axes(g)), ungrade(g)) +dual(g::GradedUnitRange) = GradedUnitRange(dual.(eachblockaxis(g)), ungrade(g)) -isdual(g::AbstractGradedUnitRange) = isdual(first(sector_axes(g))) # crash for empty. Should not be an issue. +isdual(g::AbstractGradedUnitRange) = isdual(first(eachblockaxis(g))) # crash for empty. Should not be an issue. function sectors(g::AbstractGradedUnitRange) - return sector.(sector_axes(g)) + return sector.(eachblockaxis(g)) end function map_sectors(f, g::GradedUnitRange) - return GradedUnitRange(map_sectors.(f, sector_axes(g)), ungrade(g)) + return GradedUnitRange(map_sectors.(f, eachblockaxis(g)), ungrade(g)) end ### GradedUnitRange specific slicing @@ -106,7 +109,7 @@ function gradedunitrange_getindices( gblocks = map(index -> g[index], Vector(indices)) # pass block labels to the axes of the output, # such that `only(axes(g[indices])) isa `GradedOneTo` - newg = axis_cat(sectorrange.(sector.(gblocks) .=> length.(gblocks), isdual(g))) + newg = mortar_axis(sectorrange.(sector.(gblocks) .=> length.(gblocks), isdual(g))) return mortar(gblocks, (newg,)) end @@ -117,10 +120,10 @@ function gradedunitrange_getindices( bf = findblock(g, first(indices)) bl = findblock(g, last(indices)) new_sectors = sectors(g)[Int.(bf:bl)] - new_sector_axes = sectorrange.( + new_eachblockaxis = sectorrange.( new_sectors .=> Base.oneto.(blocklengths(new_range)), isdual(g) ) - return GradedUnitRange(new_sector_axes, new_range) + return GradedUnitRange(new_eachblockaxis, new_range) end function gradedunitrange_getindices( @@ -147,7 +150,7 @@ function Base.AbstractUnitRange{T}(a::AbstractGradedUnitRange{T}) where {T} end function Base.axes(ga::AbstractGradedUnitRange) - return (axis_cat(sector_axes(ga)),) + return (mortar_axis(eachblockaxis(ga)),) end # preserve axes in SubArray @@ -195,7 +198,7 @@ function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) !space_isequal(a, b) && throw(ArgumentError("axes are not compatible")) #!space_isequal(a, b) && return combine_blockaxes(ungrade(a), ungrade(b)) # preserve BlockArrays convention for BlockedUnitRange / BlockedOneTo - return GradedUnitRange(sector_axes(a), combine_blockaxes(ungrade(a), ungrade(b))) + return GradedUnitRange(eachblockaxis(a), combine_blockaxes(ungrade(a), ungrade(b))) end # preserve BlockedOneTo when possible @@ -231,14 +234,14 @@ end function BlockSparseArrays.blockedunitrange_getindices( a::AbstractGradedUnitRange, index::Block{1} ) - sr = sector_axes(a)[Int(index)] + sr = eachblockaxis(a)[Int(index)] return sectorrange(sector(sr), ungrade(a)[index], isdual(sr)) end function BlockSparseArrays.blockedunitrange_getindices( ga::GradedUnitRange, indices::BlockRange ) - return GradedUnitRange(sector_axes(ga)[Int.(indices)], ungrade(ga)[indices]) + return GradedUnitRange(eachblockaxis(ga)[Int.(indices)], ungrade(ga)[indices]) end function BlockSparseArrays.blockedunitrange_getindices( @@ -254,7 +257,7 @@ function BlockSparseArrays.blockedunitrange_getindices( # such that `only(axes(a[indices])) isa `GradedUnitRange` # if `a isa `GradedUnitRange` new_sectoraxes = sectorrange.(sector.(gblocks), Base.oneto.(length.(gblocks)), isdual(g)) - newg = axis_cat(new_sectoraxes) + newg = mortar_axis(new_sectoraxes) return mortar(gblocks, (newg,)) end @@ -267,7 +270,7 @@ function BlockSparseArrays.blockedunitrange_getindices( new_sectors = map(b -> sectors(g)[Int.(b)], blocks(indices)) @assert all(allequal.(new_sectors)) new_lengths = length.(blks) - new_sector_axes = sectorrange.(first.(new_sectors), Base.oneto.(new_lengths), isdual(g)) - newg = axis_cat(new_sector_axes) + new_eachblockaxis = sectorrange.(first.(new_sectors), Base.oneto.(new_lengths), isdual(g)) + newg = mortar_axis(new_eachblockaxis) return mortar(blks, (newg,)) end diff --git a/src/sector_product.jl b/src/sector_product.jl index 49b0b23..54ecb34 100644 --- a/src/sector_product.jl +++ b/src/sector_product.jl @@ -111,7 +111,7 @@ end function ×(g1::AbstractGradedUnitRange, g2::AbstractGradedUnitRange) v = map(splat(×), Iterators.flatten((Iterators.product(blocks(g1), blocks(g2)),),)) - return axis_cat(v) + return mortar_axis(v) end # ==================================== Fusion rules ====================================== diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index a35e119..449d456 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -23,11 +23,11 @@ using GradedArrays: SectorOneTo, SectorUnitRange, SU, - axis_cat, dual, flip, gradedrange, isdual, + mortar_axis, sector, sector_type, sectors, @@ -211,8 +211,8 @@ using Test: @test, @test_throws, @testset sr1 = sectorrange("x", 2) sr2 = sectorrange("y", 3) - @test space_isequal(g1[Block(1):Block(2)], axis_cat([sr1, sr2])) - @test_throws ArgumentError axis_cat([sr1, dual(sr2)]) + @test space_isequal(g1[Block(1):Block(2)], mortar_axis([sr1, sr2])) + @test_throws ArgumentError mortar_axis([sr1, dual(sr2)]) end @testset "Non abelian axis" begin diff --git a/test/test_tensoralgebraext.jl b/test/test_tensoralgebraext.jl index ed4a45b..1ac8b4b 100644 --- a/test/test_tensoralgebraext.jl +++ b/test/test_tensoralgebraext.jl @@ -1,15 +1,7 @@ using BlockArrays: Block, blocksize using BlockSparseArrays: BlockSparseArray using GradedArrays: - GradedArray, - GradedMatrix, - GradedOneTo, - U1, - blocklabels, - dual, - flip, - gradedrange, - space_isequal + GradedArray, GradedMatrix, GradedOneTo, U1, dual, flip, gradedrange, space_isequal using Random: randn! using TensorAlgebra: contract, matricize, unmatricize using Test: @test, @test_broken, @testset @@ -97,7 +89,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test a_dest isa GradedArray @test a_dest ≈ a_dest_dense - # vector matrix + # vector matrix a_dest, dimnames_dest = contract(a3, (1, 2), a1, (2, -1, -2, 1)) a_dest_dense, dimnames_dest_dense = contract(a3_dense, (1, 2), a1_dense, (2, -1, -2, 1)) @test dimnames_dest == dimnames_dest_dense From db322dba31c6503027fcf239a356b9e8865ff8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 24 Apr 2025 17:20:26 -0400 Subject: [PATCH 23/33] fix dag --- src/gradedunitrange.jl | 5 ++--- src/gradedunitrange_interface.jl | 2 +- test/test_gradedarray.jl | 8 +++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 078368b..315eaf2 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -163,7 +163,8 @@ end function Base.show(io::IO, g::AbstractGradedUnitRange) v = sectors(g) .=> blocklengths(g) - return print(io, nameof(typeof(g)), '[', join(repr.(v), ", "), ']') + s = isdual(g) ? " dual " : "" + return print(io, nameof(typeof(g)), s, '[', join(repr.(v), ", "), ']') end Base.first(a::AbstractGradedUnitRange) = first(ungrade(a)) @@ -194,9 +195,7 @@ end function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) # avoid mixing different labels - # better to throw explicit error than silently dropping labels !space_isequal(a, b) && throw(ArgumentError("axes are not compatible")) - #!space_isequal(a, b) && return combine_blockaxes(ungrade(a), ungrade(b)) # preserve BlockArrays convention for BlockedUnitRange / BlockedOneTo return GradedUnitRange(eachblockaxis(a), combine_blockaxes(ungrade(a), ungrade(b))) end diff --git a/src/gradedunitrange_interface.jl b/src/gradedunitrange_interface.jl index 5d1033e..ab1e056 100644 --- a/src/gradedunitrange_interface.jl +++ b/src/gradedunitrange_interface.jl @@ -28,7 +28,7 @@ Complex conjugates `a` and takes the dual of the axes. """ function dag(a::AbstractArray) a′ = similar(a, dual.(axes(a))) - a′ .= conj.(dual_axes(a)) + a′ .= conj.(ungrade(a)) return a′ end diff --git a/test/test_gradedarray.jl b/test/test_gradedarray.jl index b43b9ef..34708fc 100644 --- a/test/test_gradedarray.jl +++ b/test/test_gradedarray.jl @@ -420,7 +420,9 @@ end a[Block(1, 1)] = randn(elt, 2, 2) a[Block(2, 2)] = randn(elt, 3, 3) @test isdual.(axes(a)) == (false, true) - @test_broken ad = dag(a) - #@test Array(ad) == conj(Array(a)) - #@test isdual.(axes(ad)) == (true, false) + ad = dag(a) + @test Array(ad) == conj(Array(a)) + @test isdual.(axes(ad)) == (true, false) + @test space_isequal(axes(ad, 1), dual(axes(a, 1))) + @test space_isequal(axes(ad, 2), dual(axes(a, 2))) end From ea8fa7e4711abda298f4fdf7f544867131eba725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 24 Apr 2025 18:40:04 -0400 Subject: [PATCH 24/33] fix matricize --- src/gradedunitrange.jl | 13 ++++++++++--- test/test_gradedunitrange.jl | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 315eaf2..3cbb604 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -194,10 +194,17 @@ function BlockArrays.blocklasts(a::AbstractGradedUnitRange) end function BlockArrays.combine_blockaxes(a::GradedUnitRange, b::GradedUnitRange) - # avoid mixing different labels - !space_isequal(a, b) && throw(ArgumentError("axes are not compatible")) + isdual(a) == isdual(b) || throw(ArgumentError("axes duality are not compatible")) + r = combine_blockaxes(ungrade(a), ungrade(b)) + sector_axes = map(zip(blocklengths(r), blocklasts(r))) do (blength, blast) + s_a = sector(a[findblock(a, blast)]) + if s_a != sector(b[findblock(b, blast)]) # forbid conflicting sectors + throw(ArgumentError("sectors are not compatible")) + end + return sectorrange(s_a, Base.oneto(blength), isdual(a)) + end # preserve BlockArrays convention for BlockedUnitRange / BlockedOneTo - return GradedUnitRange(eachblockaxis(a), combine_blockaxes(ungrade(a), ungrade(b))) + return GradedUnitRange(sector_axes, r) end # preserve BlockedOneTo when possible diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 449d456..94d5b9f 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -209,6 +209,14 @@ using Test: @test, @test_throws, @testset @test !(a isa GradedOneTo) @test space_isequal(a, g2) + g3 = gradedrange(["x" => 2, "a" => 1, "y" => 2, "z" => 2]) + @test_throws ArgumentError combine_blockaxes(g1, g3) + + g3 = gradedrange(["x" => 1, "x" => 1, "y" => 2, "z" => 2]) + g4 = gradedrange(["x" => 2, "y" => 1, "y" => 1, "z" => 2]) + a = combine_blockaxes(g3, g4) + @test space_isequal(a, gradedrange(["x" => 1, "x" => 1, "y" => 1, "y" => 1, "z" => 2])) + sr1 = sectorrange("x", 2) sr2 = sectorrange("y", 3) @test space_isequal(g1[Block(1):Block(2)], mortar_axis([sr1, sr2])) From 2ea6291abd787c0b67d10b78645794a5cdcb9a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 24 Apr 2025 21:51:41 -0400 Subject: [PATCH 25/33] define multiplicity slicing --- src/gradedunitrange.jl | 42 ++++++++++++++++++++++++++++++++++++ test/test_gradedunitrange.jl | 21 ++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 3cbb604..7b1f4ba 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -10,6 +10,7 @@ using BlockArrays: BlockVector, BlockedOneTo, block, + blockfirsts, blockedrange, blocklasts, blocklengths, @@ -181,6 +182,7 @@ for T in [ :(BlockRange{1,<:Tuple{Base.OneTo}}), :(BlockRange{1,<:Tuple{AbstractUnitRange{<:Integer}}}), :(BlockSlice), + :(Tuple{Colon,<:Any}), # TODO replace with Kronecker range ] @eval Base.getindex(g::AbstractGradedUnitRange, indices::$T) = blockedunitrange_getindices( g, indices @@ -280,3 +282,43 @@ function BlockSparseArrays.blockedunitrange_getindices( newg = mortar_axis(new_eachblockaxis) return mortar(blks, (newg,)) end + +# TODO use Kronecker range +# convention: return a sectorrange for this multiplicity +function BlockSparseArrays.blockedunitrange_getindices( + g::AbstractGradedUnitRange, indices::Tuple{Colon,<:Integer} +) + i = last(indices) + mult_range = blockedrange(sector_multiplicities(g)) + b = findblock(mult_range, i) + return g[b][(:, i - first(mult_range[b]) + 1)] +end + +# TODO use Kronecker range +# convention: return a gradedunitrange +function BlockSparseArrays.blockedunitrange_getindices( + g::AbstractGradedUnitRange, indices::Tuple{Colon,<:AbstractUnitRange{<:Integer}} +) + r = last(indices) + mult_range = blockedrange(sector_multiplicities(g)) + bf, bl = map(i -> Int(findblock(mult_range, i)), (first(r), last(r))) + new_first = + blockfirsts(g)[bf] + (first(r) - first(mult_range[Block(bf)])) * length(sectors(g)[bf]) + + if bf == bl + sr = sectorrange(sectors(g)[bf], length(r), isdual(g)) + new_range = blockedrange(new_first, [length(sr)]) + return GradedUnitRange([sr], new_range) + end + + sr_first = sectorrange( + sectors(g)[bf], blocklasts(mult_range)[bf] - first(r) + 1, isdual(g) + ) + sr_last = sectorrange( + sectors(g)[bl], last(r) - blockfirsts(mult_range)[bl] + 1, isdual(g) + ) + sector_axes = vcat([sr_first], eachblockaxis(g[Block.((bf + 1):(bl - 1))]), [sr_last]) + + new_range = blockedrange(new_first, length.(sector_axes)) + return GradedUnitRange(sector_axes, new_range) +end diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 94d5b9f..07d087d 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -29,6 +29,7 @@ using GradedArrays: isdual, mortar_axis, sector, + sector_multiplicities, sector_type, sectors, sectorrange, @@ -86,6 +87,7 @@ using Test: @test, @test_throws, @testset @test g[4] == 4 @test blocklengths(g) == [2, 3, 2] @test sectors(g) == ["x", "y", "z"] + @test sector_multiplicities(g) == [2, 3, 2] @test blockfirsts(g) == [1, 3, 6] @test first(g) == 1 @test blocklasts(g) == [2, 5, 7] @@ -159,6 +161,15 @@ using Test: @test, @test_throws, @testset @test ax isa GradedOneTo @test space_isequal(ax, gradedrange(["z" => 2, "y" => 3]; isdual=isdual(g))) + sr = g[(:, 1)] + @test sr isa SectorUnitRange + @test !(sr isa SectorOneTo) + @test space_isequal(sr, sectorrange("x", 1:1, isdual(g))) + sr = g[(:, 3)] + @test sr isa SectorUnitRange + @test !(sr isa SectorOneTo) + @test space_isequal(sr, sectorrange("y", 3:3, isdual(g))) + a = g[[Block(3)[1:1], Block(2)[2:3]]] @test a isa BlockVector @test length(a) == 3 @@ -232,6 +243,7 @@ end @test !isdual(g) @test blockisequal(g, b0) @test sectors(g) == [SU((0, 0)), SU((1, 0))] + @test sector_multiplicities(g) == [2, 2] @test space_isequal(g[Block(1)], sectorrange(SU((0, 0)), 2)) @test space_isequal(g[Block(2)], sectorrange(SU((1, 0)), 3:8)) @@ -294,6 +306,15 @@ end @test ax isa GradedOneTo @test space_isequal(ax, gradedrange([SU((1, 0)) => 2, SU((0, 0)) => 2])) + sr = g[(:, 1)] + @test sr isa SectorUnitRange + @test !(sr isa SectorOneTo) + @test space_isequal(sr, sectorrange(SU((0, 0)), 1:1)) + sr = g[(:, 3)] + @test sr isa SectorUnitRange + @test !(sr isa SectorOneTo) + @test space_isequal(sr, sectorrange(SU((1, 0)), 3:5)) + a = g[[Block(2)[1:3], Block(1)[2:2]]] @test a isa BlockVector @test length(a) == 4 From db0f0e8047791d42a925ece225c64885a1e640a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 24 Apr 2025 22:35:15 -0400 Subject: [PATCH 26/33] simpler mult slicing --- src/gradedunitrange.jl | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index 7b1f4ba..ff67a4f 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -304,21 +304,10 @@ function BlockSparseArrays.blockedunitrange_getindices( bf, bl = map(i -> Int(findblock(mult_range, i)), (first(r), last(r))) new_first = blockfirsts(g)[bf] + (first(r) - first(mult_range[Block(bf)])) * length(sectors(g)[bf]) - - if bf == bl - sr = sectorrange(sectors(g)[bf], length(r), isdual(g)) - new_range = blockedrange(new_first, [length(sr)]) - return GradedUnitRange([sr], new_range) - end - - sr_first = sectorrange( - sectors(g)[bf], blocklasts(mult_range)[bf] - first(r) + 1, isdual(g) + new_axes = sectorrange.( + sectors(g)[bf:bl] .=> blocklengths(blockedunitrange_getindices(mult_range, r)), + isdual(g), ) - sr_last = sectorrange( - sectors(g)[bl], last(r) - blockfirsts(mult_range)[bl] + 1, isdual(g) - ) - sector_axes = vcat([sr_first], eachblockaxis(g[Block.((bf + 1):(bl - 1))]), [sr_last]) - - new_range = blockedrange(new_first, length.(sector_axes)) - return GradedUnitRange(sector_axes, new_range) + new_range = blockedrange(new_first, length.(new_axes)) + return GradedUnitRange(new_axes, new_range) end From 30156744570be9984552204fa0aebc253b606b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 25 Apr 2025 10:08:29 -0400 Subject: [PATCH 27/33] gradedrange with first --- src/gradedunitrange.jl | 14 ++++++++++--- test/test_gradedunitrange.jl | 38 +++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index ff67a4f..c781ac8 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -74,9 +74,9 @@ sector_type(::Type{<:GradedUnitRange{<:Any,SUR}}) where {SUR} = sector_type(SUR) # ==================================== Constructors ====================================== -function mortar_axis(sectors::AbstractVector{<:SectorOneTo}) - brange = blockedrange(length.(sectors)) - return GradedUnitRange(sectors, brange) +function mortar_axis(geachblockaxis::AbstractVector{<:SectorOneTo}) + brange = blockedrange(length.(geachblockaxis)) + return GradedUnitRange(geachblockaxis, brange) end function mortar_axis(gaxes::AbstractVector{<:GradedOneTo}) @@ -90,6 +90,14 @@ function gradedrange( return mortar_axis(geachblockaxis) end +function gradedrange( + f::Integer, sectors_lengths::AbstractVector{<:Pair{<:Any,<:Integer}}; isdual::Bool=false +) + geachblockaxis = sectorrange.(sectors_lengths, isdual) + brange = blockedrange(f, length.(geachblockaxis)) + return GradedUnitRange(geachblockaxis, brange) +end + # ============================= GradedUnitRanges interface =============================== dual(g::GradedUnitRange) = GradedUnitRange(dual.(eachblockaxis(g)), ungrade(g)) diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 07d087d..fa840e6 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -43,7 +43,7 @@ using Test: @test, @test_throws, @testset @test g1 isa GradedOneTo @test !isdual(g1) - g2 = g1[1:7] + g2 = gradedrange(1, ["x" => 2, "y" => 3, "z" => 2]) @test !(g2 isa GradedOneTo) @test !isdual(g2) @@ -161,6 +161,8 @@ using Test: @test, @test_throws, @testset @test ax isa GradedOneTo @test space_isequal(ax, gradedrange(["z" => 2, "y" => 3]; isdual=isdual(g))) + # slice with one multiplicity + # TODO use dedicated Kroneckrange sr = g[(:, 1)] @test sr isa SectorUnitRange @test !(sr isa SectorOneTo) @@ -170,6 +172,13 @@ using Test: @test, @test_throws, @testset @test !(sr isa SectorOneTo) @test space_isequal(sr, sectorrange("y", 3:3, isdual(g))) + # slice along multiplicities + # TODO use dedicated Kroneckrange + for i in 1:length(g), j in i:length(g) + @test g[(:, i:j)] isa GradedUnitRange + @test space_isequal(g[(:, i:j)], g[i:j]) + end + a = g[[Block(3)[1:1], Block(2)[2:3]]] @test a isa BlockVector @test length(a) == 3 @@ -306,6 +315,8 @@ end @test ax isa GradedOneTo @test space_isequal(ax, gradedrange([SU((1, 0)) => 2, SU((0, 0)) => 2])) + # slice with one multiplicity + # TODO use dedicated Kroneckrange sr = g[(:, 1)] @test sr isa SectorUnitRange @test !(sr isa SectorOneTo) @@ -315,6 +326,31 @@ end @test !(sr isa SectorOneTo) @test space_isequal(sr, sectorrange(SU((1, 0)), 3:5)) + g3 = gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 2, SU((2, 1)) => 2]; isdual=true) + @test g3[(:, 1:1)] isa GradedUnitRange + @test space_isequal(g3[(:, 1:1)], gradedrange([SU((0, 0)) => 1]; isdual=true)) + @test space_isequal(g3[(:, 1:2)], gradedrange([SU((0, 0)) => 2]; isdual=true)) + @test space_isequal( + g3[(:, 1:3)], gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 1]; isdual=true) + ) + @test space_isequal( + g3[(:, 1:4)], gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 2]; isdual=true) + ) + @test space_isequal( + g3[(:, 1:5)], + gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 2, SU((2, 1)) => 1]; isdual=true), + ) + @test space_isequal( + g3[(:, 2:5)], + gradedrange(2, [SU((0, 0)) => 1, SU((1, 0)) => 2, SU((2, 1)) => 1]; isdual=true), + ) + @test space_isequal( + g3[(:, 3:5)], gradedrange(3, [SU((1, 0)) => 2, SU((2, 1)) => 1]; isdual=true) + ) + @test space_isequal( + g3[(:, 4:5)], gradedrange(6, [SU((1, 0)) => 1, SU((2, 1)) => 1]; isdual=true) + ) + a = g[[Block(2)[1:3], Block(1)[2:2]]] @test a isa BlockVector @test length(a) == 4 From 7564cdaa6d0230fcc7cb4fc526817d86bbbcbb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 25 Apr 2025 10:13:02 -0400 Subject: [PATCH 28/33] better test filenames --- test/{test_gradedunitrange_interface.jl => test_interface.jl} | 4 +++- ...dedunitranges_tensor_product.jl => test_tensor_product.jl} | 0 2 files changed, 3 insertions(+), 1 deletion(-) rename test/{test_gradedunitrange_interface.jl => test_interface.jl} (94%) rename test/{test_gradedunitranges_tensor_product.jl => test_tensor_product.jl} (100%) diff --git a/test/test_gradedunitrange_interface.jl b/test/test_interface.jl similarity index 94% rename from test/test_gradedunitrange_interface.jl rename to test/test_interface.jl index 1a8df80..0331096 100644 --- a/test/test_gradedunitrange_interface.jl +++ b/test/test_interface.jl @@ -14,13 +14,13 @@ using TensorProducts: OneToOne @test only(sectors(a0)) == NoSector() @test ungrade(a0) === a0 @test map_sectors(identity, a0) === a0 + @test dag(a0) === a0 a = 1:3 ad = dual(a) af = flip(a) @test !isdual(a) @test !isdual(ad) - @test !isdual(dag(a)) @test !isdual(af) @test ad isa UnitRange @test af isa UnitRange @@ -29,6 +29,7 @@ using TensorProducts: OneToOne @test only(sectors(a)) == NoSector() @test ungrade(a) === a @test map_sectors(identity, a) === a + @test dag(a) === a a = blockedrange([2, 3]) ad = dual(a) @@ -42,4 +43,5 @@ using TensorProducts: OneToOne @test sectors(a) == [NoSector(), NoSector()] @test ungrade(a) === a @test map_sectors(identity, a) === a + @test dag(a) === a end diff --git a/test/test_gradedunitranges_tensor_product.jl b/test/test_tensor_product.jl similarity index 100% rename from test/test_gradedunitranges_tensor_product.jl rename to test/test_tensor_product.jl From 04cbfa080ac0ad3cf3ea92d86f62683edf1f151b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 25 Apr 2025 12:18:20 -0400 Subject: [PATCH 29/33] update exports --- src/GradedArrays.jl | 12 ++++++++---- src/sector_definitions/trivial.jl | 4 ---- test/test_exports.jl | 7 ++++++- test/test_gradedunitrange.jl | 4 ++++ test/test_sectorunitrange.jl | 6 ++++++ test/test_symmetrysectors_simple_sectors.jl | 5 +++++ test/test_tensor_product.jl | 3 +++ 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/GradedArrays.jl b/src/GradedArrays.jl index c0a0b86..a5ca1ad 100644 --- a/src/GradedArrays.jl +++ b/src/GradedArrays.jl @@ -21,16 +21,20 @@ include("sector_product.jl") include("fusion.jl") include("gradedarray.jl") -export U1, +export SU2, + U1, Z, - blocklabels, dag, dual, flip, gradedrange, isdual, + sector, + sector_multiplicities, + sector_multiplicity, sectorrange, + sectors, sector_type, - space_isequal - + space_isequal, + ungrade end diff --git a/src/sector_definitions/trivial.jl b/src/sector_definitions/trivial.jl index a711ed2..7e8b968 100644 --- a/src/sector_definitions/trivial.jl +++ b/src/sector_definitions/trivial.jl @@ -19,10 +19,6 @@ end function fusion_rule(::NotAbelianStyle, c::AbstractSector, ::TrivialSector) return to_gradedrange(c) end -# Fix ambiguity error. -function fusion_rule(::NotAbelianStyle, c::TrivialSector, ::TrivialSector) - return to_gradedrange(TrivialSector()) -end # abelian case: return Sector fusion_rule(::AbelianStyle, c::AbstractSector, ::TrivialSector) = c diff --git a/test/test_exports.jl b/test/test_exports.jl index b0bcc6c..d0777a0 100644 --- a/test/test_exports.jl +++ b/test/test_exports.jl @@ -4,17 +4,22 @@ using Test: @test, @testset @testset "Test exports" begin exports = [ :GradedArrays, + :SU2, :U1, :Z, - :blocklabels, :dag, :dual, :flip, :gradedrange, :isdual, + :sector, + :sector_multiplicities, + :sector_multiplicity, :sectorrange, + :sectors, :sector_type, :space_isequal, + :ungrade, ] @test issetequal(names(GradedArrays), exports) end diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index fa840e6..2e4e94a 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -77,6 +77,8 @@ using Test: @test, @test_throws, @testset @test step(g) == 1 @test blocklength(g) == 3 @test length(blocks(g)) == 3 + @test isnothing(show(devnull, MIME("text/plain"), g)) + @test isnothing(show(devnull, g)) @test g[Block(1)] isa SectorUnitRange @test !(g[Block(1)] isa SectorOneTo) @@ -262,6 +264,8 @@ end @test space_isequal(dual(g), gradedrange([SU((0, 0)) => 2, SU((1, 0)) => 2]; isdual=true)) @test !space_isequal(dual(g), g) @test space_isequal(flip(g), gradedrange([SU((0, 0)) => 2, SU((1, 1)) => 2]; isdual=true)) + @test isnothing(show(devnull, MIME("text/plain"), g)) + @test isnothing(show(devnull, g)) @test iterate(g) == (1, 1) for i in 1:7 diff --git a/test/test_sectorunitrange.jl b/test/test_sectorunitrange.jl index ab51a6e..5338f12 100644 --- a/test/test_sectorunitrange.jl +++ b/test/test_sectorunitrange.jl @@ -51,6 +51,12 @@ using GradedArrays: @test eachindex(sr) == Base.oneto(6) @test only(axes(sr)) isa Base.OneTo @test only(axes(sr)) == 1:6 + @test iterate(sr) == (1, 1) + for i in 1:5 + @test iterate(sr, i) == (i + 1, i + 1) + end + @test isnothing(iterate(sr, 6)) + @test isnothing(show(devnull, MIME("text/plain"), sr)) @test sr == 1:6 @test sr == sr diff --git a/test/test_symmetrysectors_simple_sectors.jl b/test/test_symmetrysectors_simple_sectors.jl index adf6344..308fec7 100644 --- a/test/test_symmetrysectors_simple_sectors.jl +++ b/test/test_symmetrysectors_simple_sectors.jl @@ -57,6 +57,7 @@ using TestExtras: @constinferred @test U1(-1) < TrivialSector() @test TrivialSector() < U1(1) @test U1(Int8(1)) < U1(Int32(2)) + @test isnothing(show(devnull, q1)) end @testset "Z₂" begin @@ -127,6 +128,8 @@ using TestExtras: @constinferred @test j2 == SU2(1 / 2) # Float will be cast to HalfInteger @test_throws MethodError SU2((1,)) # avoid confusion between tuple and half-integer interfaces @test_throws MethodError SU{2,1}(1) # avoid confusion + @test isnothing(show(devnull, j1)) + @test isnothing(show(devnull, MIME("text/plain"), j1)) @test trivial(SU{2}) == SU2(0) @test istrivial(SU2(0)) @@ -161,6 +164,8 @@ using TestExtras: @constinferred @test istrivial(SU{4}((0, 0, 0))) @test SU{3}((0, 0)) == TrivialSector() @test SU{4}((0, 0, 0)) == TrivialSector() + @test isnothing(show(devnull, f3)) + @test isnothing(show(devnull, MIME("text/plain"), f3)) @test fundamental(SU{3}) == f3 @test fundamental(SU{4}) == f4 diff --git a/test/test_tensor_product.jl b/test/test_tensor_product.jl index 98e4dd9..3ac3fe4 100644 --- a/test/test_tensor_product.jl +++ b/test/test_tensor_product.jl @@ -10,6 +10,7 @@ using GradedArrays: flip, gradedrange, isdual, + sectormergesort, sectorrange, sectors, space_isequal, @@ -24,6 +25,8 @@ GradedArrays.tensor_product(s1::String, s2::String) = gradedrange([s1 * s2 => 1] @testset "unmerged_tensor_product" begin @test unmerged_tensor_product() isa OneToOne @test unmerged_tensor_product(OneToOne(), OneToOne()) isa OneToOne + @test unmerged_tensor_product(1:1, 1:1) == 1:1 + @test sectormergesort(1:1) isa UnitRange a = gradedrange(["x" => 2, "y" => 3]) @test space_isequal(unmerged_tensor_product(a), a) From 7d0b2b355e1c73eece101805830f82dca731150d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 25 Apr 2025 12:50:34 -0400 Subject: [PATCH 30/33] more tests --- test/test_symmetrysectors_sector_product.jl | 3 +++ test/test_symmetrysectors_simple_sectors.jl | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index b6b0ada..df1cb90 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -199,6 +199,9 @@ end g = gradedrange([(Nf=U1(0),) => 2, (Nf=U1(1),) => 3]) @test sector_type(g) <: SectorProduct + + @test (A=U1(1),) × ((B=SU2(2),) × (C=U1(1),)) isa + typeof((A=U1(1),) × (B=SU2(2),) × (C=U1(1),)) end @testset "Construct from Pairs" begin diff --git a/test/test_symmetrysectors_simple_sectors.jl b/test/test_symmetrysectors_simple_sectors.jl index 308fec7..6261a78 100644 --- a/test/test_symmetrysectors_simple_sectors.jl +++ b/test/test_symmetrysectors_simple_sectors.jl @@ -11,6 +11,7 @@ using GradedArrays: quantum_dimension, fundamental, istrivial, + modulus, sector_type, trivial using Test: @test, @testset, @test_throws @@ -73,6 +74,7 @@ using TestExtras: @constinferred @test dual(z0) == z0 @test dual(z1) == z1 + @test modulus(z1) == 2 @test dual(Z{2}(1)) == Z{2}(1) @test isless(Z{2}(0), Z{2}(1)) @@ -96,6 +98,7 @@ using TestExtras: @constinferred @test trivial(O2) == s0e @test istrivial(s0e) + @test isnothing(show(devnull, s0e)) @test (@constinferred quantum_dimension(s0e)) == 1 @test (@constinferred quantum_dimension(s0o)) == 1 @@ -165,7 +168,8 @@ using TestExtras: @constinferred @test SU{3}((0, 0)) == TrivialSector() @test SU{4}((0, 0, 0)) == TrivialSector() @test isnothing(show(devnull, f3)) - @test isnothing(show(devnull, MIME("text/plain"), f3)) + @test isnothing(show(devnull, MIME("text/plain"), SU((1, 1)))) + @test isnothing(show(devnull, MIME("text/plain"), SU((0, 0)))) @test fundamental(SU{3}) == f3 @test fundamental(SU{4}) == f4 @@ -201,6 +205,7 @@ using TestExtras: @constinferred @test (@constinferred quantum_dimension(τ)) == ((1 + √5) / 2) @test ı < τ + @test isnothing(show(devnull, (ı, τ))) end @testset "Ising" begin @@ -221,5 +226,6 @@ using TestExtras: @constinferred @test (@constinferred quantum_dimension(ψ)) == 1.0 @test ı < σ < ψ + @test isnothing(show(devnull, (ı, σ, ψ))) end end From 7081f8f860e9e769f24238d86cd403a02f493bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 25 Apr 2025 15:52:38 -0400 Subject: [PATCH 31/33] clean imports --- src/abstractsector.jl | 1 - src/fusion.jl | 2 +- src/gradedarray.jl | 2 -- src/gradedunitrange.jl | 7 +------ src/gradedunitrange_interface.jl | 2 +- src/sector_product.jl | 2 -- src/sectorunitrange.jl | 4 ---- test/test_gradedarray.jl | 10 ++-------- test/test_gradedunitrange.jl | 3 --- test/test_symmetrysectors_fusion_rules.jl | 1 - test/test_symmetrysectors_sector_product.jl | 1 - test/test_tensor_product.jl | 1 - test/test_tensoralgebraext.jl | 5 ++--- 13 files changed, 7 insertions(+), 34 deletions(-) diff --git a/src/abstractsector.jl b/src/abstractsector.jl index 2ac4a73..e8edc38 100644 --- a/src/abstractsector.jl +++ b/src/abstractsector.jl @@ -1,7 +1,6 @@ # This file defines the abstract type AbstractSector # all fusion categories (Z{2}, SU2, Ising...) are subtypes of AbstractSector -using BlockArrays: blocklengths using TensorProducts: TensorProducts, ⊗ abstract type AbstractSector end diff --git a/src/fusion.jl b/src/fusion.jl index d58380a..967e189 100644 --- a/src/fusion.jl +++ b/src/fusion.jl @@ -1,4 +1,4 @@ -using BlockArrays: Block, blocklengths, blocks +using BlockArrays: Block, blocks using SplitApplyCombine: groupcount using TensorProducts: TensorProducts, ⊗, OneToOne, tensor_product diff --git a/src/gradedarray.jl b/src/gradedarray.jl index 8e3d898..04a8a34 100644 --- a/src/gradedarray.jl +++ b/src/gradedarray.jl @@ -4,8 +4,6 @@ using BlockSparseArrays: AbstractBlockSparseMatrix, AnyAbstractBlockSparseArray, BlockSparseArray, - BlockSparseMatrix, - BlockSparseVector, blocktype, sparsemortar using LinearAlgebra: Adjoint diff --git a/src/gradedunitrange.jl b/src/gradedunitrange.jl index c781ac8..64aa589 100644 --- a/src/gradedunitrange.jl +++ b/src/gradedunitrange.jl @@ -15,15 +15,10 @@ using BlockArrays: blocklasts, blocklengths, blocks, - blockindex, combine_blockaxes, findblock, mortar -using BlockSparseArrays: - BlockSparseArrays, - blockedunitrange_findblock, - blockedunitrange_findblockindex, - blockedunitrange_getindices +using BlockSparseArrays: BlockSparseArrays, blockedunitrange_getindices using Compat: allequal # ==================================== Definitions ======================================= diff --git a/src/gradedunitrange_interface.jl b/src/gradedunitrange_interface.jl index ab1e056..7ea7308 100644 --- a/src/gradedunitrange_interface.jl +++ b/src/gradedunitrange_interface.jl @@ -1,4 +1,4 @@ -using BlockArrays: AbstractBlockVector, BlockRange, blockisequal, blocklength +using BlockArrays: AbstractBlockVector, blockisequal, blocklength using FillArrays: Fill """ diff --git a/src/sector_product.jl b/src/sector_product.jl index 54ecb34..88878f7 100644 --- a/src/sector_product.jl +++ b/src/sector_product.jl @@ -1,8 +1,6 @@ # This files defines a structure for Cartesian product of 2 or more fusion sectors # e.g. U(1)×U(1), U(1)×SU2(2)×SU(3) -using BlockArrays: blocklengths - # ===================================== Definition ======================================= struct SectorProduct{Sectors} <: AbstractSector arguments::Sectors diff --git a/src/sectorunitrange.jl b/src/sectorunitrange.jl index 8de435e..3a76d5d 100644 --- a/src/sectorunitrange.jl +++ b/src/sectorunitrange.jl @@ -1,9 +1,5 @@ # This files defines SectorUnitRange, a unit range associated with a sector and an arrow -using BlockArrays: BlockArrays - -using TensorProducts: tensor_product - # ===================================== Definition ======================================= # This implementation contains the "full range" diff --git a/test/test_gradedarray.jl b/test/test_gradedarray.jl index 34708fc..ae14ddd 100644 --- a/test/test_gradedarray.jl +++ b/test/test_gradedarray.jl @@ -1,11 +1,5 @@ using BlockArrays: - AbstractBlockArray, - Block, - BlockedOneTo, - BlockedUnitRange, - blockedrange, - blocklengths, - blocksize + Block, BlockedOneTo, BlockedUnitRange, blockedrange, blocklengths, blocksize using BlockSparseArrays: BlockSparseArray, BlockSparseMatrix, BlockSparseVector, blockstoredlength using GradedArrays: @@ -25,7 +19,7 @@ using GradedArrays: using SparseArraysBase: storedlength using LinearAlgebra: adjoint using Random: randn! -using Test: @test, @test_broken, @testset +using Test: @test, @testset function randn_blockdiagonal(elt::Type, axes::Tuple) a = BlockSparseArray{elt}(undef, axes) diff --git a/test/test_gradedunitrange.jl b/test/test_gradedunitrange.jl index 2e4e94a..6261d1e 100644 --- a/test/test_gradedunitrange.jl +++ b/test/test_gradedunitrange.jl @@ -4,7 +4,6 @@ using BlockArrays: BlockedOneTo, BlockVector, BlockedUnitRange, - BlockedVector, blockedrange, blockfirsts, blockisequal, @@ -17,7 +16,6 @@ using BlockArrays: findblockindex, mortar using GradedArrays: - GradedArrays, GradedOneTo, GradedUnitRange, SectorOneTo, @@ -28,7 +26,6 @@ using GradedArrays: gradedrange, isdual, mortar_axis, - sector, sector_multiplicities, sector_type, sectors, diff --git a/test/test_symmetrysectors_fusion_rules.jl b/test/test_symmetrysectors_fusion_rules.jl index c861b66..67a3cff 100644 --- a/test/test_symmetrysectors_fusion_rules.jl +++ b/test/test_symmetrysectors_fusion_rules.jl @@ -5,7 +5,6 @@ using GradedArrays: TrivialSector, U1, Z, - blocklengths, dual, flip, gradedrange, diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index df1cb90..fd0d27e 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -1,7 +1,6 @@ using GradedArrays: ×, SectorProduct, - SU, SU2, TrivialSector, U1, diff --git a/test/test_tensor_product.jl b/test/test_tensor_product.jl index 3ac3fe4..99defd9 100644 --- a/test/test_tensor_product.jl +++ b/test/test_tensor_product.jl @@ -7,7 +7,6 @@ using GradedArrays: SU2, U1, dual, - flip, gradedrange, isdual, sectormergesort, diff --git a/test/test_tensoralgebraext.jl b/test/test_tensoralgebraext.jl index 1ac8b4b..c766d2d 100644 --- a/test/test_tensoralgebraext.jl +++ b/test/test_tensoralgebraext.jl @@ -1,10 +1,9 @@ using BlockArrays: Block, blocksize using BlockSparseArrays: BlockSparseArray -using GradedArrays: - GradedArray, GradedMatrix, GradedOneTo, U1, dual, flip, gradedrange, space_isequal +using GradedArrays: GradedArray, GradedMatrix, U1, dual, flip, gradedrange, space_isequal using Random: randn! using TensorAlgebra: contract, matricize, unmatricize -using Test: @test, @test_broken, @testset +using Test: @test, @testset function randn_blockdiagonal(elt::Type, axes::Tuple) a = BlockSparseArray{elt}(undef, axes) From d87620227aa95439aacf43b83054ad88a7c903d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Fri, 25 Apr 2025 16:48:00 -0400 Subject: [PATCH 32/33] =?UTF-8?q?fix=20=C3=97(SectorUnitRange)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sector_product.jl | 8 ++------ test/test_symmetrysectors_sector_product.jl | 9 +++++++++ test/test_symmetrysectors_simple_sectors.jl | 14 +++++++++++++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/sector_product.jl b/src/sector_product.jl index 88878f7..1757e8c 100644 --- a/src/sector_product.jl +++ b/src/sector_product.jl @@ -99,10 +99,10 @@ end ×(c1::AbstractSector, c2::NamedTuple) = ×(SectorProduct(c1), SectorProduct(c2)) function ×(sr1::SectorUnitRange, sr2::SectorUnitRange) - @assert isdual(sr1) == isdual(sr2) + isdual(sr1) == isdual(sr2) || throw(ArgumentError("SectorProduct duality must match")) return sectorrange( sector(sr1) × sector(sr2), - sector_multiplicity(sr1) * sector_multiplicity(sr1), + sector_multiplicity(sr1) * sector_multiplicity(sr2), isdual(sr1), ) end @@ -120,10 +120,6 @@ end function fusion_rule(style::SymmetryStyle, c1::AbstractSector, c2::SectorProduct) return fusion_rule(style, SectorProduct(c1), c2) end -# Fix ambiguity error. -function fusion_rule(style::SymmetryStyle, c1::SectorProduct, c2::SectorProduct) - return throw(ArgumentError("SectorProduct fusion not defined for symmetry style $style.")) -end # generic case: fusion returns a GradedUnitRanges, even for fusion with Empty function fusion_rule(::NotAbelianStyle, s1::SectorProduct, s2::SectorProduct) diff --git a/test/test_symmetrysectors_sector_product.jl b/test/test_symmetrysectors_sector_product.jl index fd0d27e..c749ba5 100644 --- a/test/test_symmetrysectors_sector_product.jl +++ b/test/test_symmetrysectors_sector_product.jl @@ -49,6 +49,7 @@ using BlockArrays: blocklengths @test dual(s) == TrivialSector() × U1(-3) × SU2(1//2) @test (@constinferred trivial(s)) == SectorProduct(TrivialSector(), U1(0), SU2(0)) @test s > trivial(s) + @test isnothing(show(devnull, s)) end @testset "Ordered comparisons" begin @@ -83,6 +84,13 @@ using BlockArrays: blocklengths @test (@constinferred quantum_dimension(g)) == 16 @test (@constinferred blocklengths(g)) == [1, 3, 3, 9] + @test space_isequal( + gradedrange([U1(1) => 2]) × SU2(1), gradedrange([U1(1) × SU2(1) => 2]) + ) + @test space_isequal( + SU2(1) × gradedrange([U1(1) => 2]), gradedrange([SU2(1) × U1(1) => 2]) + ) + # mixed group g = gradedrange([(U1(2) × SU2(0) × Z{2}(0)) => 1, (U1(2) × SU2(1) × Z{2}(0)) => 1]) @test (@constinferred quantum_dimension(g)) == 4 @@ -191,6 +199,7 @@ end @test (@constinferred dual(s)) == (A=U1(-1),) × (B=SU2(2),) @test (@constinferred trivial(s)) == (A=U1(0),) × (B=SU2(0),) @test s == (B=SU2(2),) × (A=U1(1),) + @test isnothing(show(devnull, s)) s1 = (A=U1(1),) × (B=Z{2}(0),) s2 = (A=U1(1),) × (C=Z{2}(0),) diff --git a/test/test_symmetrysectors_simple_sectors.jl b/test/test_symmetrysectors_simple_sectors.jl index 6261a78..caa67dd 100644 --- a/test/test_symmetrysectors_simple_sectors.jl +++ b/test/test_symmetrysectors_simple_sectors.jl @@ -11,8 +11,10 @@ using GradedArrays: quantum_dimension, fundamental, istrivial, + level, modulus, sector_type, + su2, trivial using Test: @test, @testset, @test_throws using TestExtras: @constinferred @@ -98,7 +100,7 @@ using TestExtras: @constinferred @test trivial(O2) == s0e @test istrivial(s0e) - @test isnothing(show(devnull, s0e)) + @test isnothing(show(devnull, [s0o, s0e, s12])) @test (@constinferred quantum_dimension(s0e)) == 1 @test (@constinferred quantum_dimension(s0o)) == 1 @@ -228,4 +230,14 @@ using TestExtras: @constinferred @test ı < σ < ψ @test isnothing(show(devnull, (ı, σ, ψ))) end + + @testset "su2{k}" begin + s1 = su2{1}(0) + s2 = su2{2}(1) + + @test s1 isa su2{1} + @test trivial(s2) == su2{2}(0) + @test dual(s1) == s1 + @test level(s1) == 1 + end end From 2663089e655a1dc6f6c180b1eb4408d9c7867aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Mon, 28 Apr 2025 10:01:31 -0400 Subject: [PATCH 33/33] =?UTF-8?q?restrict=20=C3=97=20to=20OneTo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sector_product.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sector_product.jl b/src/sector_product.jl index 1757e8c..dc6a3f2 100644 --- a/src/sector_product.jl +++ b/src/sector_product.jl @@ -98,7 +98,7 @@ end ×(c1::NamedTuple, c2::AbstractSector) = ×(SectorProduct(c1), SectorProduct(c2)) ×(c1::AbstractSector, c2::NamedTuple) = ×(SectorProduct(c1), SectorProduct(c2)) -function ×(sr1::SectorUnitRange, sr2::SectorUnitRange) +function ×(sr1::SectorOneTo, sr2::SectorOneTo) isdual(sr1) == isdual(sr2) || throw(ArgumentError("SectorProduct duality must match")) return sectorrange( sector(sr1) × sector(sr2), @@ -107,8 +107,10 @@ function ×(sr1::SectorUnitRange, sr2::SectorUnitRange) ) end -function ×(g1::AbstractGradedUnitRange, g2::AbstractGradedUnitRange) - v = map(splat(×), Iterators.flatten((Iterators.product(blocks(g1), blocks(g2)),),)) +function ×(g1::GradedOneTo, g2::GradedOneTo) + v = map( + splat(×), Iterators.flatten((Iterators.product(eachblockaxis(g1), eachblockaxis(g2)),),) + ) return mortar_axis(v) end