From 29bb35a53f3102592b56e53598f92caf0151663c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 12 Jun 2025 18:52:11 -0400 Subject: [PATCH 1/3] define and test spacetype --- src/operators/localoperator.jl | 5 ++-- test/utility/localoperator.jl | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/utility/localoperator.jl diff --git a/src/operators/localoperator.jl b/src/operators/localoperator.jl index 45796ee36..0bafec669 100644 --- a/src/operators/localoperator.jl +++ b/src/operators/localoperator.jl @@ -139,7 +139,7 @@ Base.:-(O1::LocalOperator, O2::LocalOperator) = O1 + (-O2) """ mirror_antidiag(site::CartesianIndex{2}, (Nrow, Ncol)::NTuple{2,Int}) -Get the position of `site` after reflection about +Get the position of `site` after reflection about the anti-diagonal line of a unit cell of size `(Nrow, Ncol)`. """ function mirror_antidiag(site::CartesianIndex{2}, (Nrow, Ncol)::NTuple{2,Int}) @@ -186,9 +186,10 @@ end # Charge shifting # --------------- - TensorKit.sectortype(O::LocalOperator) = sectortype(typeof(O)) TensorKit.sectortype(::Type{<:LocalOperator{T,S}}) where {T,S} = sectortype(S) +TensorKit.spacetype(O::LocalOperator) = spacetype(typeof(O)) +TensorKit.spacetype(::Type{T}) where {S,T<:LocalOperator{<:Any,S}} = S @generated function _fuse_isomorphisms( op::AbstractTensorMap{<:Any,S,N,N}, fs::Vector{<:AbstractTensorMap{<:Any,S,1,2}} diff --git a/test/utility/localoperator.jl b/test/utility/localoperator.jl new file mode 100644 index 000000000..5d5791e9b --- /dev/null +++ b/test/utility/localoperator.jl @@ -0,0 +1,45 @@ +using TensorKit +using PEPSKit +using Test + +vds = (ℂ^2, Rep[U₁](1 => 1, -1 => 1), Rep[SU₂](1 / 2 => 1)) +@testset "LocalOperator $vd" for vd in vds + t = randn(ComplexF64, vd ⊗ vd ← vd ⊗ vd) + physical_spaces = fill(vd, (2, 2)) + + terms = ((CartesianIndex(1, 1), CartesianIndex(1, 2)) => t,) + op = LocalOperator(physical_spaces, terms...) + + @test op isa LocalOperator + @test length(op.terms) == 1 + @test sectortype(op) === sectortype(vd) + @test spacetype(op) === typeof(vd) + @test physicalspace(op) == physical_spaces + + @test real(last(only(real(op).terms))) == real(t) + @test real(last(only(imag(op).terms))) == imag(t) + + op2 = 2 * op + @test op2 isa LocalOperator + @test typeof(op2) === typeof(op) + @test physicalspace(op2) == physical_spaces + + @test real(last(only(real(op2).terms))) ≈ 2 * real(t) + @test real(last(only(imag(op2).terms))) ≈ 2 * imag(t) + + op3 = op / 3 + @test op3 isa LocalOperator + @test typeof(op3) === typeof(op) + @test physicalspace(op3) == physical_spaces + + @test real(last(only(real(op3).terms))) ≈ real(t) / 3 + @test real(last(only(imag(op3).terms))) ≈ imag(t) / 3 + + t2 = randn(vd ⊗ vd ← vd ⊗ vd) + terms2 = ((CartesianIndex(2, 1), CartesianIndex(1, 2)) => t2,) + op4 = LocalOperator(physical_spaces, terms2...) + op5 = op + op4 + @test op5 isa LocalOperator + @test physicalspace(op5) == physical_spaces + @test length(op5.terms) == 2 +end From 1d98c091cc47e81ffe24b06d8e114c5b05e992b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 12 Jun 2025 18:55:39 -0400 Subject: [PATCH 2/3] update runtests --- test/runtests.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 29f8fc0e3..7d93eda49 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -63,6 +63,9 @@ end end end if GROUP == "ALL" || GROUP == "UTILITY" + @time @safetestset "LocalOperator" begin + include("utility/localoperator.jl") + end @time @safetestset "SVD wrapper" begin include("utility/svd_wrapper.jl") end From 5ffa8917473f5183ab04fb2dea108d384cf4d63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Gauth=C3=A9?= Date: Thu, 12 Jun 2025 19:06:22 -0400 Subject: [PATCH 3/3] define spaces for SUWeights and InfiniteWeightPEPS --- src/states/infiniteweightpeps.jl | 51 +++++++++++++++++++------------- test/utility/iwpeps_rotation.jl | 5 ++++ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/states/infiniteweightpeps.jl b/src/states/infiniteweightpeps.jl index f26f33e16..4be67796e 100644 --- a/src/states/infiniteweightpeps.jl +++ b/src/states/infiniteweightpeps.jl @@ -2,7 +2,7 @@ """ const PEPSWeight -Default type for PEPS bond weights with 2 virtual indices, conventionally ordered as: ``wt : WS ← EN``. +Default type for PEPS bond weights with 2 virtual indices, conventionally ordered as: ``wt : WS ← EN``. `WS`, `EN` denote the west/south, east/north spaces for x/y-weights on the square lattice, respectively. """ const PEPSWeight{T,S} = AbstractTensorMap{T,S,1,1} @@ -10,9 +10,9 @@ const PEPSWeight{T,S} = AbstractTensorMap{T,S,1,1} """ struct SUWeight{E<:PEPSWeight} -Schmidt bond weights used in simple/cluster update. +Schmidt bond weights used in simple/cluster update. Weight elements are always real and non-negative. -The domain and codomain of each weight matrix +The domain and codomain of each weight matrix must be an un-dualed `ElementarySapce`. ## Fields @@ -65,6 +65,12 @@ Base.setindex!(W::SUWeight, args...) = (Base.setindex!(W.data, args...); W) Base.axes(W::SUWeight, args...) = axes(W.data, args...) Base.iterate(W::SUWeight, args...) = iterate(W.data, args...) +## spaces +TensorKit.spacetype(w::SUWeight) = spacetype(typeof(w)) +TensorKit.spacetype(::Type{T}) where {E,T<:SUWeight{E}} = spacetype(E) +TensorKit.sectortype(w::SUWeight) = sectortype(typeof(w)) +TensorKit.sectortype(::Type{<:SUWeight{T}}) where {T} = sectortype(spacetype(T)) + ## (Approximate) equality function Base.:(==)(wts1::SUWeight, wts2::SUWeight) return wts1.data == wts2.data @@ -97,7 +103,7 @@ end Represents an infinite projected entangled-pair state on a 2D square lattice consisting of vertex tensors and bond weights. -The vertex tensor, x-weight and y-weight at row `i`, column `j` +The vertex tensor, x-weight and y-weight at row `i`, column `j` are defined as (the numbers show the axis order) ``` 2 @@ -166,7 +172,7 @@ end Create an InfiniteWeightPEPS by specifying the physical, north virtual and east virtual spaces of the PEPS vertex tensor at each site in the unit cell as a matrix. Each individual space can be specified as either an `Int` or an `ElementarySpace`. -Bond weights are initialized as identity matrices of element type `Float64`. +Bond weights are initialized as identity matrices of element type `Float64`. """ function InfiniteWeightPEPS( Pspaces::M, Nspaces::M, Espaces::M @@ -192,8 +198,8 @@ end InfiniteWeightPEPS([f=randn, T=ComplexF64,] Pspace::S, Nspace::S, Espace::S=Nspace; unitcell::Tuple{Int,Int}=(1, 1)) where {S<:ElementarySpace} Create an InfiniteWeightPEPS by specifying its physical, north and east spaces (as `ElementarySpace`s) and unit cell size. -Use `T` to specify the element type of the vertex tensors. -Bond weights are initialized as identity matrices of element type `Float64`. +Use `T` to specify the element type of the vertex tensors. +Bond weights are initialized as identity matrices of element type `Float64`. """ function InfiniteWeightPEPS(Pspaces::S, Nspaces::S, Espaces::S) where {S<:ElementarySpace} return InfiniteWeightPEPS(randn, ComplexF64, Pspaces, Nspaces, Espaces) @@ -210,6 +216,11 @@ function Base.size(peps::InfiniteWeightPEPS) return size(peps.vertices) end +TensorKit.spacetype(peps::InfiniteWeightPEPS) = spacetype(typeof(peps)) +TensorKit.spacetype(::Type{T}) where {E,T<:InfiniteWeightPEPS{E}} = spacetype(E) +TensorKit.sectortype(peps::InfiniteWeightPEPS) = sectortype(typeof(peps)) +TensorKit.sectortype(::Type{<:InfiniteWeightPEPS{T}}) where {T} = sectortype(spacetype(T)) + function _absorb_weights( t::PEPSTensor, weights::SUWeight, @@ -271,13 +282,13 @@ position (`row`, `col`) in the unit cell. Weights around the tensor at `(row, co ## Arguments -- `t::T` : The vertex tensor to which the weight will be absorbed. The first axis of `t` should be the physical axis. +- `t::T` : The vertex tensor to which the weight will be absorbed. The first axis of `t` should be the physical axis. - `row::Int` : The row index specifying the position in the tensor network. - `col::Int` : The column index specifying the position in the tensor network. - `ax::Int` : The axis into which the weight is absorbed, taking values from 1 to 4, standing for north, east, south, west respectively. - `weights::SUWeight` : The weight object to absorb into the tensor. -## Keyword arguments +## Keyword arguments - `sqrtwt::Bool=false` : If `true`, the square root of the weight is absorbed. - `invwt::Bool=false` : If `true`, the inverse of the weight is absorbed. @@ -373,7 +384,7 @@ end : : : ``` -- After `mirror_antidiag`, x/y-weights are exchanged. +- After `mirror_antidiag`, x/y-weights are exchanged. ``` | | | x₃₃ x₂₃ x₁₃ @@ -391,9 +402,9 @@ end x₃₃ x₂₃ x₁₃ : : : ``` - No further operations are needed. + No further operations are needed. -- After `rotl90`, x/y-weights are exchanged. +- After `rotl90`, x/y-weights are exchanged. ``` | | | x₁₃ x₂₃ x₃₃ @@ -414,9 +425,9 @@ end We need to further: - Move 1st column of x-weights to the last column. - Permute axes of x-weights. - - Flip x-arrows from → to ←. + - Flip x-arrows from → to ←. -- After `rotr90`, x/y-weights are exchanged. +- After `rotr90`, x/y-weights are exchanged. ``` : : : x₃₃ x₂₃ x₁₃ @@ -435,11 +446,11 @@ end | | | ``` We need to further: - - Move last row of y-weights to the 1st row. - - Permute axes of y-weights. - - Flip y-arrows from ↑ to ↓. + - Move last row of y-weights to the 1st row. + - Permute axes of y-weights. + - Flip y-arrows from ↑ to ↓. -After `rot180`, x/y-weights are not exchanged. +After `rot180`, x/y-weights are not exchanged. ``` : : : y₁₃ y₁₂ y₁₁ @@ -460,8 +471,8 @@ After `rot180`, x/y-weights are not exchanged. We need to further: - Move 1st column of x-weights to the last column. - Move last row of y-weights to the 1st row. - - Permute axes of all weights and twist their axis 1. - - Flip x-arrows from → to ←, and y-arrows from ↑ to ↓. + - Permute axes of all weights and twist their axis 1. + - Flip x-arrows from → to ←, and y-arrows from ↑ to ↓. =# """ diff --git a/test/utility/iwpeps_rotation.jl b/test/utility/iwpeps_rotation.jl index b19d5ac63..876fdf432 100644 --- a/test/utility/iwpeps_rotation.jl +++ b/test/utility/iwpeps_rotation.jl @@ -75,6 +75,11 @@ weights = collect(tsvd(rand(Float64, V ← V))[2] for dir in 1:2, r in 1:Nr, c i weights = SUWeight(weights) pepswt = InfiniteWeightPEPS(vertices, weights) +@test sectortype(weights) === sectortype(V) +@test spacetype(weights) === spacetype(V) +@test sectortype(pepswt) === sectortype(V) +@test spacetype(pepswt) === spacetype(V) + test_rotation(weights) @static if pkgversion(TensorKit) >= v"0.14.6" test_rotation(pepswt)