Skip to content

Commit 1798f87

Browse files
committed
Couple more fixes and a CUDA+MPS example
1 parent 4913563 commit 1798f87

6 files changed

Lines changed: 166 additions & 31 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""
2+
# [The Hard Hexagon model](@id demo_hardhexagon)
3+
4+
![logo](hexagon.svg)
5+
6+
Tensor networks are a natural way to do statistical mechanics on a lattice.
7+
As an example of this we will extract the central charge of the hard hexagon model.
8+
This model is known to have central charge 0.8, and has very peculiar non-local (anyonic) symmetries.
9+
Because TensorKit supports anyonic symmetries, so does MPSKit.
10+
To follow the tutorial you need the following packages.
11+
"""
12+
13+
using MatrixAlgebraKit, MPSKit, MPSKitModels, TensorKit, Plots, Polynomials, CUDA, cuTENSOR
14+
15+
#src # for reproducibility:
16+
#src using Random
17+
#src Random.seed!(123)
18+
19+
"""
20+
The [hard hexagon model](https://en.wikipedia.org/wiki/Hard_hexagon_model) is a 2-dimensional lattice model of a gas, where particles are allowed to be on the vertices of a triangular lattice, but no two particles may be adjacent.
21+
This can be encoded in a transfer matrix with a local MPO tensor using anyonic symmetries, and the resulting MPO has been implemented in MPSKitModels.
22+
23+
In order to use these anyonic symmetries, we need to generalise the notion of the bond dimension and define how it interacts with the symmetry. Thus, we implement away of converting integers to symmetric spaces of the given dimension, which provides a crude guess for how the final MPS would distribute its Schmidt spectrum.
24+
"""
25+
mpo = hard_hexagon(CuMatrix{ComplexF64, CUDA.DeviceMemory})
26+
P = physicalspace(mpo, 1)
27+
function virtual_space(D::Integer)
28+
_D = round(Int, D / sum(dim, values(FibonacciAnyon)))
29+
return Vect[FibonacciAnyon](sector => _D for sector in (:I, ))
30+
end
31+
32+
@assert isapprox(dim(virtual_space(100)), 100; atol = 3)
33+
34+
"""
35+
## The leading boundary
36+
37+
One way to study statistical mechanics in infinite systems with tensor networks is by approximating the dominant eigenvector of the transfer matrix by an MPS.
38+
This dominant eigenvector contains a lot of hidden information.
39+
For example, the free energy can be extracted by computing the expectation value of the mpo.
40+
Additionally, we can compute the entanglement entropy as well as the correlation length of the state:
41+
"""
42+
43+
D = 10
44+
V = virtual_space(D)
45+
ψ₀ = InfiniteMPS(CuVector{ComplexF64, CUDA.DeviceMemory}, [P], [V]; alg_orth = CUSOLVER_HouseholderQR(; positive=true))
46+
ψ, envs, = leading_boundary(
47+
ψ₀, mpo,
48+
VUMPS(; verbosity = 0, alg_eigsolve = MPSKit.Defaults.alg_eigsolve(; ishermitian = false))
49+
) # use non-hermitian eigensolver
50+
F = real(expectation_value(ψ, mpo))
51+
S = real(first(entropy(ψ)))
52+
ξ = correlation_length(ψ)
53+
println("F = $F\tS = $S\tξ = ")
54+
55+
"""
56+
## The scaling hypothesis
57+
58+
The dominant eigenvector is of course only an approximation. The finite bond dimension enforces a finite correlation length, which effectively introduces a length scale in the system. This can be exploited to formulate a scaling hypothesis [pollmann2009](@cite), which in turn allows to extract the central charge.
59+
60+
First we need to know the entropy and correlation length at a bunch of different bond dimensions. Our approach will be to re-use the previous approximated dominant eigenvector, and then expanding its bond dimension and re-running VUMPS.
61+
According to the scaling hypothesis we should have ``S \propto \frac{c}{6} log(ξ)``. Therefore we should find ``c`` using
62+
"""
63+
64+
function scaling_simulations(
65+
ψ₀, mpo, Ds; verbosity = 0, tol = 1.0e-6,
66+
alg_eigsolve = MPSKit.Defaults.alg_eigsolve(; ishermitian = false)
67+
)
68+
entropies = similar(Ds, Float64)
69+
correlations = similar(Ds, Float64)
70+
alg = VUMPS(; verbosity, tol, alg_eigsolve)
71+
72+
ψ, envs, = leading_boundary(ψ₀, mpo, alg)
73+
entropies[1] = real(entropy(ψ)[1])
74+
correlations[1] = correlation_length(ψ)
75+
76+
for (i, d) in enumerate(diff(Ds))
77+
ψ, envs = changebonds(ψ, mpo, OptimalExpand(; trscheme = truncrank(d)), envs)
78+
ψ, envs, = leading_boundary(ψ, mpo, alg, envs)
79+
entropies[i + 1] = real(entropy(ψ)[1])
80+
correlations[i + 1] = correlation_length(ψ)
81+
end
82+
return entropies, correlations
83+
end
84+
85+
bond_dimensions = 10:5:25
86+
ψ₀ = InfiniteMPS(CuVector{ComplexF64, CUDA.DeviceMemory}, [P], [virtual_space(bond_dimensions[1])])
87+
Ss, ξs = scaling_simulations(ψ₀, mpo, bond_dimensions)
88+
89+
f = fit(log.(ξs), 6 * Ss, 1)
90+
c = f.coeffs[2]
91+
92+
#+
93+
94+
p = plot(; xlabel = "logarithmic correlation length", ylabel = "entanglement entropy")
95+
p = plot(log.(ξs), Ss; seriestype = :scatter, label = nothing)
96+
plot!(p, ξ -> f(ξ) / 6; label = "fit")

src/environments/abstract_envs.jl

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,64 @@ Base.unlock(envs::AbstractMPSEnvironments) = unlock(envs.lock);
1515
# ------------------
1616
function allocate_GL(bra::AbstractMPS, mpo::AbstractMPO, ket::AbstractMPS, i::Int)
1717
T = Base.promote_type(scalartype(bra), scalartype(mpo), scalartype(ket))
18-
TA = similarstoragetype(storagetype(mpo), T)
18+
TO = TensorKit.similarstoragetype(storagetype(mpo), T)
19+
TB = TensorKit.similarstoragetype(storagetype(bra), T)
20+
TK = TensorKit.similarstoragetype(storagetype(ket), T)
21+
TA = reduce(TensorKit.promote_storagetype, (TB, TO, TK))
1922
V = left_virtualspace(bra, i) left_virtualspace(mpo, i)'
2023
left_virtualspace(ket, i)
2124
if V isa BlockTensorKit.TensorMapSumSpace
2225
TT = blocktensormaptype(spacetype(bra), numout(V), numin(V), TA)
2326
else
24-
TT = TensorKit.TensorMapWithStorage{T, TA}
27+
TT = TensorKit.tensormaptype(spacetype(bra), numout(V), numin(V), TA)
2528
end
2629
return TT(undef, V)
2730
end
2831

2932
function allocate_GR(bra::AbstractMPS, mpo::AbstractMPO, ket::AbstractMPS, i::Int)
3033
T = Base.promote_type(scalartype(bra), scalartype(mpo), scalartype(ket))
31-
TA = similarstoragetype(storagetype(mpo), T)
34+
TO = TensorKit.similarstoragetype(storagetype(mpo), T)
35+
TB = TensorKit.similarstoragetype(storagetype(bra), T)
36+
TK = TensorKit.similarstoragetype(storagetype(ket), T)
37+
TA = reduce(TensorKit.promote_storagetype, (TB, TO, TK))
3238
V = right_virtualspace(ket, i) right_virtualspace(mpo, i)
3339
right_virtualspace(bra, i)
3440
if V isa BlockTensorKit.TensorMapSumSpace
3541
TT = blocktensormaptype(spacetype(bra), numout(V), numin(V), TA)
3642
else
37-
TT = TensorKit.TensorMapWithStorage{T, TA}
43+
TT = TensorKit.tensormaptype(spacetype(bra), numout(V), numin(V), TA)
3844
end
3945
return TT(undef, V)
4046
end
4147

4248
function allocate_GBL(bra::QP, mpo::AbstractMPO, ket::QP, i::Int)
4349
T = Base.promote_type(scalartype(bra), scalartype(mpo), scalartype(ket))
44-
TA = similarstoragetype(storagetype(mpo), T)
50+
TO = TensorKit.similarstoragetype(storagetype(mpo), T)
51+
TB = TensorKit.similarstoragetype(storagetype(bra), T)
52+
TK = TensorKit.similarstoragetype(storagetype(ket), T)
53+
TA = reduce(TensorKit.promote_storagetype, (TB, TO, TK))
4554
V = left_virtualspace(bra.left_gs, i) left_virtualspace(mpo, i)'
4655
auxiliaryspace(ket)' left_virtualspace(ket.right_gs, i)
4756
if V isa BlockTensorKit.TensorMapSumSpace
4857
TT = blocktensormaptype(spacetype(bra), numout(V), numin(V), TA)
4958
else
50-
TT = TensorKit.TensorMapWithStorage{T, TA}
59+
TT = TensorKit.tensormaptype(spacetype(bra), numout(V), numin(V), TA)
5160
end
5261
return TT(undef, V)
5362
end
5463

5564
function allocate_GBR(bra::QP, mpo::AbstractMPO, ket::QP, i::Int)
5665
T = Base.promote_type(scalartype(bra), scalartype(mpo), scalartype(ket))
57-
TA = similarstoragetype(storagetype(mpo), T)
66+
TO = TensorKit.similarstoragetype(storagetype(mpo), T)
67+
TB = TensorKit.similarstoragetype(storagetype(bra), T)
68+
TK = TensorKit.similarstoragetype(storagetype(ket), T)
69+
TA = reduce(TensorKit.promote_storagetype, (TB, TO, TK))
5870
V = right_virtualspace(ket.left_gs, i) right_virtualspace(mpo, i)
5971
auxiliaryspace(ket)' right_virtualspace(bra.right_gs, i)
6072
if V isa BlockTensorKit.TensorMapSumSpace
6173
TT = blocktensormaptype(spacetype(bra), numout(V), numin(V), TA)
6274
else
63-
TT = TensorKit.TensorMapWithStorage{T, TA}
75+
TT = TensorKit.tensormaptype(spacetype(bra), numout(V), numin(V), TA)
6476
end
6577
return TT(undef, V)
6678
end

src/operators/jordanmpotensor.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ const JordanMPOTensorMap{T, S, A <: DenseVector{T}} = JordanMPOTensor{
6565
TensorMap{T, S, 1, 2, A},
6666
TensorMap{T, S, 1, 1, A},
6767
}
68+
# TODO FIX ME
69+
TensorKit.storagetype(jmpo::JordanMPOTensorMap{E, S, TA}) where {E, S, TA} = TA
70+
TensorKit.storagetype(::Type{JordanMPOTensorMap{E, S, TA}}) where {E, S, TA} = TA
6871

6972
function JordanMPOTensor{E, S}(::UndefInitializer, V::TensorMapSumSpace{S}) where {E, S}
7073
return jordanmpotensortype(S, E)(undef, V)
@@ -121,12 +124,13 @@ function JordanMPOTensor(W::SparseBlockTensorMap{TT, E, S, 2, 2}) where {TT, E,
121124
)
122125
end
123126

124-
function jordanmpotensortype(::Type{S}, ::Type{E}) where {S <: VectorSpace, E <: Number}
125-
TA = Union{tensormaptype(S, 2, 2, E), BraidingTensor{E, S}}
126-
TB = tensormaptype(S, 2, 1, E)
127-
TC = tensormaptype(S, 1, 2, E)
128-
TD = tensormaptype(S, 1, 1, E)
129-
return JordanMPOTensor{E, S, TA, TB, TC, TD}
127+
function jordanmpotensortype(::Type{S}, ::Type{TorA}) where {S <: VectorSpace, TorA}
128+
BT = BraidingTensor{eltype(TorA), S}
129+
TA = Union{tensormaptype(S, 2, 2, TorA), BT}
130+
TB = tensormaptype(S, 2, 1, TorA)
131+
TC = tensormaptype(S, 1, 2, TorA)
132+
TD = tensormaptype(S, 1, 1, TorA)
133+
return JordanMPOTensor{eltype(TorA), S, TA, TB, TC, TD}
130134
end
131135
function jordanmpotensortype(::Type{O}) where {O <: MPOTensor}
132136
return jordanmpotensortype(spacetype(O), scalartype(O))

src/operators/mpohamiltonian.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ end
4848
const InfiniteMPOHamiltonian{O <: MPOTensor} = MPOHamiltonian{O, PeriodicVector{O}}
4949
Base.isfinite(::Type{<:InfiniteMPOHamiltonian}) = false
5050
GeometryStyle(::Type{<:InfiniteMPOHamiltonian}) = InfiniteChainStyle()
51+
TensorKit.storagetype(impo::InfiniteMPOHamiltonian{O}) where {O} = storagetype(O)
52+
TensorKit.storagetype(::Type{InfiniteMPOHamiltonian{O}}) where {O} = storagetype(O)
5153

5254
function InfiniteMPOHamiltonian(Ws::AbstractVector{O}) where {O <: MPOTensor}
5355
for i in eachindex(Ws)
@@ -522,6 +524,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_
522524
T = _find_tensortype(nonzero_opps)
523525
E = scalartype(T)
524526
S = spacetype(T)
527+
TA = storagetype(T)
525528

526529
# construct the virtual spaces
527530
MissingS = Union{Missing, S}
@@ -588,7 +591,7 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_
588591
end
589592

590593
# construct the tensor
591-
TW = jordanmpotensortype(S, E)
594+
TW = jordanmpotensortype(S, TA)
592595
Os = map(1:length(lattice)) do site
593596
V = virtualsumspaces[site - 1] * lattice[site]
594597
lattice[site] * virtualsumspaces[site]

src/states/abstractmps.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,13 @@ Construct an `MPSTensor` with given physical and virtual spaces.
3434
"""
3535
function MPSTensor(
3636
::UndefInitializer, ::Type{TorA}, P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ
37-
) where {S <: ElementarySpace, TorA}
38-
return TensorKit.TensorMapWithStorage{TorA}(undef, Vₗ P Vᵣ)
37+
) where {S <: ElementarySpace, TorA <: Number}
38+
return TensorKit.TensorMap{TorA}(undef, Vₗ P Vᵣ)
39+
end
40+
function MPSTensor(
41+
::UndefInitializer, ::Type{TorA}, P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ
42+
) where {S <: ElementarySpace, TorA <: DenseVector}
43+
return TensorKit.TensorMapWithStorage{eltype(TorA), TorA}(undef, Vₗ P Vᵣ)
3944
end
4045
function MPSTensor(
4146
f, ::Type{TorA}, P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ
@@ -51,6 +56,12 @@ function MPSTensor(
5156
throw(ArgumentError("Unsupported initializer function: $f"))
5257
end
5358
end
59+
function MPSTensor(
60+
::Type{TorA},
61+
P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ
62+
) where {S <: ElementarySpace, TorA}
63+
return MPSTensor(rand, TorA, P, Vₗ, Vᵣ)
64+
end
5465
# TODO: reinstate function initializers?
5566
function MPSTensor(
5667
P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ

src/states/infinitemps.jl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ struct InfiniteMPS{A <: GenericMPSTensor, B <: MPSBondTensor} <: AbstractMPS
102102
return new{A, B}(AL, AR, C, AC)
103103
end
104104
end
105+
TensorKit.storagetype(imps::InfiniteMPS{A, B}) where {A, B} = storagetype(A)
106+
TensorKit.storagetype(::Type{InfiniteMPS{A, B}}) where {A, B} = storagetype(A)
105107

106108
#===========================================================================================
107109
Constructors
@@ -116,35 +118,42 @@ function InfiniteMPS(
116118
convert(PeriodicVector{B}, C), convert(PeriodicVector{A}, AC)
117119
)
118120
end
119-
120121
function InfiniteMPS(
122+
::Type{TorA},
121123
pspaces::AbstractVector{S}, Dspaces::AbstractVector{S};
122124
kwargs...
123-
) where {S <: IndexSpace}
124-
return InfiniteMPS(MPSTensor.(pspaces, circshift(Dspaces, 1), Dspaces); kwargs...)
125+
) where {S <: IndexSpace, TorA}
126+
return InfiniteMPS(MPSTensor.(TorA, pspaces, circshift(Dspaces, 1), Dspaces); kwargs...)
125127
end
126128
function InfiniteMPS(
127-
f, elt::Type{<:Number}, pspaces::AbstractVector{S}, Dspaces::AbstractVector{S};
129+
pspaces::AbstractVector{S}, Dspaces::AbstractVector{S};
128130
kwargs...
129131
) where {S <: IndexSpace}
132+
return InfiniteMPS(MPSTensor.(ComplexF64, pspaces, circshift(Dspaces, 1), Dspaces); kwargs...)
133+
end
134+
function InfiniteMPS(
135+
f, ::Type{TorA}, pspaces::AbstractVector{S}, Dspaces::AbstractVector{S};
136+
kwargs...
137+
) where {S <: IndexSpace, TorA}
130138
return InfiniteMPS(
131-
MPSTensor.(f, elt, pspaces, circshift(Dspaces, 1), Dspaces);
139+
MPSTensor.(f, TorA, pspaces, circshift(Dspaces, 1), Dspaces);
132140
kwargs...
133141
)
134142
end
135-
InfiniteMPS(d::S, D::S) where {S <: Union{Int, <:IndexSpace}} = InfiniteMPS([d], [D])
143+
InfiniteMPS(::Type{TorA}, d::S, D::S; kwargs...) where {S <: Union{Int, <:IndexSpace}, TorA} = InfiniteMPS(TorA, [d], [D]; kwargs...)
144+
InfiniteMPS(d::S, D::S; kwargs...) where {S <: Union{Int, <:IndexSpace}} = InfiniteMPS([d], [D]; kwargs...)
136145
function InfiniteMPS(
137-
f, elt::Type{<:Number}, d::S, D::S
138-
) where {S <: Union{Int, <:IndexSpace}}
139-
return InfiniteMPS(f, elt, [d], [D])
146+
f, ::Type{TorA}, d::S, D::S; kwargs...
147+
) where {S <: Union{Int, <:IndexSpace}, TorA}
148+
return InfiniteMPS(f, TorA, [d], [D]; kwargs...)
140149
end
141-
function InfiniteMPS(ds::AbstractVector{Int}, Ds::AbstractVector{Int})
142-
return InfiniteMPS(ComplexSpace.(ds), ComplexSpace.(Ds))
150+
function InfiniteMPS(ds::AbstractVector{Int}, Ds::AbstractVector{Int}; kwargs...)
151+
return InfiniteMPS(ComplexSpace.(ds), ComplexSpace.(Ds); kwargs...)
143152
end
144153
function InfiniteMPS(
145-
f, elt::Type{<:Number}, ds::AbstractVector{Int}, Ds::AbstractVector{Int}, kwargs...
146-
)
147-
return InfiniteMPS(f, elt, ComplexSpace.(ds), ComplexSpace.(Ds); kwargs...)
154+
f, ::Type{TorA}, ds::AbstractVector{Int}, Ds::AbstractVector{Int}, kwargs...
155+
) where {TorA}
156+
return InfiniteMPS(f, TorA, ComplexSpace.(ds), ComplexSpace.(Ds); kwargs...)
148157
end
149158

150159
function InfiniteMPS(A::AbstractVector{<:GenericMPSTensor}; kwargs...)

0 commit comments

Comments
 (0)