How parameter ‘conserve_qns=true’ works

I have been trying to calculate the dynamics of bosons recently. And I need to represent the ground state and excited state of bosons separately through appropriate matching. For example, a simple example:

``````L = 2
tₜₒₜₐₗ = 10
τ = 0.01
tₛₜₑₚₛ = convert(Int64, tₜₒₜₐₗ /τ)
sites = siteinds("Boson",2L)
states = ["1","0","0","1"]
ψ₀ = MPS(sites,states)

os = OpSum()
for i in 1:2L
os += "a†",i
os += "a",i
end
ℋ = MPO(os,sites)
ψₜ = ψ₀
N̂ₛₜₑₚₛ = []
for i in 1:tₛₜₑₚₛ
N̂ = expect(ψₜ ,"N")
push!(N̂ₛₜₑₚₛ ,N̂')
ψₜ = tdvp(ℋ,ψₜ ,-im*τ,ishermitian=false; nsite=2)
end
``````

This example shows that we have a total of two bosons, and using siteinds, we set the first two sites to represent the ground state of the boson, and the last two sites to represent the excited state of the boson. In this way, my initial state is set as the first boson in the ground state and the second boson in the excited state. When I performed time-dependent evolution on it, the particle population image obtained was as I expected.

But when I changed ‘siteinds’ to

``````sites = siteinds("Boson",2L; conserve_qns=true)
``````

Instead, there is a situation where the number of bosons is not conserved

So my question is, according to my settings, how can I ensure that the number of bosons is conserved?
Because when I was calculating other Hamiltonians, I encountered a situation where the expected sum of the number of particles at all sites was not conserved. In this case, an error will be reported

``````Eigen currently only supports block diagonal matrices.

Stacktrace:
[1] error(s::String)
@ Base .\error.jl:35
[2] eigen(T::Hermitian{ComplexF64, NDTensors.BlockSparseTensor{ComplexF64, 2, Tuple{Index{Vector{Pair{QN, Int64}}}, Index{Vector{Pair{QN, Int64}}}}, NDTensors.BlockSparse{ComplexF64, Vector{ComplexF64}, 2}}}; min_blockdim::Nothing, mindim::Int64, maxdim::Int64, cutoff::Float64, use_absolute_cutoff::Nothing, use_relative_cutoff::Nothing)
@ NDTensors C:\Users\15935\.julia\packages\NDTensors\RDn3T\src\blocksparse\linearalgebra.jl:227
[3] eigen(A::ITensor, Linds::Vector{Index{Vector{Pair{QN, Int64}}}}, Rinds::Vector{Index{Vector{Pair{QN, Int64}}}}; mindim::Int64, maxdim::Int64, cutoff::Float64, use_absolute_cutoff::Nothing, use_relative_cutoff::Nothing, ishermitian::Bool, tags::TagSet, lefttags::Nothing, righttags::Nothing, plev::Nothing, leftplev::Nothing, rightplev::Nothing)
@ ITensors C:\Users\15935\.julia\packages\ITensors\MnaxI\src\tensor_operations\matrix_decomposition.jl:378
[4] factorize_eigen(A::ITensor, Linds::Tuple{Index{Vector{Pair{QN, Int64}}}, Index{Vector{Pair{QN, Int64}}}}; ortho::String, eigen_perturbation::Nothing, mindim::Int64, maxdim::Int64, cutoff::Float64, tags::TagSet)
@ ITensors C:\Users\15935\.julia\packages\ITensors\MnaxI\src\tensor_operations\matrix_decomposition.jl:672
[5] factorize(A::ITensor, Linds::Tuple{Index{Vector{Pair{QN, Int64}}}, Index{Vector{Pair{QN, Int64}}}}; mindim::Int64, maxdim::Int64, cutoff::Float64, ortho::String, tags::TagSet, plev::Nothing, which_decomp::Nothing, eigen_perturbation::Nothing, svd_alg::String, use_absolute_cutoff::Nothing, use_relative_cutoff::Nothing, min_blockdim::Nothing, singular_values!::Nothing, dir::Nothing)
@ ITensors C:\Users\15935\.julia\packages\ITensors\MnaxI\src\tensor_operations\matrix_decomposition.jl:790
[6] replacebond!(M::MPS, b::Int64, phi::ITensor; normalize::Bool, swapsites::Nothing, ortho::String, which_decomp::Nothing, mindim::Int64, maxdim::Int64, cutoff::Float64, eigen_perturbation::Nothing, svd_alg::String)
@ ITensors C:\Users\15935\.julia\packages\ITensors\MnaxI\src\mps\mps.jl:555
[7] tdvp_site_update!(nsite_val::Val{2}, reverse_step_val::Val{true}, solver::ITensorTDVP.var"#solver#34"{ITensorTDVP.var"#solver#33#35"{Base.Pairs{Symbol, Real, NTuple{6, Symbol}, NamedTuple{(:ishermitian, :normalize, :sweeps, :maxdim, :cutoff, :nsite), Tuple{Bool, Bool, Int64, Int64, Float64, Int64}}}}}, PH::ProjMPO, psi::MPS, b::Int64; current_time::Float64, outputlevel::Int64, time_step::ComplexF64, normalize::Bool, direction::Base.Order.ForwardOrdering, noise::Float64, which_decomp::Nothing, svd_alg::String, cutoff::Float64, maxdim::Int64, mindim::Int64, maxtruncerr::Float64)
@ ITensorTDVP C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_step.jl:365
[8] tdvp_site_update!(solver::ITensorTDVP.var"#solver#34"{ITensorTDVP.var"#solver#33#35"{Base.Pairs{Symbol, Real, NTuple{6, Symbol}, NamedTuple{(:ishermitian, :normalize, :sweeps, :maxdim, :cutoff, :nsite), Tuple{Bool, Bool, Int64, Int64, Float64, Int64}}}}}, PH::ProjMPO, psi::MPS, b::Int64; nsite::Int64, reverse_step::Bool, current_time::Float64, outputlevel::Int64, time_step::ComplexF64, normalize::Bool, direction::Base.Order.ForwardOrdering, noise::Float64, which_decomp::Nothing, svd_alg::String, cutoff::Float64, maxdim::Int64, mindim::Int64, maxtruncerr::Float64)
@ ITensorTDVP C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_step.jl:157
[9] tdvp_sweep(direction::Base.Order.ForwardOrdering, solver::Function, PH::ProjMPO, time_step::ComplexF64, psi::MPS; kwargs::Base.Pairs{Symbol, Real, NTuple{11, Symbol}, NamedTuple{(:current_time, :ishermitian, :normalize, :sweeps, :maxdim, :cutoff, :nsite, :reverse_step, :sweep, :mindim, :noise), Tuple{Float64, Bool, Bool, Int64, Int64, Float64, Int64, Bool, Int64, Int64, Float64}}})
@ ITensorTDVP C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_step.jl:80
[10] tdvp_step(order::ITensorTDVP.TDVPOrder{2, Base.Order.ForwardOrdering()}, solver::Function, PH::ProjMPO, time_step::ComplexF64, psi::MPS; current_time::Float64, kwargs::Base.Pairs{Symbol, Real, NTuple{10, Symbol}, NamedTuple{(:ishermitian, :normalize, :sweeps, :maxdim, :cutoff, :nsite, :reverse_step, :sweep, :mindim, :noise), Tuple{Bool, Bool, Int64, Int64, Float64, Int64, Bool, Int64, Int64, Float64}}})
@ ITensorTDVP C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_step.jl:9
[11] macro expansion
@ C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_generic.jl:84 [inlined]
[12] macro expansion
@ .\timing.jl:393 [inlined]
[13] tdvp(solver::Function, PH::ProjMPO, t::ComplexF64, psi0::MPS; kwargs::Base.Pairs{Symbol, Real, NTuple{6, Symbol}, NamedTuple{(:ishermitian, :normalize, :sweeps, :maxdim, :cutoff, :nsite), Tuple{Bool, Bool, Int64, Int64, Float64, Int64}}})
@ ITensorTDVP C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_generic.jl:83
[14] tdvp
@ C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_generic.jl:45 [inlined]
[15] tdvp(solver::Function, H::MPO, t::ComplexF64, psi0::MPS; kwargs::Base.Pairs{Symbol, Real, NTuple{6, Symbol}, NamedTuple{(:ishermitian, :normalize, :sweeps, :maxdim, :cutoff, :nsite), Tuple{Bool, Bool, Int64, Int64, Float64, Int64}}})
@ ITensorTDVP C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_generic.jl:150
[16] tdvp
@ C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp_generic.jl:143 [inlined]
[17] #tdvp#41
@ C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp.jl:47 [inlined]
[18] tdvp
@ C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp.jl:46 [inlined]
[19] #tdvp#43
@ C:\Users\15935\.julia\packages\ITensorTDVP\V1Kco\src\tdvp.jl:55 [inlined]
[20] macro expansion
@ .\In[63]:10 [inlined]
[21] top-level scope
@ .\timing.jl:273 [inlined]
[22] top-level scope
@ .\In[63]:0
``````

I am very much looking forward to receiving any form of help, and would greatly appreciate it if there is one！

From what I can tell, it looks like your Hamiltonian is just not particle number conserving. So then the dynamics of it will not conserve particle number and trying to use `conserve_qns=true` should cause an error, which is preventing you from trying this since it cannot work. If it did let you use it without error, then that just may be a special case where the code did not catch it. Hope that’s helpful –

Yes, after discussing with others, I found that there was indeed an issue with the setting of my Hamiltonian, not with the ITensors program. No problem with what you said, thank you very much for your quick and accurate reply!

1 Like