Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fb4693f
add todo for function split
borisdevos Mar 17, 2025
13bcf82
add comment for possible change in function merge
borisdevos Mar 17, 2025
09467c6
change one to leftone in bendright
borisdevos Mar 17, 2025
2878e08
change one to leftone in foldright
borisdevos Mar 17, 2025
10744b2
change one to f.coupled in elementary_trace
borisdevos Mar 17, 2025
1ed1ed1
progress on correct units in trace
borisdevos Mar 25, 2025
c31b99c
change dim of GradedSpace to not evaluate one
borisdevos Mar 25, 2025
a33d028
use a function that's actually exported in dimension of `GradedSpace`
borisdevos Mar 26, 2025
0e3c5df
update TensorOperations scalartype determination
lkdvos Apr 10, 2025
124def4
Merge pull request #1 from lkdvos/fork
borisdevos Apr 11, 2025
9635362
Revert "use a function that's actually exported in dimension of `Grad…
borisdevos Apr 14, 2025
4bc1e7c
Revert "change dim of GradedSpace to not evaluate one"
borisdevos Apr 14, 2025
24be2ea
irrelevant typos, but must be corrected
borisdevos Apr 16, 2025
13af986
another minor typo
borisdevos Apr 16, 2025
095dcda
minor typos
borisdevos May 6, 2025
fbfb564
then vs than is hard
borisdevos May 6, 2025
d7cae4f
remove debug elements
borisdevos May 7, 2025
a14fc70
Merge branch 'master' of https://github.com/Jutho/TensorKit.jl into b…
borisdevos May 7, 2025
4fe82f9
change TensorOperations scalartype promotion to base off field of num…
borisdevos May 8, 2025
55a00c0
typo in docs
borisdevos Jun 13, 2025
0113f73
remove some debug comments
borisdevos Jun 13, 2025
8e8b760
more rigorous check in `merge`
borisdevos Jun 18, 2025
78b135e
replace `one` evaluation in `split` with `leftone`
borisdevos Jun 18, 2025
440b42b
typos in docs
borisdevos Jun 18, 2025
00825af
typo in `_fusiontree_iterate`
borisdevos Jun 18, 2025
aad8fc8
apply suggestions related to tensor types
borisdevos Jul 4, 2025
c0ef01e
remove excess variable
borisdevos Jul 4, 2025
c189284
apply `split` suggestion
borisdevos Jul 4, 2025
bdbd2e6
format
borisdevos Jul 7, 2025
e23e092
change units in `elementary_trace`
borisdevos Jul 15, 2025
8ec577e
correct `eltype` for `BlockIterator`
lkdvos Jul 16, 2025
d915273
Remove `isdone`
lkdvos Jul 16, 2025
301780a
Adapt tests to `eltype(blocks)`
lkdvos Jul 16, 2025
f9cede3
More test fixes
lkdvos Jul 16, 2025
baf1824
even more test fixes
borisdevos Jul 17, 2025
9d543ee
format
borisdevos Jul 17, 2025
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 docs/src/man/categories.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ morphism from ``I`` to ``V``. To map morphisms from ``\mathrm{Hom}(W,V)`` to ele
``V ⊗ W^*``, i.e. morphisms in ``\mathrm{Hom}(I, V ⊗ W^*)``, we use another morphism
``\mathrm{Hom}(I, W ⊗ W^*)`` which can be considered as the inverse of the evaluation map.

Hence, duality in a monoidal category is defined via an *exact paring*, i.e. two families
Hence, duality in a monoidal category is defined via an *exact pairing*, i.e. two families
of non-degenerate morphisms, the evaluation (or co-unit) ``ϵ_V: {}^{∨}V ⊗ V → I`` and the
coevaluation (or unit) ``η_V: I → V ⊗ {}^{∨}V`` which satisfy the "snake rules":

Expand Down
6 changes: 3 additions & 3 deletions docs/src/man/sectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ groups.

