Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b30f205
specialise dim to storage type
borisdevos Aug 11, 2026
41f43fd
oplus and ominus
borisdevos Aug 11, 2026
619804d
infimum and supremum
borisdevos Aug 11, 2026
0967bd0
fuse
borisdevos Aug 11, 2026
60186d5
truncate_space
borisdevos Aug 11, 2026
8f90be2
restore binary search for sectordicts
borisdevos Aug 11, 2026
ae10f5b
refactor sorted merge procedure
borisdevos Aug 11, 2026
47a5f34
speed up fuse slightly with sortperm
borisdevos Aug 11, 2026
411dc44
import thing
borisdevos Aug 11, 2026
0adec3b
splat with type annotation above Val
borisdevos Aug 12, 2026
13677f3
actually don't splat, but construct directly where previously a vecto…
borisdevos Aug 21, 2026
399e121
make slightly more readable maybe perhaps
borisdevos Aug 21, 2026
d2a41f1
truncate_space always has non-dual entry spaces
borisdevos Aug 25, 2026
d7bdb07
overkill iszero check in dim
borisdevos Aug 25, 2026
271e96b
assert truncate_space spaces being non-dual
borisdevos Aug 26, 2026
335785f
introduce `sectorstoragetype`
borisdevos Aug 28, 2026
d79931e
specialise truncation code to sectorstoragetype
borisdevos Aug 28, 2026
d42aa28
put `_sortedmerge` in `Base.mergewith` and use where possible
borisdevos Aug 28, 2026
f8b0288
introduce and use `blockdims`
borisdevos Sep 1, 2026
2fbf6fe
introduce the ntuple cutoff
borisdevos Sep 2, 2026
2a3da90
Merge branch 'main' of https://github.com/QuantumKitHub/TensorKit.jl …
borisdevos Sep 2, 2026
e5877dc
apply code suggestions
borisdevos Sep 4, 2026
820d222
slight refactor of sortmerge implementation
lkdvos Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/TensorKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic
export sectortype, sectors, hassector
export unit, rightunit, leftunit, allunits, isunit, otimes, deligneproduct, timereversed
export Nsymbol, Fsymbol, Rsymbol, Bsymbol, frobenius_schur_phase, frobenius_schur_indicator, twist, fusiontensor
export sectorscalartype, fusionscalartype, braidingscalartype
export sectorscalartype, fusionscalartype, braidingscalartype, dimscalartype

# Export methods for fusion trees
export fusiontrees, braid, permute, transpose
Expand Down
74 changes: 59 additions & 15 deletions src/auxiliary/dicts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,12 @@ end
Base.empty(::SortedVectorDict, ::Type{K}, ::Type{V}) where {K, V} = SortedVectorDict{K, V}()
Base.empty!(d::SortedVectorDict) = (empty!(d.keys); empty!(d.values); return d)

# _searchsortedfirst(v::Vector, k) = searchsortedfirst(v, k)
function _searchsortedfirst(v::Vector, k)
i = 1
@inbounds while i <= length(v) && isless(v[i], k)
i += 1
end
return i
end

function Base.delete!(d::SortedVectorDict{K}, k) where {K}
key = convert(K, k)
if !isequal(k, key)
return d
end
i = _searchsortedfirst(d.keys, key)
i = searchsortedfirst(d.keys, key)
if i <= length(d) && isequal(d.keys[i], key)
deleteat!(d.keys, i)
deleteat!(d.values, i)
Expand All @@ -118,15 +109,15 @@ function Base.haskey(d::SortedVectorDict{K}, k) where {K}
if !isequal(k, key)
return false
end
i = _searchsortedfirst(d.keys, key)
i = searchsortedfirst(d.keys, key)
return (i <= length(d) && isequal(d.keys[i], key))
end
function Base.getindex(d::SortedVectorDict{K}, k) where {K}
key = convert(K, k)
if !isequal(k, key)
throw(KeyError(k))
end
i = _searchsortedfirst(d.keys, key)
i = searchsortedfirst(d.keys, key)
@inbounds if (i <= length(d) && isequal(d.keys[i], key))
return d.values[i]
else
Expand All @@ -138,7 +129,7 @@ function Base.setindex!(d::SortedVectorDict{K}, v, k) where {K}
if !isequal(k, key)
throw(ArgumentError("$k is not a valid key for type $K"))
end
i = _searchsortedfirst(d.keys, key)
i = searchsortedfirst(d.keys, key)
if i <= length(d) && isequal(d.keys[i], key)
d.values[i] = v
else
Expand All @@ -153,7 +144,7 @@ function Base.get(d::SortedVectorDict{K}, k, default) where {K}
if !isequal(k, key)
return default
end
i = _searchsortedfirst(d.keys, key)
i = searchsortedfirst(d.keys, key)
@inbounds begin
return (i <= length(d) && isequal(d.keys[i], key)) ? d.values[i] : default
end
Expand All @@ -163,7 +154,7 @@ function Base.get(f::Union{Function, Type}, d::SortedVectorDict{K}, k) where {K}
if !isequal(k, key)
return f()
end
i = _searchsortedfirst(d.keys, key)
i = searchsortedfirst(d.keys, key)
@inbounds begin
return (i <= length(d) && isequal(d.keys[i], key)) ? d.values[i] : f()
end
Expand All @@ -186,6 +177,59 @@ function Base.:(==)(d1::SortedVectorDict, d2::SortedVectorDict)
return true
end

