Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/operators/mpo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -390,3 +390,53 @@ function Base.isapprox(
# don't take square roots to avoid precision loss
return norm₁₂² ≤ max(atol^2, rtol^2 * max(norm₁², norm₂²))
end

@doc """
swap(mpo::FiniteMPO, i::Integer; inv::Bool=false, alg=SDD(), trscheme)
swap!(mpo::FiniteMPO, i::Integer; inv::Bool=false, alg=SDD(), trscheme)

Compose the mpo with a swap gate applied to indices `i` and `i + 1`, effectively creating an
operator that acts on the Hilbert spaces with those factors swapped.
The keyword arguments `alg` and `trscheme` can be used to control how the resulting tensor
is truncated again.
""" swap, swap!

swap(mpo::FiniteMPO, i::Integer; kwargs...) = swap!(copy(mpo), i; kwargs...)
function swap!(
mpo::FiniteMPO{<:MPOTensor}, i::Integer;
inv::Bool = false,
alg = SDD(), trscheme = truncbelow(eps(real(scalartype(mpo)))^(4 / 5))
)
O₁, O₂ = mpo[i], mpo[i + 1]

if inv
@plansor O₂₁[-1 -2 -3; -4 -5 -6] :=
τ'[-3 -6; 4 5] * O₁[-2 4; 2 1] * O₂[1 5; 3 -5] * τ[2 3; -1 -4]
else
@plansor O₂₁[-1 -2 -3; -4 -5 -6] :=
τ[-3 -6; 4 5] * O₁[-2 4; 2 1] * O₂[1 5; 3 -5] * τ'[2 3; -1 -4]
end

U, S, Vᴴ, = tsvd!(O₂₁; alg, trunc = trscheme)
sqrtS = sqrt(S)
@plansor mpo[i][-1 -2; -3 -4] := U[-3 -1 -2; 1] * sqrtS[1; -4]
@plansor mpo[i + 1][-1 -2; -3 -4] := sqrtS[-1; 1] * Vᴴ[1; -3 -4 -2]
return mpo
end

@doc """
multiply_neighbours(mpo::FiniteMPO, i::Integer)
multiply_neighbours!(mpo::FiniteMPO, i::Integer)

Construct the mpo of length `length(mpo) - 1` which is formed by multiplying the operators
on site `i` and `i + 1`.
""" multiply_neighbours, multiply_neighbours!

multiply_neighbours(mpo::FiniteMPO, i::Integer) = multiply_neighbours!(copy(mpo), i)
function multiply_neighbours!(mpo::FiniteMPO{<:MPOTensor}, i::Integer)
1 <= i < length(mpo) || throw(BoundsError(mpo, i))
O₁ = mpo[i]
O₂ = popat!(parent(mpo), i + 1)
@plansor mpo[i][-1 -2; -3 -4] := O₁[-1 -2; 1 2] * τ[1 2; 3 4] * O₂[3 4; -3 -4]
return mpo
end
36 changes: 31 additions & 5 deletions src/operators/mpohamiltonian.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,21 @@ function instantiate_operator(state::AbstractMPS, O::Pair)
return instantiate_operator(physicalspace(state), O)
end
function instantiate_operator(lattice::AbstractArray{<:VectorSpace}, (inds′, O)::Pair)
inds = inds′ isa Int ? tuple(inds′) : inds′
mpo = O isa FiniteMPO ? O : FiniteMPO(O)
inds = inds′ isa Int ? [inds′] : inds′
mpo = O isa FiniteMPO ? copy(O) : FiniteMPO(O)

# convert to linear index type
operators = parent(mpo)
indices = map(inds) do I
return Base._to_linear_index(lattice, Tuple(I)...) # this should mean all inds are valid...
indices = Vector{Int}(undef, length(inds))
for i in eachindex(indices)
indices[i] = Base._to_linear_index(lattice, Tuple(inds[i])...) # this should mean all inds are valid...
end

# sort indices and deduplicate
indices, mpo = canonicalize_indices!(indices, mpo)
operators = parent(mpo)

@assert allunique(indices) && issorted(indices) "From here on we require unique and ascending indices\n$indices"

T = eltype(mpo)
local_mpo = Union{T, scalartype(T)}[]
sites = Int[]
Expand All @@ -339,6 +346,25 @@ function instantiate_operator(lattice::AbstractArray{<:VectorSpace}, (inds′, O
return sites => local_mpo
end

function canonicalize_indices!(indices, mpo)
# swap non-sorted entries
for i in 2:length(indices)
for j in reverse(i:length(indices))
if indices[j] < indices[j - 1]
swap!(mpo, j - 1)
indices[j - 1], indices[j] = indices[j], indices[j - 1]
end
end
end
for i in length(indices):-1:2
if indices[i] == indices[i - 1]
multiply_neighbours!(mpo, i - 1)
popat!(indices, i)
end
end
return indices, mpo
end

# yields the promoted tensortype of all tensors
function _find_tensortype(nonzero_operators::AbstractArray)
return mapreduce(promote_type, nonzero_operators) do x
Expand Down
37 changes: 37 additions & 0 deletions test/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,43 @@ module TestOperators
end
end

@testset "Finite MPOHamiltonian repeated indices" begin
X = randn(ComplexF64, ℂ^2, ℂ^2)
X += X'
Y = randn(ComplexF64, ℂ^2, ℂ^2)
Y += Y'
L = 4
chain = fill(space(X, 1), 4)

H1 = FiniteMPOHamiltonian(chain, (1,) => (X * X * Y * Y))
H2 = FiniteMPOHamiltonian(chain, (1, 1, 1, 1) => (X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)

H1 = FiniteMPOHamiltonian(chain, (1, 2) => ((X * Y) ⊗ (X * Y)))
H2 = FiniteMPOHamiltonian(chain, (1, 2, 1, 2) => (X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)

H1 = FiniteMPOHamiltonian(chain, (1, 2) => ((X * X * Y) ⊗ Y))
H2 = FiniteMPOHamiltonian(chain, (1, 1, 1, 2) => (X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)

H1 = FiniteMPOHamiltonian(chain, (1, 2) => ((X * Y * Y) ⊗ X))
H2 = FiniteMPOHamiltonian(chain, (1, 2, 1, 1) => (X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)

H1 = FiniteMPOHamiltonian(chain, (1, 2, 3) => FiniteMPO((X * X) ⊗ Y ⊗ Y))
H2 = FiniteMPOHamiltonian(chain, (1, 1, 2, 3) => FiniteMPO(X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)

H1 = FiniteMPOHamiltonian(chain, (1, 2, 3) => FiniteMPO((Y * Y) ⊗ X ⊗ X))
H2 = FiniteMPOHamiltonian(chain, (2, 3, 1, 1) => FiniteMPO(X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)

H1 = FiniteMPOHamiltonian(chain, (1, 2, 3) => FiniteMPO(X ⊗ (X * Y) ⊗ Y))
H2 = FiniteMPOHamiltonian(chain, (1, 2, 2, 3) => FiniteMPO(X ⊗ X ⊗ Y ⊗ Y))
@test convert(TensorMap, H1) ≈ convert(TensorMap, H2)
end

@testset "InfiniteMPOHamiltonian $(sectortype(pspace))" for (pspace, Dspace) in zip(pspaces, vspaces)
# generate a 1-2-3 body interaction
operators = ntuple(3) do i
Expand Down
Loading