Skip to content

Commit b41cd92

Browse files
authored
Various small fixes and improvements to RandExpand (#335)
* rework random expansion * update docstring * add and update changelog * fix spaces * fix overexpanding * add changebonds for MultilineMPS * ensure spaces are consistent * add periodicvector * clamp iteration count to avoid NaN * rework implementation again * clamp krylovdim * add utility fullrank for spaces * remove stray show * fix off-by-one error * discard attempts and go back to original implementation * update changelog * update docs * fix in-place for multiline * more small fixes and changelog updates * improve docs * small fix * another small fix
1 parent 0f52943 commit b41cd92

7 files changed

Lines changed: 101 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- `LocalPreferences.toml` file to ensure `TensorOperations` properly precompiles on testing
13+
infrastructure
14+
15+
### Fixed
16+
17+
- Dynamic tolerances yielded `NaN` during the initialization stage due to `1 / sqrt(iter)`
18+
where `iter = 0`.
19+
- `InfiniteMPOHamiltonian` environments with low bond dimension and high Krylov dimension now are properly
20+
clamped.
21+
22+
### Changed
23+
24+
- The `changebonds(state, ::RandExpand)` algorithm now no longer has to perform a
25+
truncated SVD to obtain the desired spaces, and instead sample the space directly
26+
and then generates a random isometry. This should be slightly more performant, but
27+
otherwise equivalent.
28+
29+
### Deprecated
30+
31+
### Removed
32+
33+
[unreleased]: https://github.com/quantumkithub/pepskit.jl/compare/v0.13.8...HEAD

LocalPreferences.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[TensorOperations]
2+
precompile_workload = true

docs/src/man/algorithms.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ disadvantages:
292292
and if the bond dimension is grown slow enough, this still obtains a very good expansion
293293
scheme. Again, The state will remain unchanged and a one-site scheme will now be able to
294294
push the optimization further. The subspace used for expansion can be truncated through
295-
`trscheme`, which dictates how many singular values will be added.
295+
`trscheme`, which dictates how many orthogonal vectors will be added.
296296

297297
* [`VUMPSSvdCut`](@ref): This algorithm is based on the [`VUMPS`](@ref) algorithm, and
298298
consists of performing a two-site update, and then truncating the state back down. Because

src/algorithms/changebonds/randexpand.jl

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
$(TYPEDEF)
33
44
An algorithm that expands the bond dimension by adding random unitary vectors that are
5-
orthogonal to the existing state. This is achieved by performing a truncated SVD on a random
6-
two-site MPS tensor, which is made orthogonal to the existing state.
5+
orthogonal to the existing state. This means that additional directions are added to
6+
`AL` and `AR` that are contained in the nullspace of both. Note that this is happens in
7+
parallel, and therefore the expansion will never go beyond the local two-site subspace.
8+
9+
The truncation strategy dictates the number of expanded states, by generating uniformly
10+
distributed weights for each state in the two-site space and truncating that.
711
812
## Fields
913
@@ -17,32 +21,35 @@ $(TYPEDFIELDS)
1721
trscheme::TruncationStrategy
1822
end
1923

20-
function changebonds::InfiniteMPS, alg::RandExpand)
24+
function changebonds!::InfiniteMPS, alg::RandExpand)
2125
T = eltype.AL)
2226
AL′ = similar.AL)
2327
AR′ = similar.AR, tensormaptype(spacetype(T), 1, numind(T) - 1, storagetype(T)))
24-
for i in 1:length(ψ)
25-
# determine optimal expansion spaces around bond i
26-
AC2 = randomize!(_transpose_front.AC[i]) * _transpose_tail.AR[i + 1]))
2728

28-
# Use the nullspaces and SVD decomposition to determine the optimal expansion space
29+
for i in 1:length(ψ)
30+
# obtain spaces by sampling the support of both the left and right nullspace
2931
VL = left_null.AL[i])
3032
VR = right_null!(_transpose_tail.AR[i + 1]; copy = true))
31-
intermediate = normalize!(adjoint(VL) * AC2 * adjoint(VR))
32-
U, _, Vᴴ = svd_trunc!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd)
33+
V = sample_space(infimum(right_virtualspace(VL), space(VR, 1)), alg.trscheme)
3334

34-
AL′[i] = VL * U
35-
AR′[i + 1] = Vᴴ * VR
35+
# obtain (orthogonal) directions as isometries in that direction
36+
XL = randisometry(scalartype(VL), right_virtualspace(VL) V)
37+
AL′[i] = VL * XL
38+
XR = randisometry(scalartype(VR), space(VR, 1) V)
39+
AR′[i + 1] = XR' * VR
3640
end
3741

38-
return _expand(ψ, AL′, AR′)
42+
return _expand!(ψ, AL′, AR′)
3943
end
4044

