Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "FusionTensors"
uuid = "e16ca583-1f51-4df0-8e12-57d32947d33e"
authors = ["ITensor developers <[email protected]> and contributors"]
version = "0.4.1"
version = "0.5.0"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
Expand All @@ -11,6 +11,7 @@ GradedArrays = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2"
HalfIntegers = "f0d1745a-41c9-11e9-1dd9-e5d34d218721"
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Strided = "5e0ebb24-38b0-5f93-81fe-25c709ecae67"
TensorAlgebra = "68bd88dc-f39d-4e12-b2ca-f046b68fcc6a"
TensorProducts = "decf83d6-1968-43f4-96dc-fdb3fe15fc6d"
Expand All @@ -25,6 +26,7 @@ GradedArrays = "0.4.13"
HalfIntegers = "1.6"
LRUCache = "1.6"
LinearAlgebra = "1.10"
Random = "1.10"
Strided = "2.3"
TensorAlgebra = "0.3.8"
TensorProducts = "0.1.7"
Expand Down
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"

[compat]
Documenter = "1.10.0"
FusionTensors = "0.4"
FusionTensors = "0.5"
Literate = "2.20.1"
2 changes: 1 addition & 1 deletion src/fusiontensor/array_cast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function to_fusiontensor_no_checknorm(
codomain_legs::Tuple{Vararg{AbstractGradedUnitRange}},
domain_legs::Tuple{Vararg{AbstractGradedUnitRange}},
)
ft = FusionTensor(eltype(blockarray), codomain_legs, domain_legs)
ft = FusionTensor{eltype(blockarray)}(undef, codomain_legs, domain_legs)
for (f1, f2) in keys(trees_block_mapping(ft))
b = findblock(ft, f1, f2)
ft[f1, f2] = contract_fusion_trees(blockarray[b], f1, f2)
Expand Down
5 changes: 2 additions & 3 deletions src/fusiontensor/base_interface.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# This files defines Base functions for FusionTensor

using Accessors: @set

using BlockSparseArrays: @view!
using BlockSparseArrays: @view!, eachstoredblock
using TensorAlgebra: BlockedTuple, tuplemortar

set_data_matrix(ft::FusionTensor, data_matrix) = @set ft.data_matrix = data_matrix
Expand Down Expand Up @@ -105,7 +104,7 @@ function Base.similar(
return similar(ft, T, tuplemortar(new_axes))
end
function Base.similar(::FusionTensor, ::Type{T}, new_axes::BlockedTuple{2}) where {T}
return FusionTensor(T, new_axes)
return FusionTensor{T}(undef, new_axes)
end

Base.show(io::IO, ft::FusionTensor) = print(io, "$(ndims(ft))-dim FusionTensor")
Expand Down
52 changes: 49 additions & 3 deletions src/fusiontensor/fusiontensor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ using GradedArrays:
sectormergesort,
sectors,
space_isequal
using LinearAlgebra: UniformScaling
using Random: Random, AbstractRNG, randn!
using TensorAlgebra: BlockedTuple, trivial_axis, tuplemortar
using TensorProducts: tensor_product
using TypeParameterAccessors: type_parameters
Expand Down Expand Up @@ -174,12 +176,12 @@ function FusionTensor(
end

# empty matrix
function FusionTensor(elt::Type, legs::FusionTensorAxes)
function FusionTensor{T}(::UndefInitializer, legs::FusionTensorAxes) where {T}
S = sector_type(legs)
row_axis, codomain_trees_to_ranges = fuse_axes(S, codomain(legs))
col_axis, domain_trees_to_ranges = flip_domain(fuse_axes(S, dual.(domain(legs)))...)

mat = initialize_data_matrix(elt, row_axis, col_axis)
mat = initialize_data_matrix(T, row_axis, col_axis)
tree_to_block_mapping = intersect_codomain_domain(
codomain_trees_to_ranges, domain_trees_to_ranges
)
Expand All @@ -189,7 +191,7 @@ end
#constructor from precomputed data_matrix
function FusionTensor(mat::AbstractMatrix, legs::FusionTensorAxes)
# init with empty data_matrix to construct trees_block_mapping
ft = FusionTensor(eltype(mat), legs)
ft = FusionTensor{eltype(mat)}(undef, legs)
for b in eachblockstoredindex(mat)
b in eachblockstoredindex(data_matrix(ft)) ||
throw(ArgumentError("matrix block $b is not allowed"))
Expand All @@ -199,6 +201,9 @@ function FusionTensor(mat::AbstractMatrix, legs::FusionTensorAxes)
end

FusionTensor(x, legs::BlockedTuple{2}) = FusionTensor(x, FusionTensorAxes(legs))
function FusionTensor{T}(x, legs::BlockedTuple{2}) where {T}
return FusionTensor{T}(x, FusionTensorAxes(legs))
end

# constructor from split axes
function FusionTensor(
Expand All @@ -209,6 +214,47 @@ function FusionTensor(
return FusionTensor(x, tuplemortar((codomain_legs, domain_legs)))
end

function FusionTensor{T}(
x,
codomain_legs::Tuple{Vararg{AbstractGradedUnitRange}},
domain_legs::Tuple{Vararg{AbstractGradedUnitRange}},
) where {T}
return FusionTensor{T}(x, tuplemortar((codomain_legs, domain_legs)))
end

# specific constructors
function Base.zeros(::Type{T}, fta::FusionTensorAxes) where {T}
ft = FusionTensor{T}(undef, fta)
foreach(m -> fill!(m, zero(T)), eachstoredblock(data_matrix(ft)))
return ft
end
Base.zeros(fta::FusionTensorAxes) = zeros(Float64, fta)

function Base.randn(rng::AbstractRNG, ::Type{T}, fta::FusionTensorAxes) where {T}
ft = FusionTensor{T}(undef, fta)
foreach(m -> randn!(rng, m), eachstoredblock(data_matrix(ft)))
return ft
end
Base.randn(rng::AbstractRNG, fta::FusionTensorAxes) = randn(rng, Float64, fta)
Base.randn(::Type{T}, fta::FusionTensorAxes) where {T} = randn(Random.default_rng(), T, fta)
Base.randn(fta::FusionTensorAxes) = randn(Float64, fta)

function FusionTensor{T}(
s::UniformScaling, codomain_legs::Tuple{Vararg{AbstractGradedUnitRange}}
) where {T}
fta = FusionTensorAxes(codomain_legs, dual.(codomain_legs))
ft = FusionTensor{T}(undef, fta)
for m in eachstoredblock(data_matrix(ft))
m .= s(size(m, 1))
end
return ft
end
function FusionTensor(
s::UniformScaling, codomain_legs::Tuple{Vararg{AbstractGradedUnitRange}}
)
return FusionTensor{Float64}(s, codomain_legs)
end

# ================================ BlockArrays interface =================================

function BlockArrays.findblock(ft::FusionTensor, f1::SectorFusionTree, f2::SectorFusionTree)
Expand Down
8 changes: 4 additions & 4 deletions src/permutedims/permutedims.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ function fusiontensor_permutedims(ft, biperm::BlockedPermutation{2})
end
end

new_ft = FusionTensor(eltype(ft), axes(ft)[biperm])
new_ft = FusionTensor{eltype(ft)}(undef, axes(ft)[biperm])
fusiontensor_permutedims!(new_ft, ft, Tuple(biperm))
return new_ft
end

function fusiontensor_permutedims!(
new_ft::FusionTensor{T,N}, old_ft::FusionTensor{T,N}, flatperm::NTuple{N,Integer}
) where {T,N}
foreach(m -> fill!(m, zero(T)), eachstoredblock(data_matrix(new_ft)))
unitary = compute_unitary(new_ft, old_ft, flatperm)
for p in unitary
old_trees, new_trees = first(p)
for ((old_trees, new_trees), coeff) in unitary
new_block = view(new_ft, new_trees...)
old_block = view(old_ft, old_trees...)
@strided new_block .+= last(p) .* permutedims(old_block, flatperm)
@strided new_block .+= coeff .* permutedims(old_block, flatperm)
end
end
4 changes: 3 additions & 1 deletion test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ BlockSparseArrays = "2c9a651f-6452-4ace-a6ac-809f4280fbb4"
FusionTensors = "e16ca583-1f51-4df0-8e12-57d32947d33e"
GradedArrays = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
TensorAlgebra = "68bd88dc-f39d-4e12-b2ca-f046b68fcc6a"
Expand All @@ -15,9 +16,10 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Aqua = "0.8.11"
BlockArrays = "1.6"
BlockSparseArrays = "0.7"
FusionTensors = "0.4"
FusionTensors = "0.5"
GradedArrays = "0.4"
LinearAlgebra = "1.10.0"
Random = "1.10"
SafeTestsets = "0.1.0"
Suppressor = "0.2.8"
TensorAlgebra = "0.3"
Expand Down
65 changes: 56 additions & 9 deletions test/test_basics.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Test: @test, @test_throws, @testset

using BlockArrays: Block
using BlockSparseArrays: BlockSparseArray
using BlockSparseArrays: BlockSparseArray, eachblockstoredindex
using FusionTensors:
FusionTensor,
FusionTensorAxes,
Expand All @@ -28,15 +28,23 @@ using GradedArrays:
space_isequal
using TensorAlgebra: tuplemortar
using TensorProducts: tensor_product
using LinearAlgebra: LinearAlgebra
using Random: Random

include("setup.jl")

@testset "Fusion matrix" begin
g1 = gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 3])
g2 = dual(gradedrange([U1(0) => 2, U1(1) => 2, U1(3) => 1]))

fta = FusionTensorAxes((g1,), (g2,))
ft0 = FusionTensor{Float64}(undef, fta)
@test ft0 isa FusionTensor
@test space_isequal(codomain_axis(ft0), g1)
@test space_isequal(domain_axis(ft0), g2)

# check dual convention when initializing data_matrix
ft0 = FusionTensor(Float64, (g1,), (g2,))
ft0 = FusionTensor{Float64}(undef, (g1,), (g2,))
@test ft0 isa FusionTensor
@test space_isequal(codomain_axis(ft0), g1)
@test space_isequal(domain_axis(ft0), g2)
Expand Down Expand Up @@ -146,7 +154,7 @@ end
g1 = gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 3])

