@@ -10,7 +10,10 @@ const PEPSWeight{T,S} = AbstractTensorMap{T,S,1,1}
1010"""
1111 struct SUWeight{E<:PEPSWeight}
1212
13- Schmidt bond weights used in simple/cluster update. Weight elements are always real.
13+ Schmidt bond weights used in simple/cluster update.
14+ Weight elements are always real and non-negative.
15+ The domain and codomain of each weight matrix
16+ must be an un-dualed `ElementarySapce`.
1417
1518## Fields
1619
@@ -22,11 +25,21 @@ $(TYPEDFIELDS)
2225"""
2326struct SUWeight{E<: PEPSWeight }
2427 data:: Array{E,3}
28+ SUWeight{E}(data:: Array{E,3} ) where {E} = new{E}(data)
29+ end
2530
26- function SUWeight(data:: Array{E,3} ) where {E<: PEPSWeight }
27- @assert eltype(data[1 ]) <: Real
28- return new{E}(data)
31+ function SUWeight(data:: Array{E,3} ) where {E<: PEPSWeight }
32+ scalartype(data) <: Real || error(" Weight elements must be real numbers." )
33+ for wt in data
34+ isa(wt, DiagonalTensorMap) ||
35+ error(" Each weight matrix should be a DiagonalTensorMap" )
36+ domain(wt, 1 ) == codomain(wt, 1 ) ||
37+ error(" Domain and codomain of each weight matrix must be the same." )
38+ ! isdual(codomain(wt, 1 )) ||
39+ error(" Domain and codomain of each weight matrix cannot be a dual space." )
40+ all(wt. data .>= 0 ) || error(" Weight elements must be non-negative." )
2941 end
42+ return SUWeight{E}(data)
3043end
3144
3245function SUWeight(wts_mats:: AbstractMatrix{E} ...) where {E<: PEPSWeight }
@@ -50,6 +63,18 @@ VectorInterface.scalartype(::Type{T}) where {T<:SUWeight} = scalartype(eltype(T)
5063Base. getindex(W:: SUWeight , args... ) = Base. getindex(W. data, args... )
5164Base. setindex!(W:: SUWeight , args... ) = (Base. setindex!(W. data, args... ); W)
5265Base. axes(W:: SUWeight , args... ) = axes(W. data, args... )
66+ Base. iterate(W:: SUWeight , args... ) = iterate(W. data, args... )
67+
68+ # # (Approximate) equality
69+ function Base.:(== )(wts1:: SUWeight , wts2:: SUWeight )
70+ return wts1. data == wts2. data
71+ end
72+ function Base. isapprox(wts1:: SUWeight , wts2:: SUWeight ; kwargs... )
73+ for (wt1, wt2) in zip(wts1, wts2)
74+ ! isapprox(wt1, wt2; kwargs... ) && return false
75+ end
76+ return true
77+ end
5378
5479function compare_weights(wts1:: SUWeight , wts2:: SUWeight )
5580 @assert size(wts1) == size(wts2)
7297
7398Represents an infinite projected entangled-pair state on a 2D square lattice
7499consisting of vertex tensors and bond weights.
100+ The vertex tensor, x-weight and y-weight at row `i`, column `j`
101+ are defined as (the numbers show the axis order)
102+ ```
103+ 2
104+ ↓
105+ yᵢⱼ
106+ ↓
107+ 1
108+ 2
109+ ↓
110+ 5←-Tᵢⱼ←-3 1←-xᵢⱼ←-2
111+ ↓ ↘
112+ 4 1
113+ ```
75114
76115## Fields
77116
@@ -92,6 +131,7 @@ struct InfiniteWeightPEPS{T<:PEPSTensor,E<:PEPSWeight}
92131 ) where {T<: PEPSTensor ,E<: PEPSWeight }
93132 @assert size(vertices) == size(weights)[2 : end ]
94133 Nr, Nc = size(vertices)
134+ # check space matching between vertex tensors and weight matrices
95135 for (r, c) in Iterators. product(1 : Nr, 1 : Nc)
96136 space(weights[2 , r, c], 1 )' == space(vertices[r, c], 2 ) || throw(
97137 SpaceMismatch(" South space of bond weight y$((r, c)) does not match." )
136176function InfiniteWeightPEPS(
137177 f, T, Pspaces:: M , Nspaces:: M , Espaces:: M = Nspaces
138178) where {M<: AbstractMatrix{<:Union{Int,ElementarySpace}} }
179+ @assert all(! isdual, Pspaces)
180+ @assert all(! isdual, Nspaces)
181+ @assert all(! isdual, Espaces)
139182 vertices = InfinitePEPS(f, T, Pspaces, Nspaces, Espaces). A
140183 Nr, Nc = size(vertices)
141184 weights = map(Iterators. product(1 : 2 , 1 : Nr, 1 : Nc)) do (d, r, c)
@@ -296,17 +339,201 @@ function InfiniteWeightPEPS(peps::InfinitePEPS)
296339 return InfiniteWeightPEPS(peps. A, SUWeight(weights))
297340end
298341
342+ # # (Approximate) equality (gauge freedom is not allowed)
343+ function Base.:(== )(peps1:: InfiniteWeightPEPS , peps2:: InfiniteWeightPEPS )
344+ return peps1. vertices == peps2. vertices && peps1. weights == peps2. weights
345+ end
346+ function Base. isapprox(peps1:: InfiniteWeightPEPS , peps2:: InfiniteWeightPEPS ; kwargs... )
347+ for (v1, v2) in zip(peps1. vertices, peps2. vertices)
348+ ! isapprox(v1, v2; kwargs... ) && return false
349+ end
350+ ! isapprox(peps1. weights, peps2. weights; kwargs... ) && return false
351+ return true
352+ end
353+
354+ # Mirroring and rotation
355+ #= Example: 3 x 3 network
356+
357+ - Original
358+ ```
359+ | | |
360+ y₁₁ y₁₂ y₁₃
361+ | | |
362+ ..x₁₃...┼---x₁₁---┼---x₁₂---┼---x₁₃---
363+ | | | 2
364+ y₂₁ y₂₂ y₂₃ ↓
365+ | | | y
366+ ..x₂₃...┼---x₂₁---┼---x₂₂---┼---x₂₃--- ↓
367+ | | | 1
368+ y₃₁ y₃₂ y₃₃
369+ | | | 1 ←- x ←- 2
370+ ..x₃₃...┼---x₃₁---┼---x₃₂---┼---x₃₃---
371+ : : :
372+ y₁₁ y₁₂ y₁₃
373+ : : :
374+ ```
375+
376+ - After `mirror_antidiag`, x/y-weights are exchanged.
377+ ```
378+ | | |
379+ x₃₃ x₂₃ x₁₃
380+ | | |
381+ ..y₁₃...┼---y₃₃---┼---y₂₃---┼---y₁₃---
382+ | | | 2
383+ x₃₂ x₂₂ x₁₂ ↓
384+ | | | x
385+ ..y₁₂...┼---y₃₂---┼---y₂₂---┼---y₁₂--- ↓
386+ | | | 1
387+ x₃₁ x₂₁ x₁₁
388+ | | | 1 ←- y ←- 2
389+ ..y₁₁...┼---y₃₁---┼---y₂₁---┼---y₁₁---
390+ : : :
391+ x₃₃ x₂₃ x₁₃
392+ : : :
393+ ```
394+ No further operations are needed.
395+
396+ - After `rotl90`, x/y-weights are exchanged.
397+ ```
398+ | | |
399+ x₁₃ x₂₃ x₃₃
400+ | | |
401+ --y₁₃---┼---y₂₃---┼---y₃₃---┼...y₁₃...
402+ | | | 2
403+ x₁₂ x₂₂ x₃₂ ↓
404+ | | | x
405+ --y₁₂---┼---y₂₂---┼---y₃₂---┼...y₁₂... ↓
406+ | | | 1
407+ x₁₁ x₂₁ x₃₁
408+ | | | 2 -→ y -→ 1
409+ --y₁₁---┼---y₂₁---┼---y₃₁---┼...y₁₁...
410+ : : :
411+ x₁₃ x₂₃ x₃₃
412+ : : :
413+ ```
414+ We need to further:
415+ - Move 1st column of x-weights to the last column.
416+ - Permute axes of x-weights.
417+ - Flip x-arrows from → to ←.
418+
419+ - After `rotr90`, x/y-weights are exchanged.
420+ ```
421+ : : :
422+ x₃₃ x₂₃ x₁₃
423+ : : :
424+ ..y₁₁...┼---y₃₁---┼---y₂₁---┼---y₁₁---
425+ | | | 1 ←- y ←- 2
426+ x₃₁ x₂₁ x₁₁
427+ | | | 1
428+ ..y₁₂...┼---y₃₂---┼---y₂₂---┼---y₁₂--- ↑
429+ | | | x
430+ x₃₂ x₂₂ x₁₂ ↑
431+ | | | 2
432+ ..y₁₃...┼---y₃₃---┼---y₂₃---┼---y₁₃---
433+ | | |
434+ x₃₃ x₂₃ x₁₃
435+ | | |
436+ ```
437+ We need to further:
438+ - Move last row of y-weights to the 1st row.
439+ - Permute axes of y-weights.
440+ - Flip y-arrows from ↑ to ↓.
441+
442+ After `rot180`, x/y-weights are not exchanged.
443+ ```
444+ : : :
445+ y₁₃ y₁₂ y₁₁
446+ : : :
447+ --x₃₃---┼---x₃₂---┼---x₃₁---┼...x₃₃...
448+ | | | 2 -→ x -→ 1
449+ y₃₃ y₃₂ y₃₁
450+ | | | 1
451+ --x₂₃---┼---x₂₂---┼---x₂₁---┼...x₂₃... ↑
452+ | | | y
453+ y₂₃ y₂₂ y₂₁ ↑
454+ | | | 2
455+ --x₁₃---┼---x₁₂---┼---x₁₁---┼...x₁₃...
456+ | | |
457+ y₁₃ y₁₂ y₁₁
458+ | | |
459+ ```
460+ We need to further:
461+ - Move 1st column of x-weights to the last column.
462+ - Move last row of y-weights to the 1st row.
463+ - Permute axes of all weights and twist their axis 1.
464+ - Flip x-arrows from → to ←, and y-arrows from ↑ to ↓.
465+ =#
466+
467+ """
468+ Mirror an `SUWeight` by its anti-diagonal line.
469+ """
470+ function mirror_antidiag(wts:: SUWeight )
471+ weights2_x = mirror_antidiag(wts[2 , :, :])
472+ weights2_y = mirror_antidiag(wts[1 , :, :])
473+ return SUWeight(weights2_x, weights2_y)
474+ end
475+ function Base. rotl90(wts:: SUWeight )
476+ wts_x = circshift(rotl90(wts[2 , :, :]), (0 , - 1 ))
477+ for (i, wt) in enumerate(wts_x)
478+ wts_x[i] = DiagonalTensorMap(flip(permute(wt, ((2 ,), (1 ,))), (1 , 2 )))
479+ end
480+ wts_y = rotl90(wts[1 , :, :])
481+ return SUWeight(wts_x, wts_y)
482+ end
483+ function Base. rotr90(wts:: SUWeight )
484+ wts_x = rotr90(wts[2 , :, :])
485+ wts_y = circshift(rotr90(wts[1 , :, :]), (1 , 0 ))
486+ for (i, wt) in enumerate(wts_y)
487+ wts_y[i] = DiagonalTensorMap(flip(permute(wt, ((2 ,), (1 ,))), (1 , 2 )))
488+ end
489+ return SUWeight(wts_x, wts_y)
490+ end
491+ function Base. rot180(wts:: SUWeight )
492+ wts_x = circshift(rot180(wts[1 , :, :]), (0 , - 1 ))
493+ wts_y = circshift(rot180(wts[2 , :, :]), (1 , 0 ))
494+ for (i, wt) in enumerate(wts_x)
495+ wts_x[i] = DiagonalTensorMap(flip(permute(wt, ((2 ,), (1 ,))), (1 , 2 )))
496+ end
497+ for (i, wt) in enumerate(wts_y)
498+ wts_y[i] = DiagonalTensorMap(flip(permute(wt, ((2 ,), (1 ,))), (1 , 2 )))
499+ end
500+ return SUWeight(wts_x, wts_y)
501+ end
502+
299503"""
300504 mirror_antidiag(peps::InfiniteWeightPEPS)
301505
302- Mirror the unit cell of an iPEPS with weights by its anti-diagonal line.
506+ Mirror an `InfiniteWeightPEPS` by its anti-diagonal line.
303507"""
304508function mirror_antidiag(peps:: InfiniteWeightPEPS )
305509 vertices2 = mirror_antidiag(peps. vertices)
306510 for (i, t) in enumerate(vertices2)
307- vertices2[i] = permute(t, ((1 ,), (3 , 2 , 5 , 4 )))
511+ vertices2[i] = mirror_antidiag(t)
512+ end
513+ weights2 = mirror_antidiag(peps. weights)
514+ return InfiniteWeightPEPS(vertices2, weights2)
515+ end
516+ function Base. rotl90(peps:: InfiniteWeightPEPS )
517+ vertices2 = rotl90(peps. vertices)
518+ for (i, t) in enumerate(vertices2)
519+ vertices2[i] = flip(rotl90(t), (3 , 5 ))
520+ end
521+ weights2 = rotl90(peps. weights)
522+ return InfiniteWeightPEPS(vertices2, weights2)
523+ end
524+ function Base. rotr90(peps:: InfiniteWeightPEPS )
525+ vertices2 = rotr90(peps. vertices)
526+ for (i, t) in enumerate(vertices2)
527+ vertices2[i] = flip(rotr90(t), (2 , 4 ))
528+ end
529+ weights2 = rotr90(peps. weights)
530+ return InfiniteWeightPEPS(vertices2, weights2)
531+ end
532+ function Base. rot180(peps:: InfiniteWeightPEPS )
533+ vertices2 = rot180(peps. vertices)
534+ for (i, t) in enumerate(vertices2)
535+ vertices2[i] = flip(rot180(t), Tuple(2 : 5 ))
308536 end
309- weights2_x = mirror_antidiag(peps. weights[2 , :, :])
310- weights2_y = mirror_antidiag(peps. weights[1 , :, :])
311- return InfiniteWeightPEPS(vertices2, weights2_x, weights2_y)
537+ weights2 = rot180(peps. weights)
538+ return InfiniteWeightPEPS(vertices2, weights2)
312539end
0 commit comments