Skip to content

Commit ae978f4

Browse files
authored
Add support for non-sorted and overlapping indices in MPOHamiltonian constructors (#322)
* Support swap gate for MPOs * support for non-sorted indices * handle repeated indices * update docstrings * Add tests repeated indices
1 parent f54c85c commit ae978f4

File tree

3 files changed

+118
-5
lines changed

3 files changed

+118
-5
lines changed

src/operators/mpo.jl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,3 +390,53 @@ function Base.isapprox(
390390
# don't take square roots to avoid precision loss
391391
return norm₁₂² max(atol^2, rtol^2 * max(norm₁², norm₂²))
392392
end
393+
394+
@doc """
395+
swap(mpo::FiniteMPO, i::Integer; inv::Bool=false, alg=SDD(), trscheme)
396+
swap!(mpo::FiniteMPO, i::Integer; inv::Bool=false, alg=SDD(), trscheme)
397+
398+
Compose the mpo with a swap gate applied to indices `i` and `i + 1`, effectively creating an
399+
operator that acts on the Hilbert spaces with those factors swapped.
400+
The keyword arguments `alg` and `trscheme` can be used to control how the resulting tensor
401+
is truncated again.
402+
""" swap, swap!
403+
404+
swap(mpo::FiniteMPO, i::Integer; kwargs...) = swap!(copy(mpo), i; kwargs...)
405+
function swap!(
406+
mpo::FiniteMPO{<:MPOTensor}, i::Integer;
407+
inv::Bool = false,
408+
alg = SDD(), trscheme = truncbelow(eps(real(scalartype(mpo)))^(4 / 5))
409+
)
410+
O₁, O₂ = mpo[i], mpo[i + 1]
411+
412+
if inv
413+
@plansor O₂₁[-1 -2 -3; -4 -5 -6] :=
414+
τ'[-3 -6; 4 5] * O₁[-2 4; 2 1] * O₂[1 5; 3 -5] * τ[2 3; -1 -4]
415+
else
416+
@plansor O₂₁[-1 -2 -3; -4 -5 -6] :=
417+
τ[-3 -6; 4 5] * O₁[-2 4; 2 1] * O₂[1 5; 3 -5] * τ'[2 3; -1 -4]
418+
end
419+
420+
U, S, Vᴴ, = tsvd!(O₂₁; alg, trunc = trscheme)
421+
sqrtS = sqrt(S)
422+
@plansor mpo[i][-1 -2; -3 -4] := U[-3 -1 -2; 1] * sqrtS[1; -4]
423+
@plansor mpo[i + 1][-1 -2; -3 -4] := sqrtS[-1; 1] * Vᴴ[1; -3 -4 -2]
424+
return mpo
425+
end
426+
427+
@doc """
428+
multiply_neighbours(mpo::FiniteMPO, i::Integer)
429+
multiply_neighbours!(mpo::FiniteMPO, i::Integer)
430+
431+
Construct the mpo of length `length(mpo) - 1` which is formed by multiplying the operators
432+
on site `i` and `i + 1`.
433+
""" multiply_neighbours, multiply_neighbours!
434+
435+
multiply_neighbours(mpo::FiniteMPO, i::Integer) = multiply_neighbours!(copy(mpo), i)
436+
function multiply_neighbours!(mpo::FiniteMPO{<:MPOTensor}, i::Integer)
437+
1 <= i < length(mpo) || throw(BoundsError(mpo, i))
438+
O₁ = mpo[i]
439+
O₂ = popat!(parent(mpo), i + 1)
440+
@plansor mpo[i][-1 -2; -3 -4] := O₁[-1 -2; 1 2] * τ[1 2; 3 4] * O₂[3 4; -3 -4]
441+
return mpo
442+
end

src/operators/mpohamiltonian.jl

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,21 @@ function instantiate_operator(state::AbstractMPS, O::Pair)
309309
return instantiate_operator(physicalspace(state), O)
310310
end
311311
function instantiate_operator(lattice::AbstractArray{<:VectorSpace}, (inds′, O)::Pair)
312-
inds = inds′ isa Int ? tuple(inds′) : inds′
313-
mpo = O isa FiniteMPO ? O : FiniteMPO(O)
312+
inds = inds′ isa Int ? [inds′] : inds′
313+
mpo = O isa FiniteMPO ? copy(O) : FiniteMPO(O)
314314

315315
# convert to linear index type
316-
operators = parent(mpo)
317-
indices = map(inds) do I
318-
return Base._to_linear_index(lattice, Tuple(I)...) # this should mean all inds are valid...
316+
indices = Vector{Int}(undef, length(inds))
317+
for i in eachindex(indices)
318+
indices[i] = Base._to_linear_index(lattice, Tuple(inds[i])...) # this should mean all inds are valid...
319319
end
320+
321+
# sort indices and deduplicate
322+
indices, mpo = canonicalize_indices!(indices, mpo)
323+
operators = parent(mpo)
324+
325+
@assert allunique(indices) && issorted(indices) "From here on we require unique and ascending indices\n$indices"
326+
320327
T = eltype(mpo)
321328
local_mpo = Union{T, scalartype(T)}[]
322329
sites = Int[]
@@ -339,6 +346,25 @@ function instantiate_operator(lattice::AbstractArray{<:VectorSpace}, (inds′, O
339346
return sites => local_mpo
340347
end
341348

349+
function canonicalize_indices!(indices, mpo)
350+
# swap non-sorted entries
351+
for i in 2:length(indices)
352+
for j in reverse(i:length(indices))
353+
if indices[j] < indices[j - 1]
354+
swap!(mpo, j - 1)
355+
indices[j - 1], indices[j] = indices[j], indices[j - 1]
356+
end
357+
end
358+
end
359+
for i in length(indices):-1:2
360+
if indices[i] == indices[i - 1]
361+
multiply_neighbours!(mpo, i - 1)
362+
popat!(indices, i)
363+
end
364+
end
365+
return indices, mpo
366+
end
367+
342368
# yields the promoted tensortype of all tensors
343369
function _find_tensortype(nonzero_operators::AbstractArray)
344370
return mapreduce(promote_type, nonzero_operators) do x

test/operators.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,43 @@ module TestOperators
218218
end
219219
end
220220

221+
@testset "Finite MPOHamiltonian repeated indices" begin
222+
X = randn(ComplexF64, ℂ^2, ℂ^2)
223+
X += X'
224+
Y = randn(ComplexF64, ℂ^2, ℂ^2)
225+
Y += Y'
226+
L = 4
227+
chain = fill(space(X, 1), 4)
228+
229+
H1 = FiniteMPOHamiltonian(chain, (1,) => (X * X * Y * Y))
230+
H2 = FiniteMPOHamiltonian(chain, (1, 1, 1, 1) => (X X Y Y))
231+
@test convert(TensorMap, H1) convert(TensorMap, H2)
232+
233+
H1 = FiniteMPOHamiltonian(chain, (1, 2) => ((X * Y) (X * Y)))
234+
H2 = FiniteMPOHamiltonian(chain, (1, 2, 1, 2) => (X X Y Y))
235+
@test convert(TensorMap, H1) convert(TensorMap, H2)
236+
237+
H1 = FiniteMPOHamiltonian(chain, (1, 2) => ((X * X * Y) Y))
238+
H2 = FiniteMPOHamiltonian(chain, (1, 1, 1, 2) => (X X Y Y))
239+
@test convert(TensorMap, H1) convert(TensorMap, H2)
240+
241+
H1 = FiniteMPOHamiltonian(chain, (1, 2) => ((X * Y * Y) X))
242+
H2 = FiniteMPOHamiltonian(chain, (1, 2, 1, 1) => (X X Y Y))
243+
@test convert(TensorMap, H1) convert(TensorMap, H2)
244+
245+
H1 = FiniteMPOHamiltonian(chain, (1, 2, 3) => FiniteMPO((X * X) Y Y))
246+
H2 = FiniteMPOHamiltonian(chain, (1, 1, 2, 3) => FiniteMPO(X X Y Y))
247+
@test convert(TensorMap, H1) convert(TensorMap, H2)
248+
249+
H1 = FiniteMPOHamiltonian(chain, (1, 2, 3) => FiniteMPO((Y * Y) X X))
250+
H2 = FiniteMPOHamiltonian(chain, (2, 3, 1, 1) => FiniteMPO(X X Y Y))
251+
@test convert(TensorMap, H1) convert(TensorMap, H2)
252+
253+
H1 = FiniteMPOHamiltonian(chain, (1, 2, 3) => FiniteMPO(X (X * Y) Y))
254+
H2 = FiniteMPOHamiltonian(chain, (1, 2, 2, 3) => FiniteMPO(X X Y Y))
255+
@test convert(TensorMap, H1) convert(TensorMap, H2)
256+
end
257+
221258
@testset "InfiniteMPOHamiltonian $(sectortype(pspace))" for (pspace, Dspace) in zip(pspaces, vspaces)
222259
# generate a 1-2-3 body interaction
223260
operators = ntuple(3) do i

0 commit comments

Comments
 (0)