# merge two SortedVectorDicts of `GradedSpace` dimensions, applying `combine` to keys present in
# both; keys present in only one dict are kept as is or dropped according to `_keepunmatched(combine)`
# zero results are dropped since `GradedSpace` never stores an explicit zero dimension
_keepunmatched(::Any) = true
_keepunmatched(::typeof(min)) = false # infimum: a missing sector has dimension zero, so min drops it

function _sortedmerge(combine::F, d1::SortedVectorDict{K, V}, d2::SortedVectorDict{K, V}) where {F, K, V <: Integer}
keep = _keepunmatched(combine)
k1, v1 = d1.keys, d1.values
k2, v2 = d2.keys, d2.values
n1, n2 = length(k1), length(k2)
len = keep ? n1 + n2 : min(n1, n2)
ks = Vector{K}(undef, len)
vs = Vector{V}(undef, len)
i, j, n = 1, 1, 0
@inbounds while i <= n1 && j <= n2
a, b = k1[i], k2[j]
if isless(a, b)
keep && (n = _mergestore!(ks, vs, n, a, v1[i]))
i += 1
elseif isless(b, a)
keep && (n = _mergestore!(ks, vs, n, b, v2[j]))
j += 1
else
n = _mergestore!(ks, vs, n, a, combine(v1[i], v2[j]))
i += 1
j += 1
end
end
if keep
@inbounds while i <= n1
n = _mergestore!(ks, vs, n, k1[i], v1[i])
i += 1
end
@inbounds while j <= n2
n = _mergestore!(ks, vs, n, k2[j], v2[j])
j += 1
end
end
resize!(ks, n)
resize!(vs, n)
return SortedVectorDict{K, V}(ks, vs)
end
# write into slot `n + 1` and only advance the length when the value is nonzero
@inline function _mergestore!(ks, vs, n, k, d)
@inbounds ks[n + 1] = k
@inbounds vs[n + 1] = d
return n + !iszero(d)
end

Base.mergewith(combine, d1::SortedVectorDict{K, V}, d2::SortedVectorDict{K, V}) where {K, V <: Integer} =
_sortedmerge(combine, d1, d2)

"""
Hashed(value, hashfunction = Base.hash, isequal = Base.isequal)

Expand Down
4 changes: 2 additions & 2 deletions src/factorizations/factorizations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ module Factorizations
export copy_oftype, factorisation_scalartype, one!, truncspace

using ..TensorKit
using ..TensorKit: AdjointTensorMap, SectorDict, SectorVector,
using ..TensorKit: AdjointTensorMap, SectorDict, SectorVector, findindex,
blocktype, foreachblock, one!,
similar_diagonal, similarstoragetype
similar_diagonal, similarstoragetype, sectorstoragetype

using LinearAlgebra: LinearAlgebra, BlasFloat, Diagonal,
svdvals, svdvals!, eigen, eigen!,
Expand Down
12 changes: 9 additions & 3 deletions src/factorizations/pullbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,22 @@ for pullback! in (:qr_null_pullback!, :lq_null_pullback!)
return Δt
end
end
_notrunc_ind(t) = SectorDict(c => Colon() for c in blocksectors(t))
function _notrunc_ind(t)
I = sectortype(t)
return _builddensemap(sectorstoragetype(I), I, blocks(t), Colon) do _, _
Colon()
end
end

for pullback! in (:svd_pullback!, :eig_pullback!, :eigh_pullback!)
@eval function MAK.$pullback!(
Δt::AbstractTensorMap, t::AbstractTensorMap, F, ΔF, inds = _notrunc_ind(t);
kwargs...
)
Isec = sectortype(t)
foreachblock(Δt, t) do c, (Δb, b)
haskey(inds, c) || return nothing
ind = inds[c]
ind = _denseget(inds, Isec, c)
isnothing(ind) && return nothing
Fc = block.(F, Ref(c))
ΔFc = block.(ΔF, Ref(c))
MAK.$pullback!(Δb, b, Fc, ΔFc, ind; kwargs...)
Expand Down
Loading
Loading