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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
[compat]
Dictionaries = "0.4.6"
FunctionWrappers = "1.1.3"
ITensorMPS = "0.1, 0.2, 0.3"
ITensorMPS = "0.1, 0.2, 0.3, 0.4"
ITensors = "0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9"
LinearAlgebra = "1.9, 1.10"
Printf = "1"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/documentation/MPO_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ The main exported function is `MPO_new`, which takes an `OpSum` and transforms i

1. The operator must be expressed as a sum of products of single-site operators. For example, a CNOT could not appear in the sum since it is a two-site operator.

2. When dealing with fermionic systems, the parity of each term in the sum must be even. That is, the combined number of creation and annihilation operators in each term must be even. It should be possible to relax this constraint.
2. When dealing with fermionic systems, every term in the sum must have the same fermion parity. The common parity may be even or odd, but even- and odd-parity terms cannot be mixed in a single sum.

3. Each term in the sum-of-products representation can only have a single operator acting on any one site. For example, a term such as ``\mathbf{X}^{(1)} \mathbf{X}^{(1)}`` is not allowed. However, there is a preprocessing utility that can automatically replace ``\mathbf{X}^{(1)} \mathbf{X}^{(1)}`` with ``\mathbf{I}^{(1)}``.

Expand Down
3 changes: 1 addition & 2 deletions examples/electronic-structure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,9 @@ for alg in ("VC", "QR")
sites;
alg,
basis_op_cache_vec=os.op_cache_vec,
splitblocks=true,
check_for_errors=false,
checkflux=false,
) # TODO: remove splitblocks=true after warning
)
N > 5 && print_timer()

percent_sparse = round(100 * sparsity(H); digits=2)
Expand Down
11 changes: 1 addition & 10 deletions src/MPOConstruction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,10 @@ function MPO_new(
basis_op_cache_vec=nothing,
check_for_errors::Bool=true,
checkflux::Bool=true,
splitblocks::Union{Bool,Nothing}=nothing,
splitblocks::Bool=true,
output_level::Int=0,
kwargs...,
)::MPO
if isnothing(splitblocks) # TODO: Remove warning some time after v0.2.1 release
splitblocks = true
hasqns(sites) && Base.depwarn(
"`splitblocks` not specified. The default is `true`, which is a change from prior behavior.",
:MPO_new;
force=true,
)
end

prepare_opID_sum!(os, to_OpCacheVec(sites, basis_op_cache_vec))
check_for_errors && check_os_for_errors(os)

Expand Down
27 changes: 20 additions & 7 deletions src/OpIDSum.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ function OpInfo(local_op::Op, site::Index)
)
end

# TODO: Create a symbolic sum.

"""
OpCacheVec

Expand Down Expand Up @@ -578,22 +576,25 @@ Validate structural assumptions for `os`.

This checks that the cached local operators are linearly independent on every
site and that, within every term, operators are sorted by site, at most one
operator acts on each site, all terms carry the same total QN flux, and every
term has even fermion parity.
operator acts on each site, and all terms carry the same total QN flux and
fermion parity. The common fermion parity may be even or odd.
"""
@timeit function check_os_for_errors(os::OpIDSum)::Nothing
flux_of_first_term = nothing
parity_of_first_term = nothing
for i in eachindex(os)
_, ops = os[i]

flux = QN()
fermion_parity = 0
fermion_parity = false
for j in eachindex(ops)
opj = ops[j]
opj == zero(opj) && continue

flux += os.op_cache_vec[opj.n][opj.id].qn_flux
fermion_parity += os.op_cache_vec[opj.n][opj.id].is_fermionic
fermion_parity = xor(
fermion_parity, os.op_cache_vec[opj.n][opj.id].is_fermionic
)

if j < length(ops)
ops[j + 1] == zero(ops[j + 1]) && continue
Expand All @@ -613,7 +614,19 @@ term has even fermion parity.
)
end

mod(fermion_parity, 2) != 0 && error("Odd parity fermion terms not supported: $ops")
if isnothing(parity_of_first_term)
parity_of_first_term = fermion_parity
else
if fermion_parity != parity_of_first_term
first_parity_name = parity_of_first_term ? "odd" : "even"
current_parity_name = fermion_parity ? "odd" : "even"
error(
"Inconsistent fermion parity found!\n" *
" Term 1 has $first_parity_name fermion parity\n" *
" Term $i has $current_parity_name fermion parity",
)
end
end
end

check_op_cache_vec_linearly_independent(os.op_cache_vec)
Expand Down
2 changes: 1 addition & 1 deletion test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[compat]
Dictionaries = "0.4.6"
Graphs = "1.14.0"
ITensorMPS = "0.1, 0.2, 0.3"
ITensorMPS = "0.1, 0.2, 0.3, 0.4"
ITensors = "0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9"
LinearAlgebra = "1.9, 1.10"
Random = "1"
2 changes: 1 addition & 1 deletion test/mponew_h1_accuracy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ end
@test maximum(abs, H .- transpose(H)) <= 1e-12

sites = siteinds("Fermion", N; conserve_qns=true, conserve_nf=true)
H_new = MPO_new(build_h1_opsum(terms), sites; tol=1.0, splitblocks=true) # TODO: remove splitblocks=true after removing warning
H_new = MPO_new(build_h1_opsum(terms), sites; tol=1.0)

tests = make_test_orbitals(N; n_basis=8, n_random=4, seed=1234)
max_delta = 0.0
Expand Down
24 changes: 24 additions & 0 deletions test/ops-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,28 @@ function test_check_os_for_errors_rejects_linearly_dependent_cache()::Nothing
return nothing
end

function test_check_os_for_errors_fermion_parity()::Nothing
sites = siteinds("Fermion", 3)
op_cache_vec =
to_OpCacheVec(sites, [["I", "C", "Cdag", "N"] for _ in eachindex(sites)])

C(n) = OpID(2, n)
Cdag(n) = OpID(3, n)
N(n) = OpID(4, n)

odd_os = OpIDSum{3,Float64,Int}(2, op_cache_vec)
add!(odd_os, 1.0, C(1))
add!(odd_os, 1.0, Cdag(1), C(2), C(3))
@test isnothing(check_os_for_errors(odd_os))

mixed_os = OpIDSum{2,Float64,Int}(2, op_cache_vec)
add!(mixed_os, 1.0, C(1))
add!(mixed_os, 1.0, N(2))
@test_throws "Inconsistent fermion parity found!" check_os_for_errors(mixed_os)

return nothing
end

@testset "Ops" begin
test_are_equal()

Expand All @@ -201,4 +223,6 @@ end
test_rewrite_in_operator_basis_zero()

test_check_os_for_errors_rejects_linearly_dependent_cache()

test_check_os_for_errors_fermion_parity()
end
39 changes: 37 additions & 2 deletions test/test-MPOConstruction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ function test_non_zero_flux(alg::String)::Nothing
os = OpSum()
os .+= 1.0, "S-", 1, "S-", 4
os .+= 1.0, "S-", 2, "S-", 3
O = MPO_new(os, sites; alg, splitblocks=true) # TODO: remove splitblocks = true after warning.
O = MPO_new(os, sites; alg)

@test flux(O) == QN(("Number", 2))

Expand All @@ -265,7 +265,7 @@ function test_non_zero_flux(alg::String)::Nothing
os = OpSum()
os .+= 1.0, "S-", 1
os .+= 1.0, "S-", 2, "S-", 3
@test_throws "Inconsistent flux found!" MPO_new(os, sites; alg, splitblocks=true) # TODO: remove splitblocks = true after warning.
@test_throws "Inconsistent flux found!" MPO_new(os, sites; alg)
end

return nothing
Expand Down Expand Up @@ -348,6 +348,36 @@ function test_Fermi_Hubbard(
return nothing
end

function test_odd_fermion_parity(alg::String, conserve_qns::Bool)::Nothing
sites = siteinds("Fermion", 5; conserve_qns)

os = OpSum{Float64}()
os .+= 0.7, "Cdag", 1
os .+= -1.2, "Cdag", 3
os .+= 0.4, "Cdag", 5
os .+= 0.9, "Cdag", 1, "Cdag", 3, "C", 5

mpo = MPO_new(os, sites; alg)

I(n) = op("I", sites[n])
F(n) = op("F", sites[n])
C(n) = op("C", sites[n])
Cdag(n) = op("Cdag", sites[n])

exact = 0.7 * Cdag(1) * I(2) * I(3) * I(4) * I(5)
exact += -1.2 * F(1) * F(2) * Cdag(3) * I(4) * I(5)
exact += 0.4 * F(1) * F(2) * F(3) * F(4) * Cdag(5)
exact += 0.9 * Cdag(1) * I(2) * op("Cdag * F", sites[3]) * F(4) * C(5)

@test norm(prod(mpo) - exact) < 1e-12
if conserve_qns
@test flux(mpo) == flux(Cdag(1))
@test !iszero(flux(mpo))
end

return nothing
end

ITensors.op(::OpName"00", ::SiteType"Qubit") = [1 0; 0 0]

ITensors.op(::OpName"01", ::SiteType"Qubit") = [0 1; 0 0]
Expand Down Expand Up @@ -409,6 +439,11 @@ end

@testset "$alg: non zero flux" test_non_zero_flux(alg)

@testset "$alg: odd fermion parity" begin
test_odd_fermion_parity(alg, false)
test_odd_fermion_parity(alg, true)
end

@testset "$alg: qft" begin
test_qft(6, false, alg)
test_qft(6, true, alg)
Expand Down
Loading