41-
function changebonds::MultilineMPS, alg::RandExpand)
42-
return Multiline(map(x -> changebonds(x, alg), ψ.data))
45+
function changebonds!::MultilineMPS, alg::RandExpand)
46+
foreach(Base.Fix2(changebonds!, alg), ψ.data)
47+
return ψ
4348
end
4449

45-
changebonds::AbstractFiniteMPS, alg::RandExpand) = changebonds!(copy(ψ), alg)
50+
changebonds::AbstractMPS, alg::RandExpand) = changebonds!(copy(ψ), alg)
51+
changebonds::MultilineMPS, alg::RandExpand) = changebonds!(copy(ψ), alg)
52+
4653
function changebonds!::AbstractFiniteMPS, alg::RandExpand)
4754
for i in 1:(length(ψ) - 1)
4855
AC2 = randomize!(_transpose_front.AC[i]) * _transpose_tail.AR[i + 1]))
@@ -67,3 +74,15 @@ function changebonds!(ψ::AbstractFiniteMPS, alg::RandExpand)
6774

6875
return normalize!(ψ)
6976
end
77+
78+
"""
79+
sample_space(V, strategy)
80+
81+
Sample basis states within a given `V::VectorSpace` by creating weights for each state that
82+
are distributed uniformly, and then truncating according to the given `strategy`.
83+
"""
84+
function sample_space(V, strategy)
85+
S = TensorKit.SectorDict(c => Random.rand(dim(V, c)) for c in sectors(V))
86+
ind = MatrixAlgebraKit.findtruncated(S, strategy)
87+
return TensorKit.Factorizations.truncate_space(V, ind)
88+
end

src/environments/abstract_envs.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ function environment_alg(
8181
tol = Defaults.tol, maxiter = Defaults.maxiter, krylovdim = Defaults.krylovdim,
8282
verbosity = Defaults.VERBOSE_NONE
8383
)
84-
return GMRES(; tol, maxiter, krylovdim, verbosity)
84+
max_krylovdim = dim(left_virtualspace(above, 1)) * dim(left_virtualspace(below, 1))
85+
return GMRES(; tol, maxiter, krylovdim = min(max_krylovdim, krylovdim), verbosity)
8586
end
8687
function environment_alg(
8788
::Union{InfiniteQP, MultilineQP}, ::Union{InfiniteMPO, MultilineMPO},

src/states/abstractmps.jl

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ Determine whether the given tensor is full rank, i.e. whether both the map from
9393
virtual space and the physical space to the right virtual space, and the map from the right
9494
virtual space and the physical space to the left virtual space are injective.
9595
"""
96-
function isfullrank(A::GenericMPSTensor; side = :both)
97-
Vₗ = _firstspace(A)
98-
Vᵣ = _lastspace(A)
99-
P = (space.(Ref(A), 2:(numind(A) - 1))...)
96+
isfullrank(A::GenericMPSTensor; kwargs...) = isfullrank(space(A); kwargs...)
97+
function isfullrank(V::TensorKit.TensorMapSpace; side = :both)
98+
Vₗ = V[1]
99+
Vᵣ = V[numind(V)]
100+
P = (getindex.(Ref(V), 2:(numind(V) - 1))...)
100101
return if side === :both
101102
Vₗ P Vᵣ' && Vₗ' P Vᵣ
102103
elseif side === :right
@@ -129,6 +130,29 @@ function makefullrank!(A::PeriodicVector{<:GenericMPSTensor}; alg = Defaults.alg
129130
return A
130131
end
131132

133+
function makefullrank!(virtualspaces::PeriodicVector{S}, physicalspaces::PeriodicVector{S}) where {S <: ElementarySpace}
134+
haschanged = true
135+
while haschanged
136+
haschanged = false
137+
for i in 1:length(virtualspaces)
138+
Vmax = fuse(virtualspaces[i - 1], physicalspaces[i - 1])
139+
if !(virtualspaces[i] Vmax)
140+
virtualspaces[i] = infimum(virtualspaces[i], Vmax)
141+
haschanged = true
142+
end
143+
end
144+
for i in reverse(1:length(virtualspaces))
145+
Vmax = fuse(dual(physicalspaces[i - 1]), virtualspaces[i])
146+
if !(virtualspaces[i - 1] Vmax)
147+
virtualspaces[i - 1] = infimum(virtualspaces[i - 1], Vmax)
148+
haschanged = true
149+
end
150+
end
151+
end
152+
153+
return virtualspaces
154+
end
155+
132156
# Tensor accessors
133157
# ----------------
134158
@doc """

src/utility/dynamictols.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ where the new tolerance is given by
6262
new_tol = clamp(ϵ * alg.tol_factor / sqrt(iter), alg.tol_min, alg.tol_max)
6363
"""
6464
function updatetol(alg::DynamicTol, iter::Integer, ϵ::Real)
65+
iter = max(iter, one(iter))
6566
new_tol = clamp* alg.tol_factor / sqrt(iter), alg.tol_min, alg.tol_max)
6667
return _updatetol(alg.alg, new_tol)
6768
end

0 commit comments

Comments
 (0)