Other methods for `ElementarySpace`, such as [`dual`](@ref), [`fuse`](@ref) and
[`flip`](@ref) also work. In fact, `GradedSpace` is the reason `flip` exists, cause
in this case it is different then `dual`. The existence of flip originates from the
in this case it is different than `dual`. The existence of flip originates from the
non-trivial isomorphism between ``R_{\overline{a}}`` and ``R_{a}^*``, i.e. the
representation space of the dual ``\overline{a}`` of sector ``a`` and the dual of the
representation space of sector ``a``. In order for `flip(V)` to be isomorphic to `V`, it is
Expand Down Expand Up @@ -894,7 +894,7 @@ for the specific case ``N_1=4`` and ``N_2=3``. We can separate this tree into th
part ``(b_1⊗b_2)⊗b_3 → c`` and the splitting part ``c→(((a_1⊗a_2)⊗a_3)⊗a_4)``. Given that
the fusion tree can be considered to be the adjoint of a corresponding splitting tree
``c→(b_1⊗b_2)⊗b_3``, we now first consider splitting trees in isolation. A splitting tree
which goes from one coupled sectors ``c`` to ``N`` uncoupled sectors ``a_1``, ``a_2``, …,
which goes from one coupled sector ``c`` to ``N`` uncoupled sectors ``a_1``, ``a_2``, …,
``a_N`` needs ``N-2`` additional internal sector labels ``e_1``, …, ``e_{N-2}``, and, if
`FusionStyle(I) isa GenericFusion`, ``N-1`` additional multiplicity labels ``μ_1``,
…, ``μ_{N-1}``. We henceforth refer to them as vertex labels, as they are associated with
Expand All @@ -908,7 +908,7 @@ the orthogonality condition
which now forces all internal lines ``e_k`` and vertex labels ``μ_l`` to be the same.

There is one subtle remark that we have so far ignored. Within the specific subtypes of
`Sector`, we do not explicitly distinguish between ``R_a^*`` (simply denoted as ``a`^*``
`Sector`, we do not explicitly distinguish between ``R_a^*`` (simply denoted as ``a^*``
and graphically depicted as an upgoing arrow ``a``) and ``R_{\bar{a}}`` (simply denoted as
``\bar{a}`` and depicted with a downgoing arrow), i.e. between the dual space of ``R_a`` on
which the conjugated irrep acts, or the irrep ``\bar{a}`` to which the complex conjugate of
Expand Down
4 changes: 2 additions & 2 deletions docs/src/man/spaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ corresponding spaces, but in general none of those will be canonical.
There are also a number of convenience functions to create isomorphic spaces. The function
`fuse(V1, V2, ...)` or `fuse(V1 ⊗ V2 ⊗ ...)` returns an elementary space that is isomorphic
to `V1 ⊗ V2 ⊗ ...`. The function `flip(V::ElementarySpace)` returns a space that is
isomorphic to `V` but has `isdual(flip(V)) == isdual(V')`, i.e. if `V` is a normal space
than `flip(V)` is a dual space. `flip(V)` is different from `dual(V)` in the case of
isomorphic to `V` but has `isdual(flip(V)) == isdual(V')`, i.e., if `V` is a normal space,
then `flip(V)` is a dual space. `flip(V)` is different from `dual(V)` in the case of
[`GradedSpace`](@ref). It is useful to flip a tensor index from a ket to a bra (or
vice versa), by contracting that index with a unitary map from `V1` to `flip(V1)`. We refer
to the reference on [vector space methods](@ref s_spacemethods) for further information.
Expand Down
2 changes: 1 addition & 1 deletion src/fusiontrees/iterator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
nextout = iterate(outiterN, outstateN)
nextout === nothing && return nothing
b, outstateN = nextout
vertexiterN = c dual(b)
vertexiterN = coupled dual(b)

Check warning on line 160 in src/fusiontrees/iterator.jl

View check run for this annotation

Codecov / codecov/patch

src/fusiontrees/iterator.jl#L160

Added line #L160 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this. Did you encounter this by reading the code or by an actual case where the old implementation errored? Clearly there should have been a test in place to cover those lines of code. So if you have some example code that actually triggers these lines, that might be a good starting point to extract a test case from (which might be easier then engineering one from scratch).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this from an error. However, I cannot find what I was doing to trigger this particular iteration... I will keep looking

nextline = iterate(vertexiterN)
end
a, vertexstateN = nextline
Expand Down
30 changes: 17 additions & 13 deletions src/fusiontrees/manipulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,9 @@
f₂ = FusionTree{I}(f.uncoupled, f.coupled, isdual2, f.innerlines, f.vertices)
return f₁, f₂
elseif M === 0
f₁ = FusionTree{I}((), one(I), (), ())
uncoupled2 = (one(I), f.uncoupled...)
u = leftone(f.uncoupled[1])
f₁ = FusionTree{I}((), u, (), ())
uncoupled2 = (u, f.uncoupled...)
coupled2 = f.coupled
isdual2 = (false, f.isdual...)
innerlines2 = N >= 2 ? (f.uncoupled[1], f.innerlines...) : ()
Expand Down Expand Up @@ -230,7 +231,7 @@
return insertat(f, N₁ + 1, f₂)
end
function merge(f₁::FusionTree{I,0}, f₂::FusionTree{I,0}, c::I, μ) where {I}
isone(c) ||
Nsymbol(f₁.coupled, f₂.coupled, c) == μ == 1 ||
throw(SectorMismatch("cannot fuse sectors $(f₁.coupled) and $(f₂.coupled) to $c"))
return fusiontreedict(I)(f₁ => Fsymbol(c, c, c, c, c, c)[1, 1, 1, 1])
end
Expand Down Expand Up @@ -289,7 +290,8 @@
# map final splitting vertex (a, b)<-c to fusion vertex a<-(c, dual(b))
@assert N₁ > 0
c = f₁.coupled
a = N₁ == 1 ? one(I) : (N₁ == 2 ? f₁.uncoupled[1] : f₁.innerlines[end])
a = N₁ == 1 ? leftone(f₁.uncoupled[1]) :
(N₁ == 2 ? f₁.uncoupled[1] : f₁.innerlines[end])
b = f₁.uncoupled[N₁]

uncoupled1 = TupleTools.front(f₁.uncoupled)
Expand Down Expand Up @@ -360,7 +362,7 @@
hasmultiplicities = FusionStyle(a) isa GenericFusion
local newtrees
if N₁ == 1
cset = (one(c1),)
cset = (leftone(c1),) # or rightone(a)
elseif N₁ == 2
cset = (f₁.uncoupled[2],)
else
Expand Down Expand Up @@ -729,7 +731,8 @@
# if trace is zero, return empty dict
(b == dual(b′) && f.isdual[i] != f.isdual[j]) || return newtrees
if i < N
inner_extended = (one(I), f.uncoupled[1], f.innerlines..., f.coupled)
inner_extended = (leftone(f.uncoupled[1]), f.uncoupled[1], f.innerlines...,
f.coupled)
a = inner_extended[i]
d = inner_extended[i + 2]
a == d || return newtrees
Expand All @@ -753,11 +756,11 @@
if i > 1
c = f.innerlines[i - 1]
if FusionStyle(I) isa MultiplicityFreeFusion
coeff *= Fsymbol(a, b, dual(b), a, c, one(I))
coeff *= Fsymbol(a, b, dual(b), a, c, rightone(a))
else
μ = f.vertices[i - 1]
ν = f.vertices[i]
coeff *= Fsymbol(a, b, dual(b), a, c, one(I))[μ, ν, 1, 1]
coeff *= Fsymbol(a, b, dual(b), a, c, rightone(a))[μ, ν, 1, 1]

Check warning on line 763 in src/fusiontrees/manipulations.jl

View check run for this annotation

Codecov / codecov/patch

src/fusiontrees/manipulations.jl#L763

Added line #L763 was not covered by tests
end
end
if f.isdual[i]
Expand All @@ -767,7 +770,7 @@
return newtrees
else # i == N
if N == 2
f′ = FusionTree{I}((), one(I), (), (), ())
f′ = FusionTree{I}((), f.coupled, (), (), ()) # or leftone(f.uncoupled[1]) == rightone(f.uncoupled[2])
coeff = sqrtdim(b)
if !(f.isdual[N])
coeff *= conj(frobeniusschur(b))
Expand All @@ -778,18 +781,19 @@
uncoupled_ = TupleTools.front(f.uncoupled)
inner_ = TupleTools.front(f.innerlines)
coupled_ = f.innerlines[end]
@assert coupled_ == dual(b)
@assert coupled_ == dual(b) # isn't this always true at this point?
isdual_ = TupleTools.front(f.isdual)
vertices_ = TupleTools.front(f.vertices)
f_ = FusionTree(uncoupled_, coupled_, isdual_, inner_, vertices_)
fs = FusionTree((b,), b, (!f.isdual[1],), (), ())
for (f_′, coeff) in merge(fs, f_, one(I), 1)
f_′.innerlines[1] == one(I) || continue
unit = leftone(fs.coupled)
for (f_′, coeff) in merge(fs, f_, unit, 1) # coloring gets reversed here, should be the other unit
f_′.innerlines[1] == unit || continue # is this one valid?
uncoupled′ = Base.tail(Base.tail(f_′.uncoupled))
isdual′ = Base.tail(Base.tail(f_′.isdual))
inner′ = N <= 4 ? () : Base.tail(Base.tail(f_′.innerlines))
vertices′ = N <= 3 ? () : Base.tail(Base.tail(f_′.vertices))
f′ = FusionTree(uncoupled′, one(I), isdual′, inner′, vertices′)
f′ = FusionTree(uncoupled′, unit, isdual′, inner′, vertices′) # and this one?
coeff *= sqrtdim(b)
if !(f.isdual[N])
coeff *= conj(frobeniusschur(b))
Expand Down
10 changes: 5 additions & 5 deletions src/spaces/gradedspace.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ isomorphism classes of simple objects of a unitary and pivotal (pre-)fusion cate

Here `dims` represents the degeneracy or multiplicity of every sector.

The data structure `D` of `dims` will depend on the result `Base.IteratorElsize(values(I))`;
The data structure `D` of `dims` will depend on the result `Base.IteratorSize(values(I))`;
if the result is of type `HasLength` or `HasShape`, `dims` will be stored in a
`NTuple{N,Int}` with `N = length(values(I))`. This requires that a sector `s::I` can be
transformed into an index via `s == getindex(values(I), i)` and
Expand Down Expand Up @@ -189,16 +189,16 @@ end

function Base.show(io::IO, V::GradedSpace{I}) where {I<:Sector}
print(io, type_repr(typeof(V)), "(")
seperator = ""
separator = ""
comma = ", "
io2 = IOContext(io, :typeinfo => I)
for c in sectors(V)
if isdual(V)
print(io2, seperator, dual(c), "=>", dim(V, c))
print(io2, separator, dual(c), "=>", dim(V, c))
else
print(io2, seperator, c, "=>", dim(V, c))
print(io2, separator, c, "=>", dim(V, c))
end
seperator = comma
separator = comma
end
print(io, ")")
V.dual && print(io, "'")
Expand Down
10 changes: 6 additions & 4 deletions src/tensors/tensoroperations.jl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For these changes, I think it would be nice to have a dedicated discussion (indeed in a separate PR) and some comments explaining what is going on.

The bottom line being that we want the sectorscalartype to mix into the actual scalartype in a very subtle kind of way, only really being used to promote from <:Real to <:Complex while leaving the actual precision the same. (I think?)

The implementation below is somewhat of a hack since normally TensorOperations is responsible for figuring out the destination scalartype TC, so in principle we would expect scalartype(tensoradd_type(TC, ...)) === TC, which is not the case here.

There are a couple options for fixing this:

  1. Since TensorOperations uses scalartype, we could consider redefining scalartype(::AbstractTensorMap) to take the sectorscalartype into account. This would boil down to really having a different meaning between eltype and scalartype for TensorMaps, where the former is the type of the stored data, and the latter also contains information about the field.
  2. We always require complex entries if the sectorscalartype is complex. This doesnt have too many performance implications since most operations on tensors would result in a complex tensor anyways, but we would have to think carefully about DiagonalTensorMap, since that one could have real entries (e.g. for singular value decompositions)
  3. We rework the implementation of promote_contract in TensorOperations and the macros to actually work on values or types thereof instead of directly on scalartypes, such that we can overload promote_contract(::AbstractTensorMap, ::AbstractTensorMap, ::Number) or promote_contract(::Type{<:AbstractTensorMap}, ...). This is quite a big internal change for TensorOperations though, since currently all macros expand to promote_contract(scalartype(A), scalartype(B), ...).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that came up specifically in this PR? This seems like it should already have been an issue with existing complex sectors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that came up specifically in this PR? This seems like it should already have been an issue with existing complex sectors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I don't think we ever used complex sectors with non-complex tensors all that much, and MPSKit actually uses complex entries for almost everything by default.

Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ end

function TO.tensoradd_type(TC, A::AbstractTensorMap, ::Index2Tuple{N₁,N₂},
::Bool) where {N₁,N₂}
M = similarstoragetype(A, TC)
I = sectortype(A)
M = similarstoragetype(A, sectorscalartype(I) <: Real ? TC : complex(TC))
return tensormaptype(spacetype(A), N₁, N₂, M)
end

Expand Down Expand Up @@ -113,10 +114,11 @@ function TO.tensorcontract_type(TC,
A::AbstractTensorMap, ::Index2Tuple, ::Bool,
B::AbstractTensorMap, ::Index2Tuple, ::Bool,
::Index2Tuple{N₁,N₂}) where {N₁,N₂}
M = similarstoragetype(A, TC)
M == similarstoragetype(B, TC) ||
throw(ArgumentError("incompatible storage types:\n$(M) ≠ $(similarstoragetype(B, TC))"))
spacetype(A) == spacetype(B) || throw(SpaceMismatch("incompatible space types"))
I = sectortype(A)
M = similarstoragetype(A, sectorscalartype(I) <: Real ? TC : complex(TC))
MB = similarstoragetype(B, sectorscalartype(I) <: Real ? TC : complex(TC))
M == MB || throw(ArgumentError("incompatible storage types:\n$(M) ≠ $(MB)"))
return tensormaptype(spacetype(A), N₁, N₂, M)
end

Expand Down
Loading