# one row axis
ft1 = FusionTensor(Float64, (g1,), ())
ft1 = FusionTensor{Float64}(undef, (g1,), ())
@test ndims_codomain(ft1) == 1
@test ndims_domain(ft1) == 0
@test ndims(ft1) == 1
Expand All @@ -156,7 +164,7 @@ end
@test sector_type(ft1) === sector_type(g1)

# one column axis
ft2 = FusionTensor(Float64, (), (g1,))
ft2 = FusionTensor{Float64}(undef, (), (g1,))
@test ndims_codomain(ft2) == 0
@test ndims_domain(ft2) == 1
@test ndims(ft2) == 1
Expand All @@ -166,7 +174,7 @@ end
@test sector_type(ft2) === sector_type(g1)

# zero axis
ft3 = FusionTensor(Float64, (), ())
ft3 = FusionTensor{Float64}(undef, (), ())
@test ndims_codomain(ft3) == 0
@test ndims_domain(ft3) == 0
@test ndims(ft3) == 0
Expand All @@ -181,7 +189,7 @@ end
g2 = gradedrange([U1(0) => 2, U1(1) => 2, U1(3) => 1])
g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1])
g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1])
ft3 = FusionTensor(Float64, (g1, g2), (g3, g4))
ft3 = FusionTensor{Float64}(undef, (g1, g2), (g3, g4))
@test isnothing(check_sanity(ft3))

ft4 = +ft3
Expand Down Expand Up @@ -260,19 +268,58 @@ end
@test space_isequal(dual(g4), codomain_axes(ad)[2])
@test isnothing(check_sanity(ad))

ft7 = FusionTensor(Float64, (g1,), (g2, g3, g4))
ft7 = FusionTensor{Float64}(undef, (g1,), (g2, g3, g4))
@test_throws DimensionMismatch ft7 + ft3
@test_throws DimensionMismatch ft7 - ft3
@test_throws DimensionMismatch ft7 * ft3
end

@testset "specific constructors" begin
g1 = gradedrange([U1(0) => 1, U1(1) => 2, U1(2) => 3])
g2 = gradedrange([U1(0) => 2, U1(1) => 2, U1(3) => 1])
g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1])
g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1])

fta = FusionTensorAxes((g1,), (g2, g3))
@test zeros(fta) isa FusionTensor{Float64,3}
@test zeros(ComplexF64, fta) isa FusionTensor{ComplexF64,3}

rng = Random.default_rng()
ft1 = randn(rng, ComplexF64, fta)
@test ft1 isa FusionTensor{ComplexF64,3}
@test all(!=(0), data_matrix(ft1)[Block(1, 5)])
@test randn(rng, fta) isa FusionTensor{Float64,3}
@test randn(ComplexF64, fta) isa FusionTensor{ComplexF64,3}
@test randn(fta) isa FusionTensor{Float64,3}

ft2 = FusionTensor(LinearAlgebra.I, (g1, g2))
@test ft2 isa FusionTensor{Float64,4}
@test axes(ft2) == FusionTensorAxes((g1, g2), dual.((g1, g2)))
@test collect(eachblockstoredindex(data_matrix(ft2))) == map(i -> Block(i, i), 1:6)
for i in 1:6
m = data_matrix(ft2)[Block(i, i)]
@test m == LinearAlgebra.I(size(m, 1))
end

ft2 = FusionTensor(3 * LinearAlgebra.I, (g1, g2))
@test ft2 isa FusionTensor{Float64,4}
@test axes(ft2) == FusionTensorAxes((g1, g2), dual.((g1, g2)))
@test collect(eachblockstoredindex(data_matrix(ft2))) == map(i -> Block(i, i), 1:6)
for i in 1:6
m = data_matrix(ft2)[Block(i, i)]
@test m == 3 * LinearAlgebra.I(size(m, 1))
end

@test FusionTensor{ComplexF64}(LinearAlgebra.I, (g1, g2)) isa FusionTensor{ComplexF64,4}
end

@testset "missing SectorProduct" begin
g1 = gradedrange([SectorProduct(U1(1)) => 1])
g2 = gradedrange([SectorProduct(U1(1), SU2(1//2)) => 1])
g3 = gradedrange([SectorProduct(U1(1), SU2(1//2), Z{2}(1)) => 1])
S = sector_type(g3)

ft = FusionTensor(Float64, (g1,), (dual(g2), dual(g3)))
ft = FusionTensor{Float64}(undef, (g1,), (dual(g2), dual(g3)))
@test sector_type(ft) === S
gr = gradedrange([SectorProduct(U1(1), SU2(0), Z{2}(0)) => 1])
@test space_isequal(codomain_axis(ft), gr)
Expand All @@ -287,7 +334,7 @@ end
gABC = tensor_product(gA, gB, gC)
S = sector_type(gABC)

ft = FusionTensor(Float64, (gA, gB), (dual(gA), dual(gB), gC))
ft = FusionTensor{Float64}(undef, (gA, gB), (dual(gA), dual(gB), gC))
@test sector_type(ft) === S
@test space_isequal(codomain_axis(ft), gABC)
@test space_isequal(domain_axis(ft), dual(gABC))
Expand Down
10 changes: 5 additions & 5 deletions test/test_contraction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ include("setup.jl")
g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1])
g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1])

ft1 = FusionTensor(Float64, (g1, g2), (g3, g4))
ft1 = FusionTensor{Float64}(undef, (g1, g2), (g3, g4))
@test isnothing(check_sanity(ft1))

ft2 = FusionTensor(Float64, dual.((g3, g4)), (g1,))
ft2 = FusionTensor{Float64}(undef, dual.((g3, g4)), (g1,))
@test isnothing(check_sanity(ft2))

ft3 = ft1 * ft2 # tensor contraction
Expand All @@ -43,9 +43,9 @@ end
g3 = gradedrange([U1(-1) => 1, U1(0) => 2, U1(1) => 1])
g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1])

ft1 = FusionTensor(Float64, (g1, g2), (g3, g4))
ft2 = FusionTensor(Float64, dual.((g3, g4)), (dual(g1),))
ft3 = FusionTensor(Float64, dual.((g3, g4)), dual.((g1, g2)))
ft1 = FusionTensor{Float64}(undef, (g1, g2), (g3, g4))
ft2 = FusionTensor{Float64}(undef, dual.((g3, g4)), (dual(g1),))
ft3 = FusionTensor{Float64}(undef, dual.((g3, g4)), dual.((g1, g2)))

ft4, legs = contract(ft1, (1, 2, 3, 4), ft2, (3, 4, 5))
@test legs == tuplemortar(((1, 2), (5,)))
Expand Down
5 changes: 0 additions & 5 deletions test/test_linear_algebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ include("setup.jl")
gsu2 = gradedrange([SU2(1 / 2) => 1])

for g in [g0, gu1, gsu2]
ft0 = FusionTensor(Float64, (g, g), (dual(g), dual(g)))
@test isnothing(check_sanity(ft0))
@test norm(ft0) == 0
@test tr(ft0) == 0

ft = to_fusiontensor(sdst, (g, g), (dual(g), dual(g)))
@test isnothing(check_sanity(ft))
@test norm(ft) ≈ √3 / 2
Expand Down
2 changes: 1 addition & 1 deletion test/test_permutedims.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ include("setup.jl")
g4 = gradedrange([U1(-1) => 1, U1(0) => 1, U1(1) => 1])

for elt in (Float64, ComplexF64)
ft1 = FusionTensor(elt, (g1, g2), dual.((g3, g4)))
ft1 = FusionTensor{elt}(undef, (g1, g2), dual.((g3, g4)))
@test isnothing(check_sanity(ft1))

# test permutedims interface
Expand Down
Loading