diff --git a/.github/workflows/Format.yml b/.github/workflows/Format.yml deleted file mode 100644 index a1c8cf4f1..000000000 --- a/.github/workflows/Format.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Format suggestions -on: - pull_request: -jobs: - code-style: - runs-on: ubuntu-latest - steps: - - uses: julia-actions/julia-format@v4 \ No newline at end of file diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index 34b40bf3f..40aaca14f 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -3,30 +3,15 @@ name: FormatCheck on: push: branches: + - 'main' - 'master' tags: '*' pull_request: branches: + - 'main' - 'master' -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - version: - - '1' # automatically expands to the latest stable 1.x release of Julia - os: - - ubuntu-latest - arch: - - x64 - steps: - - uses: julia-actions/setup-julia@latest - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: actions/checkout@v4 - - name: Install JuliaFormatter and format - run: | - julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter"))' - julia -e 'using JuliaFormatter; format(".", verbose=true)' +jobs: + formatcheck: + name: "Format Check" + uses: "QuantumKitHub/QuantumKitHubActions/.github/workflows/FormatCheck.yml@main" diff --git a/docs/make.jl b/docs/make.jl index e899f245d..6bbfcc67c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,7 +2,7 @@ if Base.active_project() != joinpath(@__DIR__, "Project.toml") using Pkg Pkg.activate(@__DIR__) - Pkg.develop(PackageSpec(; path=(@__DIR__) * "/../")) + Pkg.develop(PackageSpec(; path = (@__DIR__) * "/../")) Pkg.resolve() Pkg.instantiate() end @@ -23,41 +23,55 @@ end # bibliography bibpath = joinpath(@__DIR__, "src", "assets", "mpskit.bib") -bib = CitationBibliography(bibpath; style=:authoryear) +bib = CitationBibliography(bibpath; style = :authoryear) # interlinks -links = InterLinks("TensorKit" => "https://jutho.github.io/TensorKit.jl/stable/", - "TensorOperations" => "https://jutho.github.io/TensorOperations.jl/stable/", - "KrylovKit" => "https://jutho.github.io/KrylovKit.jl/stable/", - "BlockTensorKit" => "https://lkdvos.github.io/BlockTensorKit.jl/dev/") +links = InterLinks( + "TensorKit" => "https://jutho.github.io/TensorKit.jl/stable/", + "TensorOperations" => "https://jutho.github.io/TensorOperations.jl/stable/", + "KrylovKit" => "https://jutho.github.io/KrylovKit.jl/stable/", + "BlockTensorKit" => "https://lkdvos.github.io/BlockTensorKit.jl/dev/" +) # include MPSKit in all doctests -DocMeta.setdocmeta!(MPSKit, :DocTestSetup, :(using MPSKit, TensorKit); recursive=true) +DocMeta.setdocmeta!(MPSKit, :DocTestSetup, :(using MPSKit, TensorKit); recursive = true) -mathengine = MathJax3(Dict(:loader => Dict("load" => ["[tex]/physics"]), - :tex => Dict("inlineMath" => [["\$", "\$"], ["\\(", "\\)"]], - "tags" => "ams", - "packages" => ["base", "ams", "autoload", "physics"]))) +mathengine = MathJax3( + Dict( + :loader => Dict("load" => ["[tex]/physics"]), + :tex => Dict( + "inlineMath" => [["\$", "\$"], ["\\(", "\\)"]], + "tags" => "ams", + "packages" => ["base", "ams", "autoload", "physics"] + ) + ) +) makedocs(; - sitename="MPSKit.jl", - format=Documenter.HTML(; - prettyurls=true, - mathengine, - assets=["assets/custom.css"], - size_threshold=1024000,), - pages=["Home" => "index.md", - "Manual" => ["man/intro.md", - "man/states.md", - "man/operators.md", - "man/algorithms.md", - # "man/environments.md", - "man/parallelism.md", - "man/lattices.md"], - "Examples" => "examples/index.md", - "Library" => "lib/lib.md", - "References" => "references.md"], - checkdocs=:exports, - doctest=true, - plugins=[bib, links]) + sitename = "MPSKit.jl", + format = Documenter.HTML(; + prettyurls = true, + mathengine, + assets = ["assets/custom.css"], + size_threshold = 1024000, + ), + pages = [ + "Home" => "index.md", + "Manual" => [ + "man/intro.md", + "man/states.md", + "man/operators.md", + "man/algorithms.md", + # "man/environments.md", + "man/parallelism.md", + "man/lattices.md", + ], + "Examples" => "examples/index.md", + "Library" => "lib/lib.md", + "References" => "references.md", + ], + checkdocs = :exports, + doctest = true, + plugins = [bib, links] +) -deploydocs(; repo="github.com/QuantumKitHub/MPSKit.jl.git", push_preview=true) +deploydocs(; repo = "github.com/QuantumKitHub/MPSKit.jl.git", push_preview = true) diff --git a/examples/classic2d/1.hard-hexagon/main.jl b/examples/classic2d/1.hard-hexagon/main.jl index bc8466947..5001c3f9d 100644 --- a/examples/classic2d/1.hard-hexagon/main.jl +++ b/examples/classic2d/1.hard-hexagon/main.jl @@ -25,7 +25,7 @@ function virtual_space(D::Integer) return Vect[FibonacciAnyon](sector => _D for sector in (:I, :τ)) end -@assert isapprox(dim(virtual_space(100)), 100; atol=3) +@assert isapprox(dim(virtual_space(100)), 100; atol = 3) md""" ## The leading boundary @@ -39,10 +39,10 @@ Additionally, we can compute the entanglement entropy as well as the correlation D = 10 V = virtual_space(D) ψ₀ = InfiniteMPS([P], [V]) -ψ, envs, = leading_boundary(ψ₀, mpo, - VUMPS(; verbosity=0, - alg_eigsolve=MPSKit.Defaults.alg_eigsolve(; - ishermitian=false))) # use non-hermitian eigensolver +ψ, envs, = leading_boundary( + ψ₀, mpo, + VUMPS(; verbosity = 0, alg_eigsolve = MPSKit.Defaults.alg_eigsolve(; ishermitian = false)) +) # use non-hermitian eigensolver F = real(expectation_value(ψ, mpo)) S = real(first(entropy(ψ))) ξ = correlation_length(ψ) @@ -57,8 +57,10 @@ First we need to know the entropy and correlation length at a bunch of different According to the scaling hypothesis we should have ``S \propto \frac{c}{6} log(ξ)``. Therefore we should find ``c`` using """ -function scaling_simulations(ψ₀, mpo, Ds; verbosity=0, tol=1e-6, - alg_eigsolve=MPSKit.Defaults.alg_eigsolve(; ishermitian=false)) +function scaling_simulations( + ψ₀, mpo, Ds; verbosity = 0, tol = 1.0e-6, + alg_eigsolve = MPSKit.Defaults.alg_eigsolve(; ishermitian = false) + ) entropies = similar(Ds, Float64) correlations = similar(Ds, Float64) alg = VUMPS(; verbosity, tol, alg_eigsolve) @@ -68,7 +70,7 @@ function scaling_simulations(ψ₀, mpo, Ds; verbosity=0, tol=1e-6, correlations[1] = correlation_length(ψ) for (i, d) in enumerate(diff(Ds)) - ψ, envs = changebonds(ψ, mpo, OptimalExpand(; trscheme=truncdim(d)), envs) + ψ, envs = changebonds(ψ, mpo, OptimalExpand(; trscheme = truncdim(d)), envs) ψ, envs, = leading_boundary(ψ, mpo, alg, envs) entropies[i + 1] = real(entropy(ψ)[1]) correlations[i + 1] = correlation_length(ψ) @@ -83,8 +85,8 @@ Ss, ξs = scaling_simulations(ψ₀, mpo, bond_dimensions) f = fit(log.(ξs), 6 * Ss, 1) c = f.coeffs[2] -#+ +#+ -p = plot(; xlabel="logarithmic correlation length", ylabel="entanglement entropy") -p = plot(log.(ξs), Ss; seriestype=:scatter, label=nothing) -plot!(p, ξ -> f(ξ) / 6; label="fit") +p = plot(; xlabel = "logarithmic correlation length", ylabel = "entanglement entropy") +p = plot(log.(ξs), Ss; seriestype = :scatter, label = nothing) +plot!(p, ξ -> f(ξ) / 6; label = "fit") diff --git a/examples/make.jl b/examples/make.jl index 6cdab5780..d7242db42 100644 --- a/examples/make.jl +++ b/examples/make.jl @@ -2,7 +2,7 @@ if Base.active_project() != joinpath(@__DIR__, "Project.toml") using Pkg Pkg.activate(@__DIR__) - Pkg.develop(PackageSpec(; path=(@__DIR__) * "/../")) + Pkg.develop(PackageSpec(; path = (@__DIR__) * "/../")) Pkg.resolve() Pkg.instantiate() end @@ -17,13 +17,13 @@ using TOML, SHA const CACHEFILE = joinpath(@__DIR__, "Cache.toml") -getcache() = isfile(CACHEFILE) ? TOML.parsefile(CACHEFILE) : Dict{String,Any}() +getcache() = isfile(CACHEFILE) ? TOML.parsefile(CACHEFILE) : Dict{String, Any}() function iscached(root, name) cache = getcache() return haskey(cache, root) && - haskey(cache[root], name) && - cache[root][name] == checksum(root, name) + haskey(cache[root], name) && + cache[root][name] == checksum(root, name) end function setcached(root, name) @@ -31,7 +31,7 @@ function setcached(root, name) if haskey(cache, root) cache[root][name] = checksum(root, name) else - cache[root] = Dict{String,Any}(name => checksum(root, name)) + cache[root] = Dict{String, Any}(name => checksum(root, name)) end return open(f -> TOML.print(f, cache), CACHEFILE, "w") end @@ -68,19 +68,23 @@ function build_example(root, name) source_file = joinpath(source_dir, "main.jl") target_dir = joinpath(@__DIR__, "..", "docs", "src", "examples", root, name) - if !iscached(root, name) - Literate.markdown(source_file, target_dir; execute=true, name="index", - preprocess=attach_notebook_badge(root, name), mdstrings=true, - nbviewer_root_url="https://nbviewer.jupyter.org/github/QuantumKitHub/MPSKit.jl/blob/gh-pages/dev", - binder_root_url="https://mybinder.org/v2/gh/QuantumKitHub/MPSKit.jl/gh-pages?filepath=dev", - credits=false, - repo_root_url="https://github.com/QuantumKitHub/MPSKit.jl") - Literate.notebook(source_file, target_dir; execute=false, name="main", - preprocess=str -> replace(str, r"(? "\$"), - mdstrings=true, credits=false) + return if !iscached(root, name) + Literate.markdown( + source_file, target_dir; execute = true, name = "index", + preprocess = attach_notebook_badge(root, name), mdstrings = true, + nbviewer_root_url = "https://nbviewer.jupyter.org/github/QuantumKitHub/MPSKit.jl/blob/gh-pages/dev", + binder_root_url = "https://mybinder.org/v2/gh/QuantumKitHub/MPSKit.jl/gh-pages?filepath=dev", + credits = false, + repo_root_url = "https://github.com/QuantumKitHub/MPSKit.jl" + ) + Literate.notebook( + source_file, target_dir; execute = false, name = "main", + preprocess = str -> replace(str, r"(? "\$"), + mdstrings = true, credits = false + ) foreach(filter(!=("main.jl"), readdir(source_dir))) do f - return cp(joinpath(source_dir, f), joinpath(target_dir, f); force=true) + return cp(joinpath(source_dir, f), joinpath(target_dir, f); force = true) end setcached(root, name) end diff --git a/examples/quantum1d/1.ising-cft/main.jl b/examples/quantum1d/1.ising-cft/main.jl index e3a5e367d..ff3d38c5d 100644 --- a/examples/quantum1d/1.ising-cft/main.jl +++ b/examples/quantum1d/1.ising-cft/main.jl @@ -27,12 +27,11 @@ tensor is equivalent to optimixing a state in the entire Hilbert space, as all o are just unitary matrices that mix the basis. """ -energies, states = exact_diagonalization(H; num=18, alg=Lanczos(; krylovdim=200)); -plot(real.(energies); - seriestype=:scatter, - legend=false, - ylabel="energy", - xlabel="#eigenvalue") +energies, states = exact_diagonalization(H; num = 18, alg = Lanczos(; krylovdim = 200)); +plot( + real.(energies); + seriestype = :scatter, legend = false, ylabel = "energy", xlabel = "#eigenvalue" +) md""" !!! note "Krylov dimension" @@ -104,11 +103,13 @@ v = 2.0 Δ = real.(energies[1:18] .- energies[1]) ./ (2π * v / L) S = momenta ./ (2π / L) -p = plot(S, real.(Δ); - seriestype=:scatter, xlabel="conformal spin (S)", ylabel="scaling dimension (Δ)", - legend=false) -vline!(p, -3:3; color="gray", linestyle=:dash) -hline!(p, [0, 1 / 8, 1, 9 / 8, 2, 17 / 8]; color="gray", linestyle=:dash) +p = plot( + S, real.(Δ); + seriestype = :scatter, xlabel = "conformal spin (S)", ylabel = "scaling dimension (Δ)", + legend = false +) +vline!(p, -3:3; color = "gray", linestyle = :dash) +hline!(p, [0, 1 / 8, 1, 9 / 8, 2, 17 / 8]; color = "gray", linestyle = :dash) p md""" @@ -129,7 +130,7 @@ ansatz. This returns quasiparticle states, which can be converted to regular `Fi objects. """ -E_ex, qps = excitations(H_mps, QuasiparticleAnsatz(), ψ, envs; num=18) +E_ex, qps = excitations(H_mps, QuasiparticleAnsatz(), ψ, envs; num = 18) states_mps = vcat(ψ, map(qp -> convert(FiniteMPS, qp), qps)) energies_mps = map(x -> expectation_value(x, H_mps), states_mps) @@ -148,9 +149,11 @@ v = 2.0 Δ_mps = real.(energies_mps[1:18] .- energies_mps[1]) ./ (2π * v / L_mps) S_mps = momenta_mps ./ (2π / L_mps) -p_mps = plot(S_mps, real.(Δ_mps); - seriestype=:scatter, xlabel="conformal spin (S)", - ylabel="scaling dimension (Δ)", legend=false) -vline!(p_mps, -3:3; color="gray", linestyle=:dash) -hline!(p_mps, [0, 1 / 8, 1, 9 / 8, 2, 17 / 8]; color="gray", linestyle=:dash) +p_mps = plot( + S_mps, real.(Δ_mps); + seriestype = :scatter, xlabel = "conformal spin (S)", + ylabel = "scaling dimension (Δ)", legend = false +) +vline!(p_mps, -3:3; color = "gray", linestyle = :dash) +hline!(p_mps, [0, 1 / 8, 1, 9 / 8, 2, 17 / 8]; color = "gray", linestyle = :dash) p_mps diff --git a/examples/quantum1d/2.haldane/main.jl b/examples/quantum1d/2.haldane/main.jl index ff2f9640f..4982e12cd 100644 --- a/examples/quantum1d/2.haldane/main.jl +++ b/examples/quantum1d/2.haldane/main.jl @@ -42,26 +42,26 @@ H = heisenberg_XXX(symmetry, chain; J, spin) physical_space = SU2Space(1 => 1) virtual_space = SU2Space(0 => 12, 1 => 12, 2 => 5, 3 => 3) ψ₀ = FiniteMPS(L, physical_space, virtual_space) -ψ, envs, delta = find_groundstate(ψ₀, H, DMRG(; verbosity=0)) +ψ, envs, delta = find_groundstate(ψ₀, H, DMRG(; verbosity = 0)) E₀ = real(expectation_value(ψ, H)) -En_1, st_1 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector=SU2Irrep(1)) -En_2, st_2 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector=SU2Irrep(2)) +En_1, st_1 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector = SU2Irrep(1)) +En_2, st_2 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector = SU2Irrep(2)) ΔE_finite = real(En_2[1] - En_1[1]) md""" We can go even further and doublecheck the claim that ``S = 1`` is an edge excitation, by plotting the energy density. """ -p_density = plot(; xaxis="position", yaxis="energy density") +p_density = plot(; xaxis = "position", yaxis = "energy density") excited_1 = convert(FiniteMPS, st_1[1]) excited_2 = convert(FiniteMPS, st_2[1]) -SS = -S_exchange(ComplexF64, SU2Irrep; spin=1) +SS = -S_exchange(ComplexF64, SU2Irrep; spin = 1) e₀ = [real(expectation_value(ψ, (i, i + 1) => SS)) for i in 1:(L - 1)] e₁ = [real(expectation_value(excited_1, (i, i + 1) => SS)) for i in 1:(L - 1)] e₂ = [real(expectation_value(excited_2, (i, i + 1) => SS)) for i in 1:(L - 1)] -plot!(p_density, e₀; label="S = 0") -plot!(p_density, e₁; label="S = 1") -plot!(p_density, e₂; label="S = 2") +plot!(p_density, e₀; label = "S = 0") +plot!(p_density, e₁; label = "S = 1") +plot!(p_density, e₂; label = "S = 2") md""" Finally, we can obtain a value for the Haldane gap by extrapolating our results for different system sizes. @@ -72,20 +72,20 @@ Ls = 12:4:30 @info "computing L = $L" ψ₀ = FiniteMPS(L, physical_space, virtual_space) H = heisenberg_XXX(symmetry, FiniteChain(L); J, spin) - ψ, envs, delta = find_groundstate(ψ₀, H, DMRG(; verbosity=0)) - En_1, st_1 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector=SU2Irrep(1)) - En_2, st_2 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector=SU2Irrep(2)) + ψ, envs, delta = find_groundstate(ψ₀, H, DMRG(; verbosity = 0)) + En_1, st_1 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector = SU2Irrep(1)) + En_2, st_2 = excitations(H, QuasiparticleAnsatz(), ψ, envs; sector = SU2Irrep(2)) return real(En_2[1] - En_1[1]) end f = fit(Ls .^ (-2), ΔEs, 1) ΔE_extrapolated = f.coeffs[1] -#+ +#+ -p_size_extrapolation = plot(; xaxis="L^(-2)", yaxis="ΔE", xlims=(0, 0.015)) -plot!(p_size_extrapolation, Ls .^ (-2), ΔEs; seriestype=:scatter, label="numerical") -plot!(p_size_extrapolation, x -> f(x); label="fit") +p_size_extrapolation = plot(; xaxis = "L^(-2)", yaxis = "ΔE", xlims = (0, 0.015)) +plot!(p_size_extrapolation, Ls .^ (-2), ΔEs; seriestype = :scatter, label = "numerical") +plot!(p_size_extrapolation, x -> f(x); label = "fit") md""" ## Thermodynamic limit @@ -103,14 +103,14 @@ chain = InfiniteChain(1) H = heisenberg_XXX(symmetry, chain; J, spin) virtual_space_inf = Rep[SU₂](1 // 2 => 16, 3 // 2 => 16, 5 // 2 => 8, 7 // 2 => 4) ψ₀_inf = InfiniteMPS([physical_space], [virtual_space_inf]) -ψ_inf, envs_inf, delta_inf = find_groundstate(ψ₀_inf, H; verbosity=0) +ψ_inf, envs_inf, delta_inf = find_groundstate(ψ₀_inf, H; verbosity = 0) kspace = range(0, π, 16) -Es, _ = excitations(H, QuasiparticleAnsatz(), kspace, ψ_inf, envs_inf; sector=SU2Irrep(1)) +Es, _ = excitations(H, QuasiparticleAnsatz(), kspace, ψ_inf, envs_inf; sector = SU2Irrep(1)) ΔE, idx = findmin(real.(Es)) println("minimum @k = $(kspace[idx]):\t ΔE = $(ΔE)") -#+ +#+ -plot(kspace, real.(Es); xaxis="momentum", yaxis="ΔE", label="S = 1") +plot(kspace, real.(Es); xaxis = "momentum", yaxis = "ΔE", label = "S = 1") diff --git a/examples/quantum1d/3.ising-dqpt/main.jl b/examples/quantum1d/3.ising-dqpt/main.jl index fd5d85eea..e81d4b997 100644 --- a/examples/quantum1d/3.ising-dqpt/main.jl +++ b/examples/quantum1d/3.ising-dqpt/main.jl @@ -26,7 +26,7 @@ First we construct the hamiltonian in mpo form, and obtain the pre-quenched grou """ L = 20 -H₀ = transverse_field_ising(FiniteChain(L); g=-0.5) +H₀ = transverse_field_ising(FiniteChain(L); g = -0.5) ψ₀ = FiniteMPS(L, ℂ^2, ℂ^10) ψ₀, _ = find_groundstate(ψ₀, H₀, DMRG()); @@ -37,16 +37,16 @@ We can define a helper function that measures the loschmith echo """ echo(ψ₀::FiniteMPS, ψₜ::FiniteMPS) = -2 * log(abs(dot(ψ₀, ψₜ))) / length(ψ₀) -@assert isapprox(echo(ψ₀, ψ₀), 0, atol=1e-10) +@assert isapprox(echo(ψ₀, ψ₀), 0, atol = 1.0e-10) md""" We will initially use a two-site TDVP scheme to dynamically increase the bond dimension while time evolving, and later on switch to a faster one-site scheme. A single timestep can be done using """ -H₁ = transverse_field_ising(FiniteChain(L); g=-2.0) +H₁ = transverse_field_ising(FiniteChain(L); g = -2.0) ψₜ = deepcopy(ψ₀) dt = 0.01 -ψₜ, envs = timestep(ψₜ, H₁, 0, dt, TDVP2(; trscheme=truncdim(20))); +ψₜ, envs = timestep(ψₜ, H₁, 0, dt, TDVP2(; trscheme = truncdim(20))); md""" "envs" is a kind of cache object that keeps track of all environments in `ψ`. It is often advantageous to re-use the environment, so that mpskit doesn't need to recalculate everything. @@ -54,12 +54,12 @@ md""" Putting it all together, we get """ -function finite_sim(L; dt=0.05, finaltime=5.0) +function finite_sim(L; dt = 0.05, finaltime = 5.0) ψ₀ = FiniteMPS(L, ℂ^2, ℂ^10) - H₀ = transverse_field_ising(FiniteChain(L); g=-0.5) + H₀ = transverse_field_ising(FiniteChain(L); g = -0.5) ψ₀, _ = find_groundstate(ψ₀, H₀, DMRG()) - H₁ = transverse_field_ising(FiniteChain(L); g=-2.0) + H₁ = transverse_field_ising(FiniteChain(L); g = -2.0) ψₜ = deepcopy(ψ₀) envs = environments(ψₜ, H₁) @@ -67,7 +67,7 @@ function finite_sim(L; dt=0.05, finaltime=5.0) times = collect(0:dt:finaltime) for t in times[2:end] - alg = t > 3 * dt ? TDVP() : TDVP2(; trscheme=truncdim(50)) + alg = t > 3 * dt ? TDVP() : TDVP2(; trscheme = truncdim(50)) ψₜ, envs = timestep(ψₜ, H₁, 0, dt, alg, envs) push!(echos, echo(ψₜ, ψ₀)) end @@ -84,7 +84,7 @@ Similarly we could start with an initial infinite state and find the pre-quench """ ψ₀ = InfiniteMPS([ℂ^2], [ℂ^10]) -H₀ = transverse_field_ising(; g=-0.5) +H₀ = transverse_field_ising(; g = -0.5) ψ₀, _ = find_groundstate(ψ₀, H₀, VUMPS()); md""" @@ -99,7 +99,7 @@ so the loschmidth echo takes on the pleasant form """ echo(ψ₀::InfiniteMPS, ψₜ::InfiniteMPS) = -2 * log(abs(dot(ψ₀, ψₜ))) -@assert isapprox(echo(ψ₀, ψ₀), 0, atol=1e-10) +@assert isapprox(echo(ψ₀, ψ₀), 0, atol = 1.0e-10) md""" This time we cannot use a two-site scheme to grow the bond dimension, as this isn't implemented (yet). @@ -109,8 +109,8 @@ Growing the bond dimension by ``5`` can be done by calling: """ ψₜ = deepcopy(ψ₀) -H₁ = transverse_field_ising(; g=-2.0) -ψₜ, envs = changebonds(ψₜ, H₁, OptimalExpand(; trscheme=truncdim(5))); +H₁ = transverse_field_ising(; g = -2.0) +ψₜ, envs = changebonds(ψₜ, H₁, OptimalExpand(; trscheme = truncdim(5))); # a single timestep is easy @@ -122,7 +122,7 @@ With performance in mind we should once again try to re-use these "envs" cache o The final code is """ -function infinite_sim(dt=0.05, finaltime=5.0) +function infinite_sim(dt = 0.05, finaltime = 5.0) ψ₀ = InfiniteMPS([ℂ^2], [ℂ^10]) ψ₀, _ = find_groundstate(ψ₀, H₀, VUMPS()) @@ -134,7 +134,7 @@ function infinite_sim(dt=0.05, finaltime=5.0) for t in times[2:end] if t < 50dt # if t is sufficiently small, we increase the bond dimension - ψₜ, envs = changebonds(ψₜ, H₁, OptimalExpand(; trscheme=truncdim(1)), envs) + ψₜ, envs = changebonds(ψₜ, H₁, OptimalExpand(; trscheme = truncdim(1)), envs) end ψₜ, envs = timestep(ψₜ, H₁, 0, dt, TDVP(), envs) push!(echos, echo(ψₜ, ψ₀)) diff --git a/examples/quantum1d/4.xxz-heisenberg/main.jl b/examples/quantum1d/4.xxz-heisenberg/main.jl index 06499e62c..548490fed 100644 --- a/examples/quantum1d/4.xxz-heisenberg/main.jl +++ b/examples/quantum1d/4.xxz-heisenberg/main.jl @@ -15,7 +15,7 @@ Then we specify an initial guess, which we then further optimize. Working directly in the thermodynamic limit, this is achieved as follows: """ -H = heisenberg_XXX(; spin=1 // 2) +H = heisenberg_XXX(; spin = 1 // 2) md""" We then need an intial state, which we shall later optimize. In this example we work directly in the thermodynamic limit. @@ -35,7 +35,7 @@ On it's own, that is already quite curious. Maybe we can do better using another algorithm, such as gradient descent. """ -groundstate, cache, delta = find_groundstate(state, H, GradientGrassmann(; maxiter=20)); +groundstate, cache, delta = find_groundstate(state, H, GradientGrassmann(; maxiter = 20)); md""" Convergence is quite slow and even fails after sufficiently many iterations. @@ -66,18 +66,19 @@ Alternatively, the hamiltonian can be constructed directly on a two-site unitcel """ ## H2 = repeat(H, 2); -- copies the one-site version -H2 = heisenberg_XXX(ComplexF64, Trivial, InfiniteChain(2); spin=1 // 2) -groundstate, envs, delta = find_groundstate(state, H2, - VUMPS(; maxiter=100, tol=1e-12)); +H2 = heisenberg_XXX(ComplexF64, Trivial, InfiniteChain(2); spin = 1 // 2) +groundstate, envs, delta = find_groundstate( + state, H2, VUMPS(; maxiter = 100, tol = 1.0e-12) +); md""" We get convergence, but it takes an enormous amount of iterations. The reason behind this becomes more obvious at higher bond dimensions: """ -groundstate, envs, delta = find_groundstate(state, H2, - IDMRG2(; trscheme=truncdim(50), maxiter=20, - tol=1e-12)); +groundstate, envs, delta = find_groundstate( + state, H2, IDMRG2(; trscheme = truncdim(50), maxiter = 20, tol = 1.0e-12) +); entanglementplot(groundstate) md""" @@ -97,7 +98,7 @@ The XXZ Heisenberg hamiltonian is SU(2) symmetric and we can exploit this to gre It is cumbersome to construct symmetric hamiltonians, but luckily su(2) symmetric XXZ is already implemented: """ -H2 = heisenberg_XXX(ComplexF64, SU2Irrep, InfiniteChain(2); spin=1 // 2); +H2 = heisenberg_XXX(ComplexF64, SU2Irrep, InfiniteChain(2); spin = 1 // 2); md""" Our initial state should also be SU(2) symmetric. @@ -120,5 +121,4 @@ Even though the bond dimension is higher than in the example without symmetry, c println(dim(V1)) println(dim(V2)) -groundstate, cache, delta = find_groundstate(state, H2, - VUMPS(; maxiter=400, tol=1e-12)); +groundstate, cache, delta = find_groundstate(state, H2, VUMPS(; maxiter = 400, tol = 1.0e-12)); diff --git a/examples/quantum1d/5.haldane-spt/main.jl b/examples/quantum1d/5.haldane-spt/main.jl index fd4e223a5..ab6f72298 100644 --- a/examples/quantum1d/5.haldane-spt/main.jl +++ b/examples/quantum1d/5.haldane-spt/main.jl @@ -34,7 +34,7 @@ using Plots casimir(s::SU2Irrep) = s.j * (s.j + 1) -function heisenberg_hamiltonian(; J=-1.0) +function heisenberg_hamiltonian(; J = -1.0) s = SU2Irrep(1) ℋ = SU2Space(1 => 1) SS = zeros(ComplexF64, ℋ ⊗ ℋ ← ℋ ⊗ ℋ) @@ -80,9 +80,9 @@ non-injective MPS. ℋ = SU2Space(1 => 1) V_wrong = SU2Space(0 => 8, 1 // 2 => 8, 1 => 3, 3 // 2 => 3) ψ = InfiniteMPS(ℋ, V_wrong) -ψ, environments, δ = find_groundstate(ψ, H, VUMPS(; maxiter=10)) +ψ, environments, δ = find_groundstate(ψ, H, VUMPS(; maxiter = 10)) sectors = SU2Irrep[0, 1 // 2, 1, 3 // 2] -transferplot(ψ; sectors, title="Transfer matrix spectrum", legend=:outertop) +transferplot(ψ; sectors, title = "Transfer matrix spectrum", legend = :outertop) md""" Nevertheless, using the symmetry, this can be remedied rather easily, by imposing the @@ -104,29 +104,33 @@ right SPT phase. V_plus = SU2Space(0 => 10, 1 => 5, 2 => 3) ψ_plus = InfiniteMPS(ℋ, V_plus) -ψ_plus, = find_groundstate(ψ_plus, H, VUMPS(; maxiter=100)) +ψ_plus, = find_groundstate(ψ_plus, H, VUMPS(; maxiter = 100)) E_plus = expectation_value(ψ_plus, H) #+ V_minus = SU2Space(1 // 2 => 10, 3 // 2 => 5, 5 // 2 => 3) ψ_minus = InfiniteMPS(ℋ, V_minus) -ψ_minus, = find_groundstate(ψ_minus, H, VUMPS(; maxiter=100)) +ψ_minus, = find_groundstate(ψ_minus, H, VUMPS(; maxiter = 100)) E_minus = expectation_value(ψ_minus, H) #+ -transferp_plus = transferplot(ψ_plus; sectors=SU2Irrep[0, 1, 2], title="ψ_plus", - legend=:outertop) -transferp_minus = transferplot(ψ_minus; sectors=SU2Irrep[0, 1, 2], title="ψ_minus", - legend=:outertop) -plot(transferp_plus, transferp_minus; layout=(1, 2), size=(800, 400)) +transferp_plus = transferplot( + ψ_plus; + sectors = SU2Irrep[0, 1, 2], title = "ψ_plus", legend = :outertop +) +transferp_minus = transferplot( + ψ_minus; + sectors = SU2Irrep[0, 1, 2], title = "ψ_minus", legend = :outertop +) +plot(transferp_plus, transferp_minus; layout = (1, 2), size = (800, 400)) #+ -entanglementp_plus = entanglementplot(ψ_plus; title="ψ_plus", legend=:outertop) -entanglementp_minus = entanglementplot(ψ_minus; title="ψ_minus", legend=:outertop) -plot(entanglementp_plus, entanglementp_minus; layout=(1, 2), size=(800, 400)) +entanglementp_plus = entanglementplot(ψ_plus; title = "ψ_plus", legend = :outertop) +entanglementp_minus = entanglementplot(ψ_minus; title = "ψ_minus", legend = :outertop) +plot(entanglementp_plus, entanglementp_minus; layout = (1, 2), size = (800, 400)) md""" diff --git a/examples/quantum1d/6.hubbard/main.jl b/examples/quantum1d/6.hubbard/main.jl index 9241ee811..6e6a2f79e 100644 --- a/examples/quantum1d/6.hubbard/main.jl +++ b/examples/quantum1d/6.hubbard/main.jl @@ -51,38 +51,44 @@ e(u) = - u - 4 \int_0^{\infty} \frac{d\omega}{\omega} \frac{J_0(\omega) J_1(\ome We can easily verify this by comparing the numerical results to the analytic solution. """ -function hubbard_energy(u; rtol=1e-12) +function hubbard_energy(u; rtol = 1.0e-12) integrandum(ω) = besselj0(ω) * besselj1(ω) / (1 + exp(2u * ω)) / ω - int, err = quadgk(integrandum, 0, Inf; rtol=rtol) + int, err = quadgk(integrandum, 0, Inf; rtol = rtol) return -u - 4 * int end -function compute_groundstate(psi, H; - svalue=1e-3, - expansionfactor=(1 / 10), - expansioniter=20) +function compute_groundstate( + psi, H; + svalue = 1.0e-3, + expansionfactor = (1 / 10), + expansioniter = 20 + ) verbosity = 2 - psi, = find_groundstate(psi, H; tol=svalue * 10, verbosity) + psi, = find_groundstate(psi, H; tol = svalue * 10, verbosity) for _ in 1:expansioniter D = maximum(x -> dim(left_virtualspace(psi, x)), 1:length(psi)) D′ = max(5, round(Int, D * expansionfactor)) trscheme = truncbelow(svalue / 10) & truncdim(D′) - psi′, = changebonds(psi, H, OptimalExpand(; trscheme=trscheme)) - all(left_virtualspace.(Ref(psi), 1:length(psi)) .== - left_virtualspace.(Ref(psi′), 1:length(psi))) && break - psi, = find_groundstate(psi′, H, VUMPS(; tol=svalue / 5, maxiter=10, verbosity)) + psi′, = changebonds(psi, H, OptimalExpand(; trscheme = trscheme)) + all( + left_virtualspace.(Ref(psi), 1:length(psi)) .== + left_virtualspace.(Ref(psi′), 1:length(psi)) + ) && break + psi, = find_groundstate(psi′, H, VUMPS(; tol = svalue / 5, maxiter = 10, verbosity)) end ## convergence steps - psi, = changebonds(psi, H, SvdCut(; trscheme=truncbelow(svalue))) - psi, = find_groundstate(psi, H, - VUMPS(; tol=svalue / 100, verbosity, maxiter=100) & - GradientGrassmann(; tol=svalue / 1000)) + psi, = changebonds(psi, H, SvdCut(; trscheme = truncbelow(svalue))) + psi, = find_groundstate( + psi, H, + VUMPS(; tol = svalue / 100, verbosity, maxiter = 100) & + GradientGrassmann(; tol = svalue / 1000) + ) return psi end -H = hubbard_model(InfiniteChain(2); U, t, mu=U / 2) +H = hubbard_model(InfiniteChain(2); U, t, mu = U / 2) Vspaces = fill(Vect[fℤ₂](0 => 10, 1 => 10), 2) psi = InfiniteMPS(physicalspace(H), Vspaces) psi = compute_groundstate(psi, H) @@ -106,14 +112,14 @@ In order to work at half-filling, we need to effectively inject one particle per In MPSKit, this is achieved by the `add_physical_charge` function, which shifts the physical spaces of the tensors to the desired charge sector. """ -H_u1_su2 = hubbard_model(ComplexF64, U1Irrep, SU2Irrep, InfiniteChain(2); U, t, mu=U / 2); +H_u1_su2 = hubbard_model(ComplexF64, U1Irrep, SU2Irrep, InfiniteChain(2); U, t, mu = U / 2); charges = fill(FermionParity(1) ⊠ U1Irrep(1) ⊠ SU2Irrep(0), 2); H_u1_su2 = MPSKit.add_physical_charge(H_u1_su2, charges); pspaces = physicalspace.(Ref(H_u1_su2), 1:2) vspaces = [oneunit(eltype(pspaces)), first(pspaces)] psi = InfiniteMPS(pspaces, vspaces) -psi = compute_groundstate(psi, H_u1_su2; expansionfactor=1 / 3) +psi = compute_groundstate(psi, H_u1_su2; expansionfactor = 1 / 3) E = real(expectation_value(psi, H_u1_su2)) / 2 @info """ Groundstate energy: @@ -133,47 +139,49 @@ In other words, the groundstates are ``\psi_{AB}` and ``\psi_{BA}``, where ``A`` These excitations can be constructed as follows: """ -alg = QuasiparticleAnsatz(; tol=1e-3) -momenta = range(-π, π; length=33) +alg = QuasiparticleAnsatz(; tol = 1.0e-3) +momenta = range(-π, π; length = 33) psi_AB = psi envs_AB = environments(psi_AB, H_u1_su2); psi_BA = circshift(psi, 1) envs_BA = environments(psi_BA, H_u1_su2); spinon_charge = FermionParity(0) ⊠ U1Irrep(0) ⊠ SU2Irrep(1 // 2) -E_spinon, ϕ_spinon = excitations(H_u1_su2, alg, momenta, - psi_AB, envs_AB, psi_BA, envs_BA; - sector=spinon_charge, num=1); +E_spinon, ϕ_spinon = excitations( + H_u1_su2, alg, momenta, psi_AB, envs_AB, psi_BA, envs_BA; + sector = spinon_charge, num = 1 +); holon_charge = FermionParity(1) ⊠ U1Irrep(-1) ⊠ SU2Irrep(0) -E_holon, ϕ_holon = excitations(H_u1_su2, alg, momenta, - psi_AB, envs_AB, psi_BA, envs_BA; - sector=holon_charge, num=1); +E_holon, ϕ_holon = excitations( + H_u1_su2, alg, momenta, psi_AB, envs_AB, psi_BA, envs_BA; + sector = holon_charge, num = 1 +); md""" Again, we can compare the numerical results to the analytic solution. Here, the formulae for the excitation energies are expressed in terms of dressed momenta: """ -function spinon_momentum(Λ, u; rtol=1e-12) +function spinon_momentum(Λ, u; rtol = 1.0e-12) integrandum(ω) = besselj0(ω) * sin(ω * Λ) / ω / cosh(ω * u) - return π / 2 - quadgk(integrandum, 0, Inf; rtol=rtol)[1] + return π / 2 - quadgk(integrandum, 0, Inf; rtol = rtol)[1] end -function spinon_energy(Λ, u; rtol=1e-12) +function spinon_energy(Λ, u; rtol = 1.0e-12) integrandum(ω) = besselj1(ω) * cos(ω * Λ) / ω / cosh(ω * u) - return 2 * quadgk(integrandum, 0, Inf; rtol=rtol)[1] + return 2 * quadgk(integrandum, 0, Inf; rtol = rtol)[1] end -function holon_momentum(k, u; rtol=1e-12) +function holon_momentum(k, u; rtol = 1.0e-12) integrandum(ω) = besselj0(ω) * sin(ω * sin(k)) / ω / (1 + exp(2u * abs(ω))) - return π / 2 - k - 2 * quadgk(integrandum, 0, Inf; rtol=rtol)[1] + return π / 2 - k - 2 * quadgk(integrandum, 0, Inf; rtol = rtol)[1] end -function holon_energy(k, u; rtol=1e-12) +function holon_energy(k, u; rtol = 1.0e-12) integrandum(ω) = besselj1(ω) * cos(ω * sin(k)) * exp(-ω * u) / ω / cosh(ω * u) - return 2 * cos(k) + 2u + 2 * quadgk(integrandum, 0, Inf; rtol=rtol)[1] + return 2 * cos(k) + 2u + 2 * quadgk(integrandum, 0, Inf; rtol = rtol)[1] end -Λs = range(-10, 10; length=51) +Λs = range(-10, 10; length = 51) P_spinon_analytic = rem2pi.(spinon_momentum.(Λs, U / 4), RoundNearest) E_spinon_analytic = spinon_energy.(Λs, U / 4) I_spinon = sortperm(P_spinon_analytic) @@ -182,19 +190,19 @@ E_spinon_analytic = E_spinon_analytic[I_spinon] P_spinon_analytic = [reverse(-P_spinon_analytic); P_spinon_analytic] E_spinon_analytic = [reverse(E_spinon_analytic); E_spinon_analytic]; -ks = range(0, 2π; length=51) +ks = range(0, 2π; length = 51) P_holon_analytic = rem2pi.(holon_momentum.(ks, U / 4), RoundNearest) E_holon_analytic = holon_energy.(ks, U / 4) I_holon = sortperm(P_holon_analytic) P_holon_analytic = P_holon_analytic[I_holon] E_holon_analytic = E_holon_analytic[I_holon]; -p = let p_excitations = plot(; xaxis="momentum", yaxis="energy") - scatter!(p_excitations, momenta, real(E_spinon); label="spinon") - plot!(p_excitations, P_spinon_analytic, E_spinon_analytic; label="spinon (analytic)") +p = let p_excitations = plot(; xaxis = "momentum", yaxis = "energy") + scatter!(p_excitations, momenta, real(E_spinon); label = "spinon") + plot!(p_excitations, P_spinon_analytic, E_spinon_analytic; label = "spinon (analytic)") - scatter!(p_excitations, momenta, real(E_holon); label="holon") - plot!(p_excitations, P_holon_analytic, E_holon_analytic; label="holon (analytic)") + scatter!(p_excitations, momenta, real(E_holon); label = "holon") + plot!(p_excitations, P_holon_analytic, E_holon_analytic; label = "holon (analytic)") p_excitations end @@ -207,12 +215,12 @@ Here, we can fix this shift by realizing that our choice of shifting the grounds """ momenta_shifted = rem2pi.(momenta .- π / 2, RoundNearest) -p = let p_excitations = plot(; xaxis="momentum", yaxis="energy", xlims=(-π, π)) - scatter!(p_excitations, momenta_shifted, real(E_spinon); label="spinon") - plot!(p_excitations, P_spinon_analytic, E_spinon_analytic; label="spinon (analytic)") +p = let p_excitations = plot(; xaxis = "momentum", yaxis = "energy", xlims = (-π, π)) + scatter!(p_excitations, momenta_shifted, real(E_spinon); label = "spinon") + plot!(p_excitations, P_spinon_analytic, E_spinon_analytic; label = "spinon (analytic)") - scatter!(p_excitations, momenta_shifted, real(E_holon); label="holon") - plot!(p_excitations, P_holon_analytic, E_holon_analytic; label="holon (analytic)") + scatter!(p_excitations, momenta_shifted, real(E_holon); label = "holon") + plot!(p_excitations, P_holon_analytic, E_holon_analytic; label = "holon (analytic)") p_excitations end @@ -225,10 +233,14 @@ If these are truly scattering states, the energy of the scattering state should Thus, we can find the lowest-energy scattering states by minimizing the energy over the combination of momenta for the constituent elementary excitations. """ -holon_dispersion_itp = linear_interpolation(P_holon_analytic, E_holon_analytic; - extrapolation_bc=Line()) -spinon_dispersion_itp = linear_interpolation(P_spinon_analytic, E_spinon_analytic; - extrapolation_bc=Line()) +holon_dispersion_itp = linear_interpolation( + P_holon_analytic, E_holon_analytic; + extrapolation_bc = Line() +) +spinon_dispersion_itp = linear_interpolation( + P_spinon_analytic, E_spinon_analytic; + extrapolation_bc = Line() +) function scattering_energy(p1, p2, p3) p1, p2, p3 = rem2pi.((p1, p2, p3), RoundNearest) return holon_dispersion_itp(p1) + spinon_dispersion_itp(p2) + spinon_dispersion_itp(p3) @@ -259,17 +271,20 @@ E_scattering_max = map(momenta_shifted) do p return e end; -p = let p_excitations = plot(; xaxis="momentum", yaxis="energy", xlims=(-π, π), - ylims=(-0.1, 5)) - scatter!(p_excitations, momenta_shifted, real(E_spinon); label="spinon") - plot!(p_excitations, P_spinon_analytic, E_spinon_analytic; label="spinon (analytic)") +p = let p_excitations = plot(; + xaxis = "momentum", yaxis = "energy", xlims = (-π, π), ylims = (-0.1, 5) + ) + scatter!(p_excitations, momenta_shifted, real(E_spinon); label = "spinon") + plot!(p_excitations, P_spinon_analytic, E_spinon_analytic; label = "spinon (analytic)") - scatter!(p_excitations, momenta_shifted, real(E_holon); label="holon") - plot!(p_excitations, P_holon_analytic, E_holon_analytic; label="holon (analytic)") + scatter!(p_excitations, momenta_shifted, real(E_holon); label = "holon") + plot!(p_excitations, P_holon_analytic, E_holon_analytic; label = "holon (analytic)") I = sortperm(momenta_shifted) - plot!(p_excitations, momenta_shifted[I], E_scattering_min[I]; label="scattering states", - fillrange=E_scattering_max[I], fillalpha=0.3, fillstyle=:x) + plot!( + p_excitations, momenta_shifted[I], E_scattering_min[I]; label = "scattering states", + fillrange = E_scattering_max[I], fillalpha = 0.3, fillstyle = :x + ) p_excitations end diff --git a/examples/quantum1d/7.xy-finiteT/main.jl b/examples/quantum1d/7.xy-finiteT/main.jl index 349cfcb22..cab8dcd7c 100644 --- a/examples/quantum1d/7.xy-finiteT/main.jl +++ b/examples/quantum1d/7.xy-finiteT/main.jl @@ -29,14 +29,15 @@ J = 1 / 2 T = ComplexF64 symmetry = U1Irrep -function XY_hamiltonian(::Type{T}=ComplexF64, ::Type{S}=Trivial; J=1 / 2, - N) where {T<:Number,S<:Sector} +function XY_hamiltonian( + ::Type{T} = ComplexF64, ::Type{S} = Trivial; J = 1 / 2, N + ) where {T <: Number, S <: Sector} spin = 1 // 2 term = J * (S_xx(T, S; spin) + S_yy(T, S; spin)) lattice = isfinite(N) ? FiniteChain(N) : InfiniteChain(1) return @mpoham begin sum(nearest_neighbours(lattice)) do (i, j) - return term{i,j} + return term{i, j} end end end @@ -69,7 +70,7 @@ We can check our results by comparing them to the exact diagonalization of the H """ N_exact = 6 -H = open_boundary_conditions(XY_hamiltonian(T, symmetry; J, N=Inf), N_exact) +H = open_boundary_conditions(XY_hamiltonian(T, symmetry; J, N = Inf), N_exact) H_dense = convert(TensorMap, H); vals = eigvals(H_dense)[one(symmetry)] ./ N_exact groundstate_energy(J, N_exact) @@ -90,7 +91,7 @@ D = 64 V_init = symmetry === Trivial ? ℂ^32 : U1Space(i => 10 for i in -1:(1 // 2):1) psi_init = FiniteMPS(N, physicalspace(H, 1), V_init) trscheme = truncdim(D) -psi, envs, = find_groundstate(psi_init, H, DMRG2(; trscheme, maxiter=5)); +psi, envs, = find_groundstate(psi_init, H, DMRG2(; trscheme, maxiter = 5)); E_0 = expectation_value(psi, H, envs) / N println("Numerical:\t", real(E_0)) @@ -171,8 +172,7 @@ expansion_orders = 1:3 function logpartition_taylor(β, H; expansion_order) dτ = im * β - expH = make_time_mpo(H, dτ, - TaylorCluster(; N=expansion_order)) + expH = make_time_mpo(H, dτ, TaylorCluster(; N = expansion_order)) return log(real(tr(expH))) / length(H) end @@ -183,16 +183,21 @@ end F_taylor = -(1 ./ βs) .* Z_taylor p_taylor = let - labels = reshape(map(expansion_orders) do N - return "Taylor N=$N" - end, 1, :) - p1 = plot(βs, Z_analytic; label="analytic", - title="Partition function", - xlabel="β", ylabel="Z(β)") - plot!(p1, βs, Z_taylor; label=labels) - p2 = plot(βs, F_analytic; label="analytic", title="Free energy", - xlabel="β", ylabel="F(β)") - plot!(p2, βs, F_taylor; label=labels) + labels = reshape( + map(expansion_orders) do N + return "Taylor N=$N" + end, 1, : + ) + p1 = plot( + βs, Z_analytic; label = "analytic", title = "Partition function", + xlabel = "β", ylabel = "Z(β)" + ) + plot!(p1, βs, Z_taylor; label = labels) + p2 = plot( + βs, F_analytic; label = "analytic", title = "Free energy", + xlabel = "β", ylabel = "F(β)" + ) + plot!(p2, βs, F_taylor; label = labels) plot(p1, p2) end @@ -232,14 +237,20 @@ Z_taylor = Z_taylor[:, 2:end] F_taylor = F_taylor[:, 2:end] p_taylor_diff = let - labels = reshape(map(expansion_orders) do N - return "Taylor N=$N" - end, 1, :) - p1 = plot(βs, abs.(Z_taylor .- Z_analytic); - label=labels, title="Partition function error", - xlabel="β", ylabel="ΔZ(β)", legend=:topleft) - p2 = plot(βs, abs.(F_taylor .- F_analytic); label=labels, - xlabel="β", ylabel="ΔF(β)", title="Free energy error", legend=:topleft) + labels = reshape( + map(expansion_orders) do N + return "Taylor N=$N" + end, 1, : + ) + p1 = plot( + βs, abs.(Z_taylor .- Z_analytic); + label = labels, title = "Partition function error", + xlabel = "β", ylabel = "ΔZ(β)", legend = :topleft + ) + p2 = plot( + βs, abs.(F_taylor .- F_analytic); label = labels, + xlabel = "β", ylabel = "ΔF(β)", title = "Free energy error", legend = :topleft + ) plot(p1, p2) end @@ -263,11 +274,11 @@ Otherwise, we could still use the same trick, but we would have to compute the e Add a figure to illustrate this trick. """ -double_logpartition(ρ₁, ρ₂=ρ₁) = log(real(dot(ρ₁, ρ₂))) / length(ρ₁) +double_logpartition(ρ₁, ρ₂ = ρ₁) = log(real(dot(ρ₁, ρ₂))) / length(ρ₁) function logpartition_taylor2(β, H; expansion_order) dτ = im * β / 2 - expH = make_time_mpo(H, dτ, TaylorCluster(; N=expansion_order)) + expH = make_time_mpo(H, dτ, TaylorCluster(; N = expansion_order)) return double_logpartition(expH) end @@ -278,14 +289,20 @@ end F_taylor2 = -(1 ./ βs) .* Z_taylor2 p_taylor2_diff = let - labels = reshape(map(expansion_orders[2:end]) do N - return "Taylor N=$N" - end, 1, :) - p1 = plot(βs, abs.(Z_taylor2 .- Z_analytic); - label=labels, title="Partition function error", - xlabel="β", ylabel="ΔZ(β)", legend=:topleft) - p2 = plot(βs, abs.(F_taylor2 .- F_analytic); label=labels, - xlabel="β", ylabel="ΔF(β)", title="Free energy error", legend=:topleft) + labels = reshape( + map(expansion_orders[2:end]) do N + return "Taylor N=$N" + end, 1, : + ) + p1 = plot( + βs, abs.(Z_taylor2 .- Z_analytic); + label = labels, title = "Partition function error", + xlabel = "β", ylabel = "ΔZ(β)", legend = :topleft + ) + p2 = plot( + βs, abs.(F_taylor2 .- F_analytic); label = labels, + xlabel = "β", ylabel = "ΔF(β)", title = "Free energy error", legend = :topleft + ) plot(p1, p2) end @@ -321,7 +338,7 @@ Z_mpo_mul = zeros(length(βs)) D_max = 64 ## first iteration: start from high order Taylor expansion -ρ₀ = make_time_mpo(H, im * βs[2] / 2, TaylorCluster(; N=3)) +ρ₀ = make_time_mpo(H, im * βs[2] / 2, TaylorCluster(; N = 3)) Z_mpo_mul[1] = Z_taylor[1] Z_mpo_mul[2] = double_logpartition(ρ₀) @@ -330,26 +347,37 @@ Z_mpo_mul[2] = double_logpartition(ρ₀) for i in 3:length(βs) global ρ_mps @info "Computing β = $(βs[i])" - ρ_mps, = approximate(ρ_mps, (ρ₀, ρ_mps), - DMRG2(; trscheme=truncdim(D_max), maxiter=10)) + ρ_mps, = approximate( + ρ_mps, (ρ₀, ρ_mps), DMRG2(; trscheme = truncdim(D_max), maxiter = 10) + ) Z_mpo_mul[i] = double_logpartition(ρ_mps) end F_mpo_mul = -(1 ./ βs) .* Z_mpo_mul p_mpo_mul_diff = let - labels = reshape(map(expansion_orders) do N - return "Taylor N=$N" - end, 1, :) - p1 = plot(βs, abs.(Z_taylor2 .- Z_analytic); - label=labels, title="Partition function error", - xlabel="β", ylabel="ΔZ(β)", legend=:bottomright, yscale=:log10) - plot!(p1, βs, abs.(Z_mpo_mul .- Z_analytic); - label="MPO multiplication") - p2 = plot(βs, abs.(F_taylor2 .- F_analytic); label=labels, - xlabel="β", ylabel="ΔF(β)", title="Free energy error", legend=nothing, - yscale=:log10) - plot!(p2, βs, abs.(F_mpo_mul .- F_analytic); - label="MPO multiplication") + labels = reshape( + map(expansion_orders) do N + return "Taylor N=$N" + end, 1, : + ) + p1 = plot( + βs, abs.(Z_taylor2 .- Z_analytic); + label = labels, title = "Partition function error", + xlabel = "β", ylabel = "ΔZ(β)", legend = :bottomright, yscale = :log10 + ) + plot!( + p1, βs, abs.(Z_mpo_mul .- Z_analytic); + label = "MPO multiplication" + ) + p2 = plot( + βs, abs.(F_taylor2 .- F_analytic); label = labels, + xlabel = "β", ylabel = "ΔF(β)", title = "Free energy error", legend = nothing, + yscale = :log10 + ) + plot!( + p2, βs, abs.(F_mpo_mul .- F_analytic); + label = "MPO multiplication" + ) plot(p1, p2) end @@ -393,7 +421,7 @@ F_analytic_exp = free_energy.(βs_exp, J, N) Z_mpo_mul_exp = zeros(length(βs_exp)) ## first iteration: start from high order Taylor expansion -ρ₀ = make_time_mpo(H, im * first(βs_exp) / 2, TaylorCluster(; N=3)) +ρ₀ = make_time_mpo(H, im * first(βs_exp) / 2, TaylorCluster(; N = 3)) Z_mpo_mul_exp[1] = double_logpartition(ρ₀) ## subsequent iterations: square @@ -402,27 +430,34 @@ Z_mpo_mul_exp[1] = double_logpartition(ρ₀) for i in 2:length(βs_exp) global ρ_mps, ρ @info "Computing β = $(βs_exp[i])" - ρ_mps, = approximate(ρ_mps, (ρ, ρ_mps), - DMRG2(; trscheme=truncdim(D_max), maxiter=10)) + ρ_mps, = approximate( + ρ_mps, (ρ, ρ_mps), DMRG2(; trscheme = truncdim(D_max), maxiter = 10) + ) Z_mpo_mul_exp[i] = double_logpartition(ρ_mps) ρ = convert(FiniteMPO, ρ_mps) end F_mpo_mul_exp = -(1 ./ βs_exp) .* Z_mpo_mul_exp p_mpo_mul_exp_diff = let - labels = reshape(map(expansion_orders[2:end]) do N - return "Taylor N=$N" - end, 1, :) - p1 = plot(βs, abs.(Z_taylor2 .- Z_analytic); - label=labels, title="Partition function error", xlabel="β", ylabel="ΔZ(β)", - legend=:bottomright, yscale=:log10) - plot!(p1, βs, abs.(Z_mpo_mul .- Z_analytic); label="MPO multiplication") - plot!(p1, βs_exp, abs.(Z_mpo_mul_exp .- Z_analytic_exp); label="MPO multiplication exp") - - p2 = plot(βs, abs.(F_taylor2 .- F_analytic); label=labels, xlabel="β", ylabel="ΔF(β)", - title="Free energy error", legend=nothing, yscale=:log10) - plot!(p2, βs, abs.(F_mpo_mul .- F_analytic); label="MPO multiplication") - plot!(p2, βs_exp, abs.(F_mpo_mul_exp .- F_analytic_exp); label="MPO multiplication exp") + labels = reshape( + map(expansion_orders[2:end]) do N + return "Taylor N=$N" + end, 1, : + ) + p1 = plot( + βs, abs.(Z_taylor2 .- Z_analytic); + label = labels, title = "Partition function error", xlabel = "β", ylabel = "ΔZ(β)", + legend = :bottomright, yscale = :log10 + ) + plot!(p1, βs, abs.(Z_mpo_mul .- Z_analytic); label = "MPO multiplication") + plot!(p1, βs_exp, abs.(Z_mpo_mul_exp .- Z_analytic_exp); label = "MPO multiplication exp") + + p2 = plot( + βs, abs.(F_taylor2 .- F_analytic); label = labels, xlabel = "β", ylabel = "ΔF(β)", + title = "Free energy error", legend = nothing, yscale = :log10 + ) + plot!(p2, βs, abs.(F_mpo_mul .- F_analytic); label = "MPO multiplication") + plot!(p2, βs_exp, abs.(F_mpo_mul_exp .- F_analytic_exp); label = "MPO multiplication exp") plot(p1, p2) end @@ -462,28 +497,36 @@ Z_tdvp[1] = double_logpartition(ρ₀) for i in 2:length(βs) global ρ_mps @info "Computing β = $(βs[i])" - ρ_mps, = timestep(ρ_mps, H, βs[i - 1] / 2, -im * (βs[i] - βs[i - 1]) / 2, - TDVP2(; trscheme=truncdim(64))) + ρ_mps, = timestep( + ρ_mps, H, βs[i - 1] / 2, -im * (βs[i] - βs[i - 1]) / 2, + TDVP2(; trscheme = truncdim(64)) + ) Z_tdvp[i] = double_logpartition(ρ_mps) end F_tdvp = -(1 ./ βs) .* Z_tdvp p_mpo_mul_diff = let - labels = reshape(map(expansion_orders) do N - return "Taylor N=$N" - end, 1, :) - p1 = plot(βs, abs.(Z_taylor2 .- Z_analytic); label=labels, - title="Partition function error", xlabel="β", ylabel="ΔZ(β)", - legend=:bottomright, yscale=:log10) - plot!(p1, βs, abs.(Z_mpo_mul .- Z_analytic); label="MPO multiplication") - plot!(p1, βs_exp, abs.(Z_mpo_mul_exp .- Z_analytic_exp); label="MPO multiplication exp") - plot!(p1, βs, abs.(Z_tdvp .- Z_analytic); label="TDVP") - - p2 = plot(βs, abs.(F_taylor2 .- F_analytic); label=labels, xlabel="β", ylabel="ΔF(β)", - title="Free energy error", legend=nothing, yscale=:log10) - plot!(p2, βs, abs.(F_mpo_mul .- F_analytic); label="MPO multiplication") - plot!(p2, βs_exp, abs.(F_mpo_mul_exp .- F_analytic_exp); label="MPO multiplication exp") - plot!(p2, βs, abs.(F_tdvp .- F_analytic); label="TDVP") + labels = reshape( + map(expansion_orders) do N + return "Taylor N=$N" + end, 1, : + ) + p1 = plot( + βs, abs.(Z_taylor2 .- Z_analytic); label = labels, + title = "Partition function error", xlabel = "β", ylabel = "ΔZ(β)", + legend = :bottomright, yscale = :log10 + ) + plot!(p1, βs, abs.(Z_mpo_mul .- Z_analytic); label = "MPO multiplication") + plot!(p1, βs_exp, abs.(Z_mpo_mul_exp .- Z_analytic_exp); label = "MPO multiplication exp") + plot!(p1, βs, abs.(Z_tdvp .- Z_analytic); label = "TDVP") + + p2 = plot( + βs, abs.(F_taylor2 .- F_analytic); label = labels, xlabel = "β", ylabel = "ΔF(β)", + title = "Free energy error", legend = nothing, yscale = :log10 + ) + plot!(p2, βs, abs.(F_mpo_mul .- F_analytic); label = "MPO multiplication") + plot!(p2, βs_exp, abs.(F_mpo_mul_exp .- F_analytic_exp); label = "MPO multiplication exp") + plot!(p2, βs, abs.(F_tdvp .- F_analytic); label = "TDVP") plot(p1, p2) end diff --git a/examples/windowmps.jl b/examples/windowmps.jl index 807c8c807..967b18979 100644 --- a/examples/windowmps.jl +++ b/examples/windowmps.jl @@ -2,14 +2,14 @@ using MPSKit, MPSKitModels, TensorKit, Plots let #defining the hamiltonian - th = nonsym_ising_ham(; lambda=0.3) + th = nonsym_ising_ham(; lambda = 0.3) sx, sy, sz = nonsym_spintensors(1 // 2) #initilizing a random mps ts = InfiniteMPS([ℂ^2], [ℂ^12]) #Finding the groundstate - (ts, envs, _) = find_groundstate(ts, th, VUMPS(; maxiter=400)) + ts, envs, _ = find_groundstate(ts, th, VUMPS(; maxiter = 400)) len = 20 deltat = 0.05 @@ -26,7 +26,7 @@ let szdat = [expectation_value(mpco, sz)] for i in 1:(totaltime / deltat) - (mpco, envs) = timestep(mpco, th, deltat, TDVP2(; trscheme=truncdim(20)), envs) + mpco, envs = timestep(mpco, th, deltat, TDVP2(; trscheme = truncdim(20)), envs) push!(szdat, expectation_value(mpco, sz)) end diff --git a/src/algorithms/ED.jl b/src/algorithms/ED.jl index 88826c10e..a3a5c4036 100644 --- a/src/algorithms/ED.jl +++ b/src/algorithms/ED.jl @@ -30,10 +30,11 @@ equivalent to dense eigenvectors. of all the physical spaces in the system. """ -function exact_diagonalization(H::FiniteMPOHamiltonian; - sector=one(sectortype(H)), - num::Int=1, which::Symbol=:SR, - alg=Defaults.alg_eigsolve(; dynamic_tols=false)) +function exact_diagonalization( + H::FiniteMPOHamiltonian; + sector = one(sectortype(H)), num::Int = 1, which::Symbol = :SR, + alg = Defaults.alg_eigsolve(; dynamic_tols = false) + ) L = length(H) @assert L > 1 "FiniteMPOHamiltonian must have length > 1" middle_site = (L >> 1) + 1 @@ -42,7 +43,7 @@ function exact_diagonalization(H::FiniteMPOHamiltonian; TA = tensormaptype(spacetype(H), 2, 1, T) # fuse from left to right - ALs = Vector{Union{Missing,TA}}(missing, L) + ALs = Vector{Union{Missing, TA}}(missing, L) left = oneunit(spacetype(H)) for i in 1:(middle_site - 1) P = physicalspace(H, i) @@ -51,7 +52,7 @@ function exact_diagonalization(H::FiniteMPOHamiltonian; end # fuse from right to left - ARs = Vector{Union{Missing,TA}}(missing, L) + ARs = Vector{Union{Missing, TA}}(missing, L) right = spacetype(H)(sector => 1) for i in reverse((middle_site + 1):L) P = physicalspace(H, i) @@ -60,12 +61,12 @@ function exact_diagonalization(H::FiniteMPOHamiltonian; end # center - ACs = Vector{Union{Missing,TA}}(missing, L) + ACs = Vector{Union{Missing, TA}}(missing, L) P = physicalspace(H, middle_site) ACs[middle_site] = rand!(similar(ALs[1], left ⊗ P ← right)) TB = tensormaptype(spacetype(H), 1, 1, T) - Cs = Vector{Union{Missing,TB}}(missing, L + 1) + Cs = Vector{Union{Missing, TB}}(missing, L + 1) state = FiniteMPS(ALs, ARs, ACs, Cs) envs = environments(state, H) diff --git a/src/algorithms/approximate/approximate.jl b/src/algorithms/approximate/approximate.jl index e2bd6ee5f..f905c5405 100644 --- a/src/algorithms/approximate/approximate.jl +++ b/src/algorithms/approximate/approximate.jl @@ -27,33 +27,41 @@ of an MPS `ψ₀`. approximate, approximate! # implementation in terms of Multiline -function approximate(ψ::InfiniteMPS, - toapprox::Tuple{<:InfiniteMPO,<:InfiniteMPS}, - algorithm, - envs=environments(ψ, toapprox)) +function approximate( + ψ::InfiniteMPS, toapprox::Tuple{<:InfiniteMPO, <:InfiniteMPS}, algorithm, + envs = environments(ψ, toapprox) + ) envs′ = Multiline([envs]) - multi, envs = approximate(convert(MultilineMPS, ψ), - (convert(MultilineMPO, toapprox[1]), - convert(MultilineMPS, toapprox[2])), algorithm, envs′) + multi, envs = approximate( + convert(MultilineMPS, ψ), + (convert(MultilineMPO, toapprox[1]), convert(MultilineMPS, toapprox[2])), + algorithm, + envs′ + ) ψ = convert(InfiniteMPS, multi) return ψ, envs end # dispatch to in-place method -function approximate(ψ, toapprox, alg::Union{DMRG,DMRG2,IDMRG,IDMRG2}, - envs=environments(ψ, toapprox)) +function approximate( + ψ, toapprox, alg::Union{DMRG, DMRG2, IDMRG, IDMRG2}, + envs = environments(ψ, toapprox) + ) return approximate!(copy(ψ), toapprox, alg, envs) end # disambiguate -function approximate(ψ::InfiniteMPS, - toapprox::Tuple{<:InfiniteMPO,<:InfiniteMPS}, - algorithm::Union{IDMRG,IDMRG2}, - envs=environments(ψ, toapprox)) +function approximate( + ψ::InfiniteMPS, toapprox::Tuple{<:InfiniteMPO, <:InfiniteMPS}, + algorithm::Union{IDMRG, IDMRG2}, envs = environments(ψ, toapprox) + ) envs′ = Multiline([envs]) - multi, envs = approximate(convert(MultilineMPS, ψ), - (convert(MultilineMPO, toapprox[1]), - convert(MultilineMPS, toapprox[2])), algorithm, envs′) + multi, envs = approximate( + convert(MultilineMPS, ψ), + (convert(MultilineMPO, toapprox[1]), convert(MultilineMPS, toapprox[2])), + algorithm, + envs′ + ) ψ = convert(InfiniteMPS, multi) return ψ, envs end diff --git a/src/algorithms/approximate/fvomps.jl b/src/algorithms/approximate/fvomps.jl index f9db19dbc..a10e2a112 100644 --- a/src/algorithms/approximate/fvomps.jl +++ b/src/algorithms/approximate/fvomps.jl @@ -1,5 +1,4 @@ -function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, - envs=environments(ψ, Oϕ)) +function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, envs = environments(ψ, Oϕ)) ϵ::Float64 = 2 * alg.tol log = IterLog("DMRG2") @@ -9,7 +8,7 @@ function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, ϵ = 0.0 for pos in [1:(length(ψ) - 1); (length(ψ) - 2):-1:1] AC2′ = AC2_projection(pos, ψ, Oϕ, envs) - al, c, ar, = tsvd!(AC2′; trunc=alg.trscheme) + al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme) AC2 = ψ.AC[pos] * _transpose_tail(ψ.AR[pos + 1]) ϵ = max(ϵ, norm(al * c * ar - AC2) / norm(AC2)) @@ -19,7 +18,7 @@ function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, end # finalize - ψ, envs = alg.finalize(iter, ψ, Oϕ, envs)::Tuple{typeof(ψ),typeof(envs)} + ψ, envs = alg.finalize(iter, ψ, Oϕ, envs)::Tuple{typeof(ψ), typeof(envs)} if ϵ < alg.tol @infov 2 logfinish!(log, iter, ϵ) @@ -36,7 +35,7 @@ function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG2, return ψ, envs, ϵ end -function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG, envs=environments(ψ, Oϕ)) +function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG, envs = environments(ψ, Oϕ)) ϵ::Float64 = 2 * alg.tol log = IterLog("DMRG") @@ -53,7 +52,7 @@ function approximate!(ψ::AbstractFiniteMPS, Oϕ, alg::DMRG, envs=environments( end # finalize - ψ, envs = alg.finalize(iter, ψ, Oϕ, envs)::Tuple{typeof(ψ),typeof(envs)} + ψ, envs = alg.finalize(iter, ψ, Oϕ, envs)::Tuple{typeof(ψ), typeof(envs)} if ϵ < alg.tol @infov 2 logfinish!(log, iter, ϵ) diff --git a/src/algorithms/approximate/idmrg.jl b/src/algorithms/approximate/idmrg.jl index adb4aeec3..a08f618dd 100644 --- a/src/algorithms/approximate/idmrg.jl +++ b/src/algorithms/approximate/idmrg.jl @@ -1,5 +1,7 @@ -function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:MultilineMPS}, - alg::IDMRG, envs=environments(ψ, toapprox)) +function approximate!( + ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO, <:MultilineMPS}, alg::IDMRG, + envs = environments(ψ, toapprox) + ) log = IterLog("IDMRG") ϵ::Float64 = 2 * alg.tol local iter @@ -12,8 +14,9 @@ function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:Multili # left to right sweep for col in 1:size(ψ, 2) for row in 1:size(ψ, 1) - ψ.AC[row + 1, col] = AC_projection(CartesianIndex(row, col), ψ, - toapprox, envs) + ψ.AC[row + 1, col] = AC_projection( + CartesianIndex(row, col), ψ, toapprox, envs + ) normalize!(ψ.AC[row + 1, col]) ψ.AL[row + 1, col], ψ.C[row + 1, col] = leftorth!(ψ.AC[row + 1, col]) end @@ -23,11 +26,11 @@ function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:Multili # right to left sweep for col in reverse(1:size(ψ, 2)) for row in 1:size(ψ, 1) - ψ.AC[row + 1, col] = AC_projection(CartesianIndex(row, col), - ψ, toapprox, envs) + ψ.AC[row + 1, col] = AC_projection( + CartesianIndex(row, col), ψ, toapprox, envs + ) normalize!(ψ.AC[row + 1, col]) - ψ.C[row + 1, col - 1], temp = rightorth!(_transpose_tail(ψ.AC[row + 1, - col])) + ψ.C[row + 1, col - 1], temp = rightorth!(_transpose_tail(ψ.AC[row + 1, col])) ψ.AR[row + 1, col] = _transpose_front(temp) end transfer_rightenv!(envs, ψ, toapprox, col - 1) @@ -57,8 +60,10 @@ function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:Multili return ψ, envs, ϵ end -function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:MultilineMPS}, - alg::IDMRG2, envs=environments(ψ, toapprox)) +function approximate!( + ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO, <:MultilineMPS}, + alg::IDMRG2, envs = environments(ψ, toapprox) + ) size(ψ, 2) < 2 && throw(ArgumentError("unit cell should be >= 2")) ϵ::Float64 = 2 * alg.tol log = IterLog("IDMRG2") @@ -73,9 +78,11 @@ function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:Multili # sweep from left to right for site in 1:size(ψ, 2) for row in 1:size(ψ, 1) - AC2′ = AC2_projection(CartesianIndex(row, site), ψ, toapprox, envs; - kind=:ACAR) - al, c, ar, = tsvd!(AC2′; trunc=alg.trscheme, alg=alg.alg_svd) + AC2′ = AC2_projection( + CartesianIndex(row, site), ψ, toapprox, envs; + kind = :ACAR + ) + al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[row + 1, site] = al @@ -93,9 +100,11 @@ function approximate!(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:Multili # sweep from right to left for site in reverse(0:(size(ψ, 2) - 1)) for row in 1:size(ψ, 1) - AC2′ = AC2_projection(CartesianIndex(row, site), ψ, toapprox, envs; - kind=:ALAC) - al, c, ar, = tsvd!(AC2′; trunc=alg.trscheme, alg=alg.alg_svd) + AC2′ = AC2_projection( + CartesianIndex(row, site), ψ, toapprox, envs; + kind = :ALAC + ) + al, c, ar, = tsvd!(AC2′; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[row + 1, site] = al diff --git a/src/algorithms/approximate/vomps.jl b/src/algorithms/approximate/vomps.jl index 71a621baa..7011bbe3e 100644 --- a/src/algorithms/approximate/vomps.jl +++ b/src/algorithms/approximate/vomps.jl @@ -1,12 +1,19 @@ -Base.@deprecate(approximate(ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:MultilineMPS}, - alg::VUMPS, envs...; kwargs...), - approximate(ψ, toapprox, - VOMPS(; alg.tol, alg.maxiter, alg.finalize, - alg.verbosity, alg.alg_gauge, alg.alg_environments), - envs...; kwargs...)) - -function approximate(mps::MultilineMPS, toapprox::Tuple{<:MultilineMPO,<:MultilineMPS}, - alg::VOMPS, envs=environments(mps, toapprox)) +Base.@deprecate( + approximate( + ψ::MultilineMPS, toapprox::Tuple{<:MultilineMPO, <:MultilineMPS}, alg::VUMPS, envs...; + kwargs... + ), + approximate( + ψ, toapprox, + VOMPS(; alg.tol, alg.maxiter, alg.finalize, alg.verbosity, alg.alg_gauge, alg.alg_environments), + envs...; kwargs... + ) +) + +function approximate( + mps::MultilineMPS, toapprox::Tuple{<:MultilineMPO, <:MultilineMPS}, alg::VOMPS, + envs = environments(mps, toapprox) + ) log = IterLog("VOMPS") iter = 0 ϵ = calc_galerkin(mps, toapprox..., envs) @@ -38,7 +45,7 @@ end # need to specialize a bunch of functions because different arguments are passed with tuples # TODO: can we avoid this? -function Base.iterate(it::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any,<:Tuple}) +function Base.iterate(it::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any, <:Tuple}) ACs = localupdate_step!(it, state) mps = gauge_step!(it, state, ACs) envs = envs_step!(it, state, mps) @@ -55,28 +62,32 @@ function Base.iterate(it::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any,<:Tu return (mps, envs, ϵ), it.state end -function localupdate_step!(::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any,<:Tuple}, - ::SerialScheduler) +function localupdate_step!( + ::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any, <:Tuple}, ::SerialScheduler + ) alg_orth = QRpos() ACs = similar(state.mps.AC) dst_ACs = state.mps isa Multiline ? eachcol(ACs) : ACs foreach(eachsite(state.mps)) do site - AC = circshift([AC_projection(CartesianIndex(row, site), state.mps, state.operator, - state.envs) - for row in 1:size(state.mps, 1)], 1) - C = circshift([C_projection(CartesianIndex(row, site), state.mps, state.operator, - state.envs) - for row in 1:size(state.mps, 1)], 1) - dst_ACs[site] = regauge!(AC, C; alg=alg_orth) + AC = map(1:size(state.mps, 1)) do row + AC_projection(CartesianIndex(row, site), state.mps, state.operator, state.envs) + end + circshift!(AC, 1) + C = map(1:size(state.mps, 1)) do row + C_projection(CartesianIndex(row, site), state.mps, state.operator, state.envs) + end + circshift!(C, 1) + dst_ACs[site] = regauge!(AC, C; alg = alg_orth) return nothing end return ACs end -function localupdate_step!(::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any,<:Tuple}, - scheduler) +function localupdate_step!( + ::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any, <:Tuple}, scheduler + ) alg_orth = QRpos() ACs = similar(state.mps.AC) @@ -86,24 +97,26 @@ function localupdate_step!(::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any,< local AC, C @sync begin Threads.@spawn begin - AC = circshift([AC_projection(CartesianIndex(row, site), state.mps, - state.operator, state.envs) - for row in 1:size(state.mps, 1)], 1) + AC = map(1:size(state.mps, 1)) do row + AC_projection(CartesianIndex(row, site), state.mps, state.operator, state.envs) + end + circshift!(AC, 1) end Threads.@spawn begin - C = circshift([C_projection(CartesianIndex(row, site), state.mps, - state.operator, state.envs) - for row in 1:size(state.mps, 1)], 1) + C = map(1:size(state.mps, 1)) do row + C_projection(CartesianIndex(row, site), state.mps, state.operator, state.envs) + end + circshift!(C, 1) end end - dst_ACs[site] = regauge!(AC, C; alg=alg_orth) + dst_ACs[site] = regauge!(AC, C; alg = alg_orth) return nothing end return ACs end -function envs_step!(it::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any,<:Tuple}, mps) +function envs_step!(it::IterativeSolver{<:VOMPS}, state::VOMPSState{<:Any, <:Tuple}, mps) alg_environments = updatetol(it.alg_environments, state.iter, state.ϵ) return recalculate!(state.envs, mps, state.operator...; alg_environments.tol) end diff --git a/src/algorithms/changebonds/changebonds.jl b/src/algorithms/changebonds/changebonds.jl index f2cab1b87..700b5ab0b 100644 --- a/src/algorithms/changebonds/changebonds.jl +++ b/src/algorithms/changebonds/changebonds.jl @@ -10,10 +10,12 @@ function changebonds end function changebonds! end # write in terms of MultilineMPS -function changebonds(ψ::InfiniteMPS, operator::InfiniteMPO, alg, - envs=environments(ψ, operator)) - ψ′, envs′ = changebonds(convert(MultilineMPS, ψ), convert(MultilineMPO, operator), alg, - Multiline([envs])) +function changebonds( + ψ::InfiniteMPS, operator::InfiniteMPO, alg, envs = environments(ψ, operator) + ) + ψ′, envs′ = changebonds( + convert(MultilineMPS, ψ), convert(MultilineMPO, operator), alg, Multiline([envs]) + ) return convert(InfiniteMPS, ψ′), envs end diff --git a/src/algorithms/changebonds/optimalexpand.jl b/src/algorithms/changebonds/optimalexpand.jl index 98f33f212..3631c481c 100644 --- a/src/algorithms/changebonds/optimalexpand.jl +++ b/src/algorithms/changebonds/optimalexpand.jl @@ -17,8 +17,10 @@ $(TYPEDFIELDS) trscheme::TruncationScheme end -function changebonds(ψ::InfiniteMPS, H::InfiniteMPOHamiltonian, alg::OptimalExpand, - envs=environments(ψ, H)) +function changebonds( + ψ::InfiniteMPS, H::InfiniteMPOHamiltonian, alg::OptimalExpand, + envs = environments(ψ, H) + ) T = eltype(ψ.AL) AL′ = similar(ψ.AL) AR′ = similar(ψ.AR, tensormaptype(spacetype(T), 1, numind(T) - 1, storagetype(T))) @@ -31,7 +33,7 @@ function changebonds(ψ::InfiniteMPS, H::InfiniteMPOHamiltonian, alg::OptimalExp VL = leftnull(ψ.AL[i]) VR = rightnull!(_transpose_tail(ψ.AR[i + 1])) intermediate = normalize!(adjoint(VL) * AC2 * adjoint(VR)) - U, _, V, = tsvd!(intermediate; trunc=alg.trscheme, alg=alg.alg_svd) + U, _, V, = tsvd!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd) AL′[i] = VL * U AR′[i + 1] = V * VR @@ -42,7 +44,7 @@ function changebonds(ψ::InfiniteMPS, H::InfiniteMPOHamiltonian, alg::OptimalExp return newψ, envs end -function changebonds(ψ::MultilineMPS, H, alg::OptimalExpand, envs=environments(ψ, H)) +function changebonds(ψ::MultilineMPS, H, alg::OptimalExpand, envs = environments(ψ, H)) TL = eltype(ψ.AL) AL′ = PeriodicMatrix{TL}(undef, size(ψ.AL)) TR = tensormaptype(spacetype(TL), 1, numind(TL) - 1, storagetype(TL)) @@ -57,7 +59,7 @@ function changebonds(ψ::MultilineMPS, H, alg::OptimalExpand, envs=environments( VL = leftnull(ψ.AL[i, j]) VR = rightnull!(_transpose_tail(ψ.AR[i, j + 1])) intermediate = normalize!(adjoint(VL) * AC2 * adjoint(VR)) - U, _, V, = tsvd!(intermediate; trunc=alg.trscheme, alg=alg.alg_svd) + U, _, V, = tsvd!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd) AL′[i, j] = VL * U AR′[i, j + 1] = V * VR @@ -68,10 +70,10 @@ function changebonds(ψ::MultilineMPS, H, alg::OptimalExpand, envs=environments( return newψ, envs end -function changebonds(ψ::AbstractFiniteMPS, H, alg::OptimalExpand, envs=environments(ψ, H)) +function changebonds(ψ::AbstractFiniteMPS, H, alg::OptimalExpand, envs = environments(ψ, H)) return changebonds!(copy(ψ), H, alg, envs) end -function changebonds!(ψ::AbstractFiniteMPS, H, alg::OptimalExpand, envs=environments(ψ, H)) +function changebonds!(ψ::AbstractFiniteMPS, H, alg::OptimalExpand, envs = environments(ψ, H)) #inspired by the infinite mps algorithm, alternative is to use https://arxiv.org/pdf/1501.05504.pdf #the idea is that we always want to expand the state in such a way that there are zeros at site i @@ -88,12 +90,12 @@ function changebonds!(ψ::AbstractFiniteMPS, H, alg::OptimalExpand, envs=environ #Use this nullspaces and SVD decomposition to determine the optimal expansion space intermediate = normalize!(adjoint(NL) * AC2 * adjoint(NR)) - _, _, V, = tsvd!(intermediate; trunc=alg.trscheme, alg=alg.alg_svd) + _, _, V, = tsvd!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd) ar_re = V * NR ar_le = zerovector!(similar(ar_re, codomain(ψ.AC[i]) ← space(V, 1))) - (nal, nc) = leftorth!(catdomain(ψ.AC[i], ar_le); alg=QRpos()) + nal, nc = leftorth!(catdomain(ψ.AC[i], ar_le); alg = QRpos()) nar = _transpose_front(catcodomain(_transpose_tail(ψ.AR[i + 1]), ar_re)) ψ.AC[i] = (nal, nc) diff --git a/src/algorithms/changebonds/randexpand.jl b/src/algorithms/changebonds/randexpand.jl index f54c88134..c933519f5 100644 --- a/src/algorithms/changebonds/randexpand.jl +++ b/src/algorithms/changebonds/randexpand.jl @@ -29,7 +29,7 @@ function changebonds(ψ::InfiniteMPS, alg::RandExpand) VL = leftnull(ψ.AL[i]) VR = rightnull!(_transpose_tail(ψ.AR[i + 1])) intermediate = normalize!(adjoint(VL) * AC2 * adjoint(VR)) - U, _, V, = tsvd!(intermediate; trunc=alg.trscheme, alg=alg.alg_svd) + U, _, V, = tsvd!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd) AL′[i] = VL * U AR′[i + 1] = V * VR @@ -53,12 +53,12 @@ function changebonds!(ψ::AbstractFiniteMPS, alg::RandExpand) #Use this nullspaces and SVD decomposition to determine the optimal expansion space intermediate = normalize!(adjoint(NL) * AC2 * adjoint(NR)) - _, _, V, = tsvd!(intermediate; trunc=alg.trscheme, alg=alg.alg_svd) + _, _, V, = tsvd!(intermediate; trunc = alg.trscheme, alg = alg.alg_svd) ar_re = V * NR ar_le = zerovector!(similar(ar_re, codomain(ψ.AC[i]) ← space(V, 1))) - (nal, nc) = leftorth!(catdomain(ψ.AC[i], ar_le); alg=QRpos()) + (nal, nc) = leftorth!(catdomain(ψ.AC[i], ar_le); alg = QRpos()) nar = _transpose_front(catcodomain(_transpose_tail(ψ.AR[i + 1]), ar_re)) ψ.AC[i] = (nal, nc) diff --git a/src/algorithms/changebonds/svdcut.jl b/src/algorithms/changebonds/svdcut.jl index 85497a2ea..cd60bf1b8 100644 --- a/src/algorithms/changebonds/svdcut.jl +++ b/src/algorithms/changebonds/svdcut.jl @@ -18,9 +18,9 @@ end function changebonds(ψ::AbstractFiniteMPS, alg::SvdCut; kwargs...) return changebonds!(copy(ψ), alg; kwargs...) end -function changebonds!(ψ::AbstractFiniteMPS, alg::SvdCut; normalize::Bool=true) +function changebonds!(ψ::AbstractFiniteMPS, alg::SvdCut; normalize::Bool = true) for i in (length(ψ) - 1):-1:1 - U, S, V, = tsvd(ψ.C[i]; trunc=alg.trscheme, alg=alg.alg_svd) + U, S, V, = tsvd(ψ.C[i]; trunc = alg.trscheme, alg = alg.alg_svd) AL′ = ψ.AL[i] * U ψ.AC[i] = (AL′, complex(S)) AR′ = _transpose_front(V * _transpose_tail(ψ.AR[i + 1])) @@ -44,7 +44,7 @@ function changebonds!(mpo::FiniteMPO, alg::SvdCut) O_left = transpose(mpo[1], ((3, 1, 2), (4,))) local O_right for i in 2:length(mpo) - U, S, V, = tsvd!(O_left; trunc=alg.trscheme, alg=alg.alg_svd) + U, S, V, = tsvd!(O_left; trunc = alg.trscheme, alg = alg.alg_svd) @inbounds mpo[i - 1] = transpose(U, ((2, 3), (1, 4))) if i < length(mpo) @plansor O_left[-3 -1 -2; -4] := S[-1; 1] * V[1; 2] * mpo[i][2 -2; -3 -4] @@ -55,13 +55,13 @@ function changebonds!(mpo::FiniteMPO, alg::SvdCut) # right to left for i in (length(mpo) - 1):-1:1 - U, S, V, = tsvd!(O_right; trunc=alg.trscheme, alg=alg.alg_svd) + U, S, V, = tsvd!(O_right; trunc = alg.trscheme, alg = alg.alg_svd) @inbounds mpo[i + 1] = transpose(V, ((1, 4), (2, 3))) if i > 1 @plansor O_right[-1; -3 -4 -2] := mpo[i][-1 -2; -3 2] * U[2; 1] * S[1; -4] else @plansor _O[-1 -2; -3 -4] := mpo[1][-1 -2; -3 2] * U[2; 1] * - S[1; -4] + S[1; -4] @inbounds mpo[1] = _O end end @@ -84,7 +84,7 @@ function changebonds(ψ::InfiniteMPS, alg::SvdCut) ncr = ψ.C[1] for i in 1:length(ψ) - U, ncr, = tsvd(ψ.C[i]; trunc=alg.trscheme, alg=alg.alg_svd) + U, ncr, = tsvd(ψ.C[i]; trunc = alg.trscheme, alg = alg.alg_svd) copied[i] = copied[i] * U copied[i + 1] = _transpose_front(U' * _transpose_tail(copied[i + 1])) end @@ -102,6 +102,6 @@ function changebonds(ψ::InfiniteMPS, alg::SvdCut) return normalize!(ψ) end -function changebonds(ψ, H, alg::SvdCut, envs=environments(ψ, H)) - return (changebonds(ψ, alg), envs) +function changebonds(ψ, H, alg::SvdCut, envs = environments(ψ, H)) + return changebonds(ψ, alg), envs end diff --git a/src/algorithms/changebonds/vumpssvd.jl b/src/algorithms/changebonds/vumpssvd.jl index 4c1dadc65..697915978 100644 --- a/src/algorithms/changebonds/vumpssvd.jl +++ b/src/algorithms/changebonds/vumpssvd.jl @@ -9,10 +9,10 @@ $(TYPEDFIELDS) """ @kwdef struct VUMPSSvdCut <: Algorithm "algorithm used for gauging the `InfiniteMPS`" - alg_gauge = Defaults.alg_gauge(; dynamic_tols=false) + alg_gauge = Defaults.alg_gauge(; dynamic_tols = false) "algorithm used for the eigenvalue solvers" - alg_eigsolve = Defaults.alg_eigsolve(; dynamic_tols=false) + alg_eigsolve = Defaults.alg_eigsolve(; dynamic_tols = false) "algorithm used for the singular value decomposition" alg_svd = Defaults.alg_svd() @@ -21,8 +21,9 @@ $(TYPEDFIELDS) trscheme::TruncationScheme end -function changebonds_1(state::InfiniteMPS, H, alg::VUMPSSvdCut, - envs=environments(state, H)) # would be more efficient if we also repeated envs +function changebonds_1( + state::InfiniteMPS, H, alg::VUMPSSvdCut, envs = environments(state, H) + ) # would be more efficient if we also repeated envs # the unitcell==1 case is unique, because there you have a sef-consistency condition # expand the one site to two sites @@ -36,18 +37,19 @@ function changebonds_1(state::InfiniteMPS, H, alg::VUMPSSvdCut, # collapse back to 1 site if D2 != D1 - cut_alg = SvdCut(; alg.alg_svd, trscheme=truncspace(infimum(D1, D2))) + cut_alg = SvdCut(; alg.alg_svd, trscheme = truncspace(infimum(D1, D2))) nstate, nenvs = changebonds(nstate, nH, cut_alg, nenvs) end - collapsed = InfiniteMPS([nstate.AL[1]], nstate.C[1]; alg.alg_gauge.tol, - alg.alg_gauge.maxiter) + collapsed = InfiniteMPS( + [nstate.AL[1]], nstate.C[1]; alg.alg_gauge.tol, alg.alg_gauge.maxiter + ) recalculate!(envs, collapsed, H, collapsed) return collapsed, envs end -function changebonds_n(state::InfiniteMPS, H, alg::VUMPSSvdCut, envs=environments(state, H)) +function changebonds_n(state::InfiniteMPS, H, alg::VUMPSSvdCut, envs = environments(state, H)) meps = 0.0 for loc in 1:length(state) @plansor AC2[-1 -2; -3 -4] := state.AC[loc][-1 -2; 1] * state.AR[loc + 1][1 -4; -3] @@ -59,13 +61,13 @@ function changebonds_n(state::InfiniteMPS, H, alg::VUMPSSvdCut, envs=environment _, nC2 = fixedpoint(Hc, state.C[loc + 1], :SR, alg.alg_eigsolve) #svd ac2, get new AL1 and S,V ---> AC - AL1, S, V, eps = tsvd!(nAC2; trunc=alg.trscheme, alg=alg.alg_svd) + AL1, S, V, eps = tsvd!(nAC2; trunc = alg.trscheme, alg = alg.alg_svd) @plansor AC[-1 -2; -3] := S[-1; 1] * V[1; -3 -2] meps = max(eps, meps) #find AL2 from AC and C as in vumps paper - QAC, _ = leftorth(AC; alg=QRpos()) - QC, _ = leftorth(nC2; alg=QRpos()) + QAC, _ = leftorth(AC; alg = QRpos()) + QC, _ = leftorth(nC2; alg = QRpos()) dom_map = isometry(domain(QC), domain(QAC)) @plansor AL2[-1 -2; -3] := QAC[-1 -2; 1] * conj(dom_map[2; 1]) * conj(QC[-3; 2]) @@ -80,7 +82,7 @@ function changebonds_n(state::InfiniteMPS, H, alg::VUMPSSvdCut, envs=environment return state, envs end -function changebonds(state::InfiniteMPS, H, alg::VUMPSSvdCut, envs=environments(state, H)) +function changebonds(state::InfiniteMPS, H, alg::VUMPSSvdCut, envs = environments(state, H)) return length(state) == 1 ? changebonds_1(state, H, alg, envs) : - changebonds_n(state, H, alg, envs) + changebonds_n(state, H, alg, envs) end diff --git a/src/algorithms/correlators.jl b/src/algorithms/correlators.jl index a5dfbfdf5..acd3adebf 100644 --- a/src/algorithms/correlators.jl +++ b/src/algorithms/correlators.jl @@ -11,8 +11,9 @@ function correlator(state::AbstractMPS, O₁::MPOTensor, O₂::MPOTensor, i::Int return first(correlator(state, O₁, O₂, i, j:j)) end -function correlator(state::AbstractMPS, O₁::MPOTensor, O₂::MPOTensor, i::Int, - js::AbstractRange{Int}) +function correlator( + state::AbstractMPS, O₁::MPOTensor, O₂::MPOTensor, i::Int, js::AbstractRange{Int} + ) first(js) > i || @error "i should be smaller than j ($i, $(first(js)))" S₁ = _firstspace(O₁) S₁ == oneunit(S₁) || throw(ArgumentError("O₁ should start with a trivial leg.")) @@ -23,7 +24,7 @@ function correlator(state::AbstractMPS, O₁::MPOTensor, O₂::MPOTensor, i::Int U = ones(scalartype(state), S₁) @plansor Vₗ[-1 -2; -3] := state.AC[i][3 4; -3] * conj(U[1]) * O₁[1 2; 4 -2] * - conj(state.AC[i][3 2; -1]) + conj(state.AC[i][3 2; -1]) ctr = i + 1 for (k, j) in enumerate(js) @@ -31,14 +32,15 @@ function correlator(state::AbstractMPS, O₁::MPOTensor, O₂::MPOTensor, i::Int Vₗ = Vₗ * TransferMatrix(state.AR[ctr:(j - 1)]) end G[k] = @plansor Vₗ[2 3; 5] * state.AR[j][5 6; 7] * O₂[3 4; 6 1] * U[1] * - conj(state.AR[j][2 4; 7]) + conj(state.AR[j][2 4; 7]) ctr = j end return G end -function correlator(state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any,S,2,2}, i::Int, - j) where {S} +function correlator( + state::AbstractMPS, O₁₂::AbstractTensorMap{<:Any, S, 2, 2}, i::Int, j + ) where {S} O₁, O₂ = decompose_localmpo(add_util_leg(O₁₂)) return correlator(state, O₁, O₂, i, j) end diff --git a/src/algorithms/derivatives/derivatives.jl b/src/algorithms/derivatives/derivatives.jl index 869da571e..1f3980e37 100644 --- a/src/algorithms/derivatives/derivatives.jl +++ b/src/algorithms/derivatives/derivatives.jl @@ -69,8 +69,9 @@ See also [`AC2_projection`](@ref). # boilerplate for the derivative operators for hamiltonian in (:C_hamiltonian, :AC_hamiltonian, :AC2_hamiltonian) - @eval function $hamiltonian(site::CartesianIndex{2}, below, operator::MultilineMPO, - above, envs) + @eval function $hamiltonian( + site::CartesianIndex{2}, below, operator::MultilineMPO, above, envs + ) row, col = Tuple(site) return $hamiltonian(col, below[row + 1], operator[row], above[row], envs[row]) end @@ -90,12 +91,13 @@ for hamiltonian in (:C_hamiltonian, :AC_hamiltonian, :AC2_hamiltonian) end return LinearCombination(Hs, operator.coeffs) end - @eval function $hamiltonian(site::Int, below, operator::LazySum, above, - envs::MultipleEnvironments) + @eval function $hamiltonian( + site::Int, below, operator::LazySum, above, envs::MultipleEnvironments + ) Hs = map(operator.ops, envs.envs) do o, env return $hamiltonian(site, below, o, above, env) end - elT = Union{D,MultipliedOperator{D}} where {D<:DerivativeOperator} + elT = Union{D, MultipliedOperator{D}} where {D <: DerivativeOperator} return LazySum{elT}(Hs) end end @@ -170,11 +172,15 @@ for kind in (:C, :AC, :AC2) @eval function $projection(site, below, above::AbstractMPS, envs; kwargs...) return $projection(site, below, nothing, above, envs; kwargs...) end - @eval function $projection(site::CartesianIndex{2}, below::MultilineMPS, operator, - above::MultilineMPS, envs; kwargs...) + @eval function $projection( + site::CartesianIndex{2}, below::MultilineMPS, operator, + above::MultilineMPS, envs; kwargs... + ) row, col = Tuple(site) - return $projection(col, below[row + 1], operator[row], above[row], envs[row]; - kwargs...) + return $projection( + col, below[row + 1], operator[row], above[row], envs[row]; + kwargs... + ) end @eval function $projection(site, below, above::LazySum, envs; kwargs...) return sum(zip(above.ops, envs.envs)) do x @@ -200,16 +206,10 @@ end Base.:*(H::Multiline{<:DerivativeOperator}, x::AbstractVector) = H(x) # time dependent derivative operators +const DerivativeOrMultiplied{D <: DerivativeOperator} = Union{MultipliedOperator{D}, D} + (h::UntimedOperator{<:DerivativeOperator})(y, args...) = h.f * h.op(y) (h::TimedOperator{<:DerivativeOperator})(y, t::Number) = h.f(t) * h.op(y) -function (x::LazySum{<:Union{MultipliedOperator{D},D} where {D<:DerivativeOperator}})(y, - t::Number) - return sum(O -> O(y, t), x) -end -function (x::LazySum{<:Union{MultipliedOperator{D},D} where {D<:DerivativeOperator}})(y) - return sum(O -> O(y), x) -end -function Base.:*(h::LazySum{<:Union{D,MultipliedOperator{D}} where {D<:DerivativeOperator}}, - v) - return h(v) -end +(x::LazySum{<:DerivativeOrMultiplied})(y, t::Number) = sum(O -> O(y, t), x) +(x::LazySum{<:DerivativeOrMultiplied})(y) = sum(O -> O(y), x) +Base.:*(h::LazySum{<:Union{DerivativeOrMultiplied}}, v) = h(v) diff --git a/src/algorithms/derivatives/hamiltonian_derivatives.jl b/src/algorithms/derivatives/hamiltonian_derivatives.jl index 3fa5ab263..b922205f8 100644 --- a/src/algorithms/derivatives/hamiltonian_derivatives.jl +++ b/src/algorithms/derivatives/hamiltonian_derivatives.jl @@ -4,15 +4,16 @@ Efficient operator for representing the single-site derivative of a `MPOHamiltonian` sandwiched between two MPSs. In particular, this operator aims to make maximal use of the structure of the `MPOHamiltonian` to reduce the number of operations required to apply the operator to a tensor. """ -struct JordanMPO_AC_Hamiltonian{O1,O2,O3} <: DerivativeOperator - D::Union{O1,Missing} # onsite - I::Union{O1,Missing} # not started - E::Union{O1,Missing} # finished - C::Union{O2,Missing} # starting - B::Union{O2,Missing} # ending +struct JordanMPO_AC_Hamiltonian{O1, O2, O3} <: DerivativeOperator + D::Union{O1, Missing} # onsite + I::Union{O1, Missing} # not started + E::Union{O1, Missing} # finished + C::Union{O2, Missing} # starting + B::Union{O2, Missing} # ending A::O3 # continuing - function JordanMPO_AC_Hamiltonian(onsite, not_started, finished, starting, ending, - continuing) + function JordanMPO_AC_Hamiltonian( + onsite, not_started, finished, starting, ending, continuing + ) # obtaining storagetype of environments since these should have already mixed # the types of the operator and state gl = continuing[1] @@ -20,12 +21,14 @@ struct JordanMPO_AC_Hamiltonian{O1,O2,O3} <: DerivativeOperator M = storagetype(gl) O1 = tensormaptype(S, 1, 1, M) O2 = tensormaptype(S, 2, 2, M) - return new{O1,O2,typeof(continuing)}(onsite, not_started, finished, starting, - ending, continuing) + return new{O1, O2, typeof(continuing)}( + onsite, not_started, finished, starting, ending, continuing + ) end - function JordanMPO_AC_Hamiltonian{O1,O2,O3}(onsite, not_started, finished, starting, - ending, continuing) where {O1,O2,O3} - return new{O1,O2,O3}(onsite, not_started, finished, starting, ending, continuing) + function JordanMPO_AC_Hamiltonian{O1, O2, O3}( + onsite, not_started, finished, starting, ending, continuing + ) where {O1, O2, O3} + return new{O1, O2, O3}(onsite, not_started, finished, starting, ending, continuing) end end @@ -35,17 +38,17 @@ end Efficient operator for representing the single-site derivative of a `MPOHamiltonian` sandwiched between two MPSs. In particular, this operator aims to make maximal use of the structure of the `MPOHamiltonian` to reduce the number of operations required to apply the operator to a tensor. """ -struct JordanMPO_AC2_Hamiltonian{O1,O2,O3,O4} <: DerivativeOperator - II::Union{O1,Missing} # not_started - IC::Union{O2,Missing} # starting right - ID::Union{O1,Missing} # onsite right - CB::Union{O2,Missing} # starting left - ending right - CA::Union{O3,Missing} # starting left - continuing right - AB::Union{O3,Missing} # continuing left - ending right - AA::O4 # continuing left - continuing right - BE::Union{O2,Missing} # ending left - DE::Union{O1,Missing} # onsite left - EE::Union{O1,Missing} # finished +struct JordanMPO_AC2_Hamiltonian{O1, O2, O3, O4} <: DerivativeOperator + II::Union{O1, Missing} # not_started + IC::Union{O2, Missing} # starting right + ID::Union{O1, Missing} # onsite right + CB::Union{O2, Missing} # starting left - ending right + CA::Union{O3, Missing} # starting left - continuing right + AB::Union{O3, Missing} # continuing left - ending right + AA::O4 # continuing left - continuing right + BE::Union{O2, Missing} # ending left + DE::Union{O1, Missing} # onsite left + EE::Union{O1, Missing} # finished function JordanMPO_AC2_Hamiltonian(II, IC, ID, CB, CA, AB, AA, BE, DE, EE) # obtaining storagetype of environments since these should have already mixed # the types of the operator and state @@ -55,22 +58,27 @@ struct JordanMPO_AC2_Hamiltonian{O1,O2,O3,O4} <: DerivativeOperator O1 = tensormaptype(S, 1, 1, M) O2 = tensormaptype(S, 2, 2, M) O3 = tensormaptype(S, 3, 3, M) - return new{O1,O2,O3,typeof(AA)}(II, IC, ID, CB, CA, AB, AA, BE, DE, EE) + return new{O1, O2, O3, typeof(AA)}(II, IC, ID, CB, CA, AB, AA, BE, DE, EE) end - function JordanMPO_AC2_Hamiltonian{O1,O2,O3,O4}(II, IC, ID, CB, CA, AB, AA, BE, DE, - EE) where {O1,O2,O3,O4} - return new{O1,O2,O3,O4}(II, IC, ID, CB, CA, AB, AA, BE, DE, EE) + function JordanMPO_AC2_Hamiltonian{O1, O2, O3, O4}( + II, IC, ID, CB, CA, AB, AA, BE, DE, EE + ) where {O1, O2, O3, O4} + return new{O1, O2, O3, O4}(II, IC, ID, CB, CA, AB, AA, BE, DE, EE) end end # Constructors # ------------ -const _HAM_MPS_TYPES = Union{FiniteMPS{<:MPSTensor},WindowMPS{<:MPSTensor}, - InfiniteMPS{<:MPSTensor}} +const _HAM_MPS_TYPES = Union{ + FiniteMPS{<:MPSTensor}, + WindowMPS{<:MPSTensor}, + InfiniteMPS{<:MPSTensor}, +} -function AC_hamiltonian(site::Int, below::_HAM_MPS_TYPES, - operator::MPOHamiltonian{<:JordanMPOTensor}, above::_HAM_MPS_TYPES, - envs) +function AC_hamiltonian( + site::Int, below::_HAM_MPS_TYPES, operator::MPOHamiltonian{<:JordanMPOTensor}, + above::_HAM_MPS_TYPES, envs + ) GL = leftenv(envs, site, below) GR = rightenv(envs, site, below) W = operator[site] @@ -134,13 +142,15 @@ function AC_hamiltonian(site::Int, below::_HAM_MPS_TYPES, A = W.A continuing = (GL[2:(end - 1)], A, GR[2:(end - 1)]) - return JordanMPO_AC_Hamiltonian(onsite, not_started, finished, starting, ending, - continuing) + return JordanMPO_AC_Hamiltonian( + onsite, not_started, finished, starting, ending, continuing + ) end -function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, - operator::MPOHamiltonian{<:JordanMPOTensor}, above::_HAM_MPS_TYPES, - envs) +function AC2_hamiltonian( + site::Int, below::_HAM_MPS_TYPES, operator::MPOHamiltonian{<:JordanMPOTensor}, + above::_HAM_MPS_TYPES, envs + ) GL = leftenv(envs, site, below) GR = rightenv(envs, site + 1, below) W1 = operator[site] @@ -149,7 +159,7 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, # starting left - continuing right if nonzero_length(W1.C) > 0 && nonzero_length(W2.A) > 0 @plansor CA_[-1 -2 -3; -4 -5 -6] ≔ W1.C[-1; -4 2] * W2.A[2 -2; -5 1] * - GR[2:(end - 1)][-6 1; -3] + GR[2:(end - 1)][-6 1; -3] CA = only(CA_) else CA = missing @@ -158,7 +168,7 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, # continuing left - ending right if nonzero_length(W1.A) > 0 && nonzero_length(W2.B) > 0 @plansor AB_[-1 -2 -3; -4 -5 -6] ≔ GL[2:(end - 1)][-1 2; -4] * W1.A[2 -2; -5 1] * - W2.B[1 -3; -6] + W2.B[1 -3; -6] AB = only(AB_) else AB = missing @@ -168,12 +178,11 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, if nonzero_length(W1.C) > 0 && nonzero_length(W2.B) > 0 if !ismissing(CA) @plansor CA[-1 -2 -3; -4 -5 -6] += W1.C[-1; -4 1] * W2.B[1 -2; -5] * - removeunit(GR[end], 2)[-6; -3] + removeunit(GR[end], 2)[-6; -3] CB = missing elseif !ismissing(AB) @plansor AB[-1 -2 -3; -4 -5 -6] += removeunit(GL[1], 2)[-1; -4] * - W1.C[-2; -5 1] * - W2.B[1 -3; -6] + W1.C[-2; -5 1] * W2.B[1 -3; -6] CB = missing else @plansor CB_[-1 -2; -3 -4] ≔ W1.C[-1; -3 1] * W2.B[1 -2; -4] @@ -188,7 +197,7 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, if !ismissing(CA) I = id(storagetype(GR[1]), physicalspace(W1)) @plansor CA[-1 -2 -3; -4 -5 -6] += (I[-1; -4] * W2.C[-2; -5 1]) * - GR[2:(end - 1)][-6 1; -3] + GR[2:(end - 1)][-6 1; -3] IC = missing else @plansor IC[-1 -2; -3 -4] ≔ W2.C[-1; -3 1] * GR[2:(end - 1)][-4 1; -2] @@ -202,7 +211,7 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, if !ismissing(AB) I = id(storagetype(GL[end]), physicalspace(W2)) @plansor AB[-1 -2 -3; -4 -5 -6] += GL[2:(end - 1)][-1 1; -4] * - (W1.B[1 -2; -5] * I[-3; -6]) + (W1.B[1 -2; -5] * I[-3; -6]) BE = missing else @plansor BE[-1 -2; -3 -4] ≔ GL[2:(end - 1)][-1 2; -3] * W1.B[2 -2; -4] @@ -219,7 +228,7 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, elseif !ismissing(AB) I = id(storagetype(GL[end]), physicalspace(W2)) @plansor AB[-1 -2 -3; -4 -5 -6] += removeunit(GL[1], 2)[-1; -4] * - (W1.D[-2; -5] * I[-3; -6]) + (W1.D[-2; -5] * I[-3; -6]) DE = missing # TODO: could also try in CA? else @@ -237,7 +246,7 @@ function AC2_hamiltonian(site::Int, below::_HAM_MPS_TYPES, elseif !ismissing(CA) I = id(storagetype(GR[1]), physicalspace(W1)) @plansor CA[-1 -2 -3; -4 -5 -6] += (I[-1; -4] * W2.D[-2; -5]) * - removeunit(GR[end], 2)[-6; -3] + removeunit(GR[end], 2)[-6; -3] ID = missing else ID = only(W2.D) @@ -318,7 +327,7 @@ function (H::JordanMPO_AC2_Hamiltonian)(x::MPOTensor) if nonzero_length(A1) > 0 && nonzero_length(A2) > 0 # TODO: there are too many entries here, this could be further optimized @plansor y[-1 -2; -3 -4] += GL[-1 7; 6] * x[6 5; 1 3] * A1[7 -2; 5 4] * - A2[4 -4; 3 2] * GR[1 2; -3] + A2[4 -4; 3 2] * GR[1 2; -3] end return y diff --git a/src/algorithms/derivatives/mpo_derivatives.jl b/src/algorithms/derivatives/mpo_derivatives.jl index 4ab7d08ed..12b030b1e 100644 --- a/src/algorithms/derivatives/mpo_derivatives.jl +++ b/src/algorithms/derivatives/mpo_derivatives.jl @@ -3,7 +3,7 @@ Effective local operator obtained from taking the partial derivative of an MPS-MPO-MPS sandwich. """ -struct MPODerivativeOperator{L,O<:Tuple,R} <: DerivativeOperator +struct MPODerivativeOperator{L, O <: Tuple, R} <: DerivativeOperator leftenv::L operators::O rightenv::R @@ -11,13 +11,13 @@ end Base.length(H::MPODerivativeOperator) = length(H.operators) -const MPO_C_Hamiltonian{L,R} = MPODerivativeOperator{L,Tuple{},R} +const MPO_C_Hamiltonian{L, R} = MPODerivativeOperator{L, Tuple{}, R} MPO_C_Hamiltonian(GL, GR) = MPODerivativeOperator(GL, (), GR) -const MPO_AC_Hamiltonian{L,O,R} = MPODerivativeOperator{L,Tuple{O},R} +const MPO_AC_Hamiltonian{L, O, R} = MPODerivativeOperator{L, Tuple{O}, R} MPO_AC_Hamiltonian(GL, O, GR) = MPODerivativeOperator(GL, (O,), GR) -const MPO_AC2_Hamiltonian{L,O₁,O₂,R} = MPODerivativeOperator{L,Tuple{O₁,O₂},R} +const MPO_AC2_Hamiltonian{L, O₁, O₂, R} = MPODerivativeOperator{L, Tuple{O₁, O₂}, R} MPO_AC2_Hamiltonian(GL, O1, O2, GR) = MPODerivativeOperator(GL, (O1, O2), GR) # Constructors @@ -31,8 +31,9 @@ function AC_hamiltonian(site::Int, below, operator, above, envs) end function AC2_hamiltonian(site::Int, below, operator, above, envs) O1, O2 = isnothing(operator) ? (nothing, nothing) : (operator[site], operator[site + 1]) - return MPO_AC2_Hamiltonian(leftenv(envs, site, below), O1, O2, - rightenv(envs, site + 1, below)) + return MPO_AC2_Hamiltonian( + leftenv(envs, site, below), O1, O2, rightenv(envs, site + 1, below) + ) end # Properties @@ -40,65 +41,67 @@ end function TensorKit.domain(H::MPODerivativeOperator) V_l = right_virtualspace(H.leftenv) V_r = left_virtualspace(H.rightenv) - V_o = prod(physicalspace, H.O; init=one(V_l)) + V_o = prod(physicalspace, H.O; init = one(V_l)) return V_l ⊗ V_o ⊗ V_r end function TensorKit.codomain(H::MPODerivativeOperator) V_l = left_virtualspace(H.leftenv) V_r = right_virtualspace(H.rightenv) - V_o = prod(physicalspace, H.O; init=one(V_l)) + V_o = prod(physicalspace, H.O; init = one(V_l)) return V_l ⊗ V_o ⊗ V_r end # Actions # ------- -function (h::MPO_C_Hamiltonian{<:MPSBondTensor,<:MPSBondTensor})(x::MPSBondTensor) +function (h::MPO_C_Hamiltonian{<:MPSBondTensor, <:MPSBondTensor})(x::MPSBondTensor) @plansor y[-1; -2] ≔ h.leftenv[-1; 1] * x[1; 2] * h.rightenv[2; -2] return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_C_Hamiltonian{<:MPSTensor,<:MPSTensor})(x::MPSBondTensor) +function (h::MPO_C_Hamiltonian{<:MPSTensor, <:MPSTensor})(x::MPSBondTensor) @plansor y[-1; -2] ≔ h.leftenv[-1 3; 1] * x[1; 2] * h.rightenv[2 3; -2] return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_AC_Hamiltonian{<:MPSTensor,<:MPOTensor,<:MPSTensor})(x::MPSTensor) - @plansor y[-1 -2; -3] ≔ h.leftenv[-1 5; 4] * x[4 2; 1] * - h.operators[1][5 -2; 2 3] * h.rightenv[1 3; -3] +function (h::MPO_AC_Hamiltonian{<:MPSTensor, <:MPOTensor, <:MPSTensor})(x::MPSTensor) + @plansor y[-1 -2; -3] ≔ h.leftenv[-1 5; 4] * x[4 2; 1] * h.operators[1][5 -2; 2 3] * + h.rightenv[1 3; -3] return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_AC_Hamiltonian{<:MPSTensor,<:Number,<:MPSTensor})(x::MPSTensor) - @plansor y[-1 -2; -3] ≔ (h.leftenv[-1 5; 4] * x[4 6; 1] * τ[6 5; 7 -2] * - h.rightenv[1 7; -3]) * only(h.operators) +function (h::MPO_AC_Hamiltonian{<:MPSTensor, <:Number, <:MPSTensor})(x::MPSTensor) + @plansor y[-1 -2; -3] ≔ ( + h.leftenv[-1 5; 4] * x[4 6; 1] * τ[6 5; 7 -2] * h.rightenv[1 7; -3] + ) * only(h.operators) return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_AC_Hamiltonian{<:MPSBondTensor,Nothing,<:MPSBondTensor})(x::MPSTensor) - @plansor y[-1 -2; -3] ≔ h.leftenv[-1; 2] * x[2 -2; 1] * h.rightenv[1; -3] +function (h::MPO_AC_Hamiltonian{<:MPSBondTensor, Nothing, <:MPSBondTensor})(x::MPSTensor) + return @plansor y[-1 -2; -3] ≔ h.leftenv[-1; 2] * x[2 -2; 1] * h.rightenv[1; -3] end -function (h::MPO_AC_Hamiltonian{<:MPSTensor,<:MPOTensor,<:MPSTensor})(x::GenericMPSTensor{<:Any, - 3}) +function (h::MPO_AC_Hamiltonian{<:MPSTensor, <:MPOTensor, <:MPSTensor})( + x::GenericMPSTensor{<:Any, 3} + ) @plansor y[-1 -2 -3; -4] ≔ h.leftenv[-1 7; 6] * x[6 4 2; 1] * - h.operators[1][7 -2; 4 5] * τ[5 -3; 2 3] * - h.rightenv[1 3; -4] + h.operators[1][7 -2; 4 5] * τ[5 -3; 2 3] * h.rightenv[1 3; -4] return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_AC2_Hamiltonian{<:MPSBondTensor,Nothing,Nothing,<:MPSBondTensor})(x::MPOTensor) +function (h::MPO_AC2_Hamiltonian{<:MPSBondTensor, Nothing, Nothing, <:MPSBondTensor})( + x::MPOTensor + ) @plansor y[-1 -2; -3 -4] ≔ h.leftenv[-1; 1] * x[1 -2; 2 -4] * h.rightenv[2 -3] return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_AC2_Hamiltonian{<:MPSTensor,<:MPOTensor,<:MPOTensor,<:MPSTensor})(x::MPOTensor) +function (h::MPO_AC2_Hamiltonian{<:MPSTensor, <:MPOTensor, <:MPOTensor, <:MPSTensor})( + x::MPOTensor + ) @plansor y[-1 -2; -3 -4] ≔ h.leftenv[-1 7; 6] * x[6 5; 1 3] * - h.operators[1][7 -2; 5 4] * h.operators[2][4 -4; 3 2] * - h.rightenv[1 2; -3] + h.operators[1][7 -2; 5 4] * h.operators[2][4 -4; 3 2] * h.rightenv[1 2; -3] return y isa AbstractBlockTensorMap ? only(y) : y end -function (h::MPO_AC2_Hamiltonian{<:MPSTensor,<:MPOTensor,<:MPOTensor,<:MPSTensor})(x::AbstractTensorMap{<:Any, - <:Any, - 3, - 3}) +function (h::MPO_AC2_Hamiltonian{<:MPSTensor, <:MPOTensor, <:MPOTensor, <:MPSTensor})( + x::AbstractTensorMap{<:Any, <:Any, 3, 3} + ) @plansor y[-1 -2 -3; -4 -5 -6] ≔ h.leftenv[-1 11; 10] * x[10 8 6; 1 2 4] * - h.rightenv[1 3; -4] * - h.operators[1][11 -2; 8 9] * τ[9 -3; 6 7] * - h.operators[2][7 -6; 4 5] * τ[5 -5; 2 3] + h.rightenv[1 3; -4] * h.operators[1][11 -2; 8 9] * τ[9 -3; 6 7] * + h.operators[2][7 -6; 4 5] * τ[5 -5; 2 3] return y isa AbstractBlockTensorMap ? only(y) : y end diff --git a/src/algorithms/derivatives/projection_derivatives.jl b/src/algorithms/derivatives/projection_derivatives.jl index 10105d01f..85232f862 100644 --- a/src/algorithms/derivatives/projection_derivatives.jl +++ b/src/algorithms/derivatives/projection_derivatives.jl @@ -1,38 +1,42 @@ -struct ProjectionDerivativeOperator{L,O<:Tuple,R} <: DerivativeOperator +struct ProjectionDerivativeOperator{L, O <: Tuple, R} <: DerivativeOperator leftenv::L As::O rightenv::R end -const Projection_AC_Hamiltonian{L,O,R} = ProjectionDerivativeOperator{L,Tuple{O},R} +const Projection_AC_Hamiltonian{L, O, R} = ProjectionDerivativeOperator{L, Tuple{O}, R} Projection_AC_Hamiltonian(GL, A, GR) = ProjectionDerivativeOperator(GL, (A,), GR) -const Projection_AC2_Hamiltonian{L,O₁,O₂,R} = ProjectionDerivativeOperator{L,Tuple{O₁,O₂},R} +const Projection_AC2_Hamiltonian{L, O₁, O₂, R} = ProjectionDerivativeOperator{L, Tuple{O₁, O₂}, R} Projection_AC2_Hamiltonian(GL, A1, A2, GR) = ProjectionDerivativeOperator(GL, (A1, A2), GR) # Constructors # ------------ function AC_hamiltonian(site::Int, below, operator::ProjectionOperator, above, envs) - return Projection_AC_Hamiltonian(leftenv(envs, site, below), operator.ket.AC[site], - rightenv(envs, site, below)) + return Projection_AC_Hamiltonian( + leftenv(envs, site, below), operator.ket.AC[site], rightenv(envs, site, below) + ) end function AC2_hamiltonian(site::Int, below, operator::ProjectionOperator, above, envs) - return Projection_AC2_Hamiltonian(leftenv(envs, site, below), operator.ket.AC[site], - operator.ket.AR[site + 1], - rightenv(envs, site + 1, below)) + return Projection_AC2_Hamiltonian( + leftenv(envs, site, below), operator.ket.AC[site], + operator.ket.AR[site + 1], rightenv(envs, site + 1, below) + ) end # Actions # ------- function (h::Projection_AC_Hamiltonian)(x::MPSTensor) @plansor v[-1; -2 -3 -4] := h.leftenv[4; -1 -2 5] * h.As[1][5 2; 1] * - h.rightenv[1; -3 -4 3] * conj(x[4 2; 3]) + h.rightenv[1; -3 -4 3] * conj(x[4 2; 3]) @plansor y[-1 -2; -3] := conj(v[1; 2 5 6]) * h.leftenv[-1; 1 2 4] * h.As[1][4 -2; 3] * - h.rightenv[3; 5 6 -3] + h.rightenv[3; 5 6 -3] + return y end function (h::Projection_AC2_Hamiltonian)(x::MPOTensor) @plansor v[-1; -2 -3 -4] := h.leftenv[6; -1 -2 7] * h.As[1][7 4; 5] * h.As[2][5 2; 1] * - h.rightenv[1; -3 -4 3] * conj(x[6 4; 3 2]) + h.rightenv[1; -3 -4 3] * conj(x[6 4; 3 2]) @plansor y[-1 -2; -3 -4] := conj(v[2; 3 5 6]) * h.leftenv[-1; 2 3 4] * - h.As[1][4 -2; 7] * h.As[2][7 -4; 1] * h.rightenv[1; 5 6 -3] + h.As[1][4 -2; 7] * h.As[2][7 -4; 1] * h.rightenv[1; 5 6 -3] + return y end diff --git a/src/algorithms/excitation/chepigaansatz.jl b/src/algorithms/excitation/chepigaansatz.jl index ea908de19..63f5efb6d 100644 --- a/src/algorithms/excitation/chepigaansatz.jl +++ b/src/algorithms/excitation/chepigaansatz.jl @@ -20,21 +20,23 @@ keyword arguments to [`Arnoldi`][@extref KrylovKit.Arnoldi]. - [Chepiga et al. Phys. Rev. B 96 (2017)](@cite chepiga2017) """ -struct ChepigaAnsatz{A<:KrylovAlgorithm} <: Algorithm +struct ChepigaAnsatz{A <: KrylovAlgorithm} <: Algorithm "algorithm used for the eigenvalue solvers" alg::A end function ChepigaAnsatz(; kwargs...) if isempty(kwargs) - alg = Arnoldi(; krylovdim=30, tol=1e-10, eager=true) + alg = Arnoldi(; krylovdim = 30, tol = 1.0e-10, eager = true) else alg = Arnoldi(; kwargs...) end return ChepigaAnsatz(alg) end -function excitations(H, alg::ChepigaAnsatz, ψ::FiniteMPS, envs=environments(ψ, H); - sector=one(sectortype(ψ)), num::Int=1, pos::Int=length(ψ) ÷ 2) +function excitations( + H, alg::ChepigaAnsatz, ψ::FiniteMPS, envs = environments(ψ, H); + sector = one(sectortype(ψ)), num::Int = 1, pos::Int = length(ψ) ÷ 2 + ) 1 ≤ pos ≤ length(ψ) || throw(ArgumentError("invalid position $pos")) sector == one(sector) || error("not yet implemented for charged excitations") @@ -83,21 +85,23 @@ keyword arguments to `Arnoldi`. - [Chepiga et al. Phys. Rev. B 96 (2017)](@cite chepiga2017) """ -struct ChepigaAnsatz2{A<:KrylovAlgorithm} <: Algorithm +struct ChepigaAnsatz2{A <: KrylovAlgorithm} <: Algorithm alg::A trscheme::Any end -function ChepigaAnsatz2(; trscheme=notrunc(), kwargs...) +function ChepigaAnsatz2(; trscheme = notrunc(), kwargs...) if isempty(kwargs) - alg = Arnoldi(; krylovdim=30, tol=1e-10, eager=true) + alg = Arnoldi(; krylovdim = 30, tol = 1.0e-10, eager = true) else alg = Arnoldi(; kwargs...) end return ChepigaAnsatz2(alg, trscheme) end -function excitations(H, alg::ChepigaAnsatz2, ψ::FiniteMPS, envs=environments(ψ, H); - sector=one(sectortype(ψ)), num::Int=1, pos::Int=length(ψ) ÷ 2) +function excitations( + H, alg::ChepigaAnsatz2, ψ::FiniteMPS, envs = environments(ψ, H); + sector = one(sectortype(ψ)), num::Int = 1, pos::Int = length(ψ) ÷ 2 + ) 1 ≤ pos ≤ length(ψ) - 1 || throw(ArgumentError("invalid position $pos")) sector == one(sector) || error("not yet implemented for charged excitations") @@ -117,7 +121,7 @@ function excitations(H, alg::ChepigaAnsatz2, ψ::FiniteMPS, envs=environments(ψ # map back to finitemps ψs = map(AC2s) do ac ψ′ = copy(ψ) - AL, C, AR, = tsvd!(ac; trunc=alg.trscheme) + AL, C, AR, = tsvd!(ac; trunc = alg.trscheme) normalize!(C) ψ′.AC[pos] = (AL, complex(C)) ψ′.AC[pos + 1] = (complex(C), _transpose_front(AR)) diff --git a/src/algorithms/excitation/dmrgexcitation.jl b/src/algorithms/excitation/dmrgexcitation.jl index 8b92dae6b..57fdd8440 100644 --- a/src/algorithms/excitation/dmrgexcitation.jl +++ b/src/algorithms/excitation/dmrgexcitation.jl @@ -18,20 +18,24 @@ $(TYPEDFIELDS) weight::Float64 = 10.0 end -function excitations(H::FiniteMPOHamiltonian, alg::FiniteExcited, - states::Tuple{T,Vararg{T}}; - init=FiniteMPS([copy(first(states).AC[i]) - for i in 1:length(first(states))]), - num=1) where {T<:FiniteMPS} +function excitations( + H::FiniteMPOHamiltonian, alg::FiniteExcited, + states::Tuple{T, Vararg{T}}; + init = FiniteMPS( + [ copy(first(states).AC[i]) for i in 1:length(first(states)) ] + ), num = 1 + ) where {T <: FiniteMPS} num == 0 && return (scalartype(T)[], T[]) - super_op = LinearCombination(tuple(H, ProjectionOperator.(states)...), - tuple(1.0, broadcast(x -> alg.weight, states)...)) + super_op = LinearCombination( + tuple(H, ProjectionOperator.(states)...), + tuple(1.0, broadcast(x -> alg.weight, states)...) + ) envs = environments(init, super_op) ne, _ = find_groundstate(init, super_op, alg.gsalg, envs) nstates = (states..., ne) - ens, excis = excitations(H, alg, nstates; init=init, num=num - 1) + ens, excis = excitations(H, alg, nstates; init = init, num = num - 1) push!(ens, expectation_value(ne, H)) push!(excis, ne) diff --git a/src/algorithms/excitation/exci_transfer_system.jl b/src/algorithms/excitation/exci_transfer_system.jl index c489783c6..bb0b90095 100644 --- a/src/algorithms/excitation/exci_transfer_system.jl +++ b/src/algorithms/excitation/exci_transfer_system.jl @@ -1,5 +1,7 @@ -function left_excitation_transfer_system(lBs, H, exci; mom=exci.momentum, - solver=Defaults.linearsolver) +function left_excitation_transfer_system( + lBs, H, exci; mom = exci.momentum, + solver = Defaults.linearsolver + ) len = length(H) found = zero.(lBs) odim = length(lBs) @@ -13,11 +15,8 @@ function left_excitation_transfer_system(lBs, H, exci; mom=exci.momentum, T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isid(H, i) - @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * - r_RL(exci.right_gs)[2; 3] * - τ[3 4; 5 1] * - l_RL(exci.right_gs)[-1; 6] * - τ[5 6; -4 -2] + @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * r_RL(exci.right_gs)[2; 3] * + τ[3 4; 5 1] * l_RL(exci.right_gs)[-1; 6] * τ[5 6; -4 -2] end found[i] = add!(start, lBs[i]) @@ -29,21 +28,24 @@ function left_excitation_transfer_system(lBs, H, exci; mom=exci.momentum, tm = regularize(tm, l_RL(exci.right_gs), r_RL(exci.right_gs)) end else - tm = TransferMatrix(exci.right_gs.AR, getindex.(H.data, i, i), - exci.left_gs.AL) + tm = TransferMatrix( + exci.right_gs.AR, getindex.(H.data, i, i), exci.left_gs.AL + ) end - found[i], convhist = linsolve(flip(tm), found[i], found[i], solver, 1, - -cis(-mom * len)) + found[i], convhist = linsolve( + flip(tm), found[i], found[i], solver, 1, -cis(-mom * len) + ) convhist.converged == 0 && @warn "GBL$i failed to converge: normres = $(convhist.normres)" end end return found end -function left_excitation_transfer_system(GBL, H::InfiniteMPOHamiltonian, exci; - mom=exci.momentum, - solver=Defaults.linearsolver) +function left_excitation_transfer_system( + GBL, H::InfiniteMPOHamiltonian, exci; + mom = exci.momentum, solver = Defaults.linearsolver + ) len = length(H) found = zerovector(GBL) odim = length(GBL) @@ -57,11 +59,8 @@ function left_excitation_transfer_system(GBL, H::InfiniteMPOHamiltonian, exci; T = TransferMatrix(exci.right_gs.AR, H_partial, exci.left_gs.AL) start = scale!(last(found[1:i] * T), cis(-mom * len)) if exci.trivial && isidentitylevel(H, i) - @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * - r_RL(exci.right_gs)[2; 3] * - τ[3 4; 5 1] * - l_RL(exci.right_gs)[-1; 6] * - τ[5 6; -4 -2] + @plansor start[-1 -2; -3 -4] -= start[1 4; -3 2] * r_RL(exci.right_gs)[2; 3] * + τ[3 4; 5 1] * l_RL(exci.right_gs)[-1; 6] * τ[5 6; -4 -2] end found[i] = add!(start, GBL[i]) @@ -73,20 +72,23 @@ function left_excitation_transfer_system(GBL, H::InfiniteMPOHamiltonian, exci; T = regularize(T, l_RL(exci.right_gs), r_RL(exci.right_gs)) end else - T = TransferMatrix(exci.right_gs.AR, map(h -> h[i, 1, 1, i], parent(H)), - exci.left_gs.AL) + T = TransferMatrix( + exci.right_gs.AR, map(h -> h[i, 1, 1, i], parent(H)), exci.left_gs.AL + ) end - found[i], convhist = linsolve(flip(T), found[i], found[i], solver, 1, - -cis(-mom * len)) + found[i], convhist = linsolve( + flip(T), found[i], found[i], solver, 1, -cis(-mom * len) + ) convhist.converged == 0 && @warn "GBL$i failed to converge: normres = $(convhist.normres)" end end return found end -function right_excitation_transfer_system(rBs, H, exci; mom=exci.momentum, - solver=Defaults.linearsolver) +function right_excitation_transfer_system( + rBs, H, exci; mom = exci.momentum, solver = Defaults.linearsolver + ) len = length(H) found = zero.(rBs) odim = length(rBs) @@ -101,8 +103,7 @@ function right_excitation_transfer_system(rBs, H, exci; mom=exci.momentum, start = scale!(first(T * found[i:odim]), cis(mom * len)) if exci.trivial && isid(H, i) @plansor start[-1 -2; -3 -4] -= τ[6 2; 3 4] * start[3 4; -3 5] * - l_LR(exci.right_gs)[5; 2] * - r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] + l_LR(exci.right_gs)[5; 2] * r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] end found[i] = add!(start, rBs[i]) @@ -114,21 +115,25 @@ function right_excitation_transfer_system(rBs, H, exci; mom=exci.momentum, tm = regularize(tm, l_LR(exci.left_gs), r_LR(exci.right_gs)) end else - tm = TransferMatrix(exci.left_gs.AL, getindex.(H.data, i, i), - exci.right_gs.AR) + tm = TransferMatrix( + exci.left_gs.AL, getindex.(H.data, i, i), exci.right_gs.AR + ) end - found[i], convhist = linsolve(tm, found[i], found[i], solver, 1, - -cis(mom * len)) + found[i], convhist = linsolve( + tm, found[i], found[i], solver, 1, -cis(mom * len) + ) convhist.converged < 1 && @warn "GBR$i failed to converge: normres = $(convhist.normres)" end end return found end -function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; - mom=exci.momentum, - solver=Defaults.linearsolver) +function right_excitation_transfer_system( + GBR, H::InfiniteMPOHamiltonian, exci; + mom = exci.momentum, + solver = Defaults.linearsolver + ) len = length(H) found = zerovector(GBR) odim = length(GBR) @@ -143,8 +148,7 @@ function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; start = scale!(first(T * found[i:odim]), cis(mom * len)) if exci.trivial && isidentitylevel(H, i) @plansor start[-1 -2; -3 -4] -= τ[6 2; 3 4] * start[3 4; -3 5] * - l_LR(exci.right_gs)[5; 2] * - r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] + l_LR(exci.right_gs)[5; 2] * r_LR(exci.right_gs)[-1; 1] * τ[-2 -4; 1 6] end found[i] = add!(start, GBR[i]) @@ -156,12 +160,15 @@ function right_excitation_transfer_system(GBR, H::InfiniteMPOHamiltonian, exci; tm = regularize(tm, l_LR(exci.left_gs), r_LR(exci.right_gs)) end else - tm = TransferMatrix(exci.left_gs.AL, map(h -> h[i, 1, 1, i], parent(H)), - exci.right_gs.AR) + tm = TransferMatrix( + exci.left_gs.AL, map(h -> h[i, 1, 1, i], parent(H)), exci.right_gs.AR + ) end - found[i], convhist = linsolve(tm, found[i], found[i], solver, 1, - -cis(mom * len)) + found[i], convhist = linsolve( + tm, found[i], found[i], solver, 1, + -cis(mom * len) + ) convhist.converged < 1 && @warn "GBR$i failed to converge: normres = $(convhist.normres)" end diff --git a/src/algorithms/excitation/quasiparticleexcitation.jl b/src/algorithms/excitation/quasiparticleexcitation.jl index 2c0170b5f..a2fc2759c 100644 --- a/src/algorithms/excitation/quasiparticleexcitation.jl +++ b/src/algorithms/excitation/quasiparticleexcitation.jl @@ -24,7 +24,7 @@ keyword arguments to `Arnoldi`. - [Haegeman et al. Phys. Rev. Let. 111 (2013)](@cite haegeman2013) """ -struct QuasiparticleAnsatz{A,E} <: Algorithm +struct QuasiparticleAnsatz{A, E} <: Algorithm "algorithm used for the eigenvalue solvers" alg::A @@ -32,10 +32,10 @@ struct QuasiparticleAnsatz{A,E} <: Algorithm alg_environments::E end function QuasiparticleAnsatz(; - alg_environments=Defaults.alg_environments(; - dynamic_tols=false), - kwargs...) - alg = Defaults.alg_eigsolve(; dynamic_tols=false, kwargs...) + alg_environments = Defaults.alg_environments(; dynamic_tols = false), + kwargs... + ) + alg = Defaults.alg_eigsolve(; dynamic_tols = false, kwargs...) return QuasiparticleAnsatz(alg, alg_environments) end @@ -43,7 +43,7 @@ end # Infinite Excitations # ################################################################################ -function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; num::Int=1) +function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; num::Int = 1) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) H_eff = EffectiveExcitationHamiltonian(H, lenvs, renvs, E) @@ -55,14 +55,12 @@ function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renv return Es, ϕs end -function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs; - num=1, kwargs...) +function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs; num = 1, kwargs...) # Infer `renvs` in function body as it depends on `solver`. renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs, renvs; num, kwargs...) end -function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP; - num=1, kwargs...) +function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP; num = 1, kwargs...) # Infer `lenvs` in function body as it depends on `solver`. lenvs = environments(ϕ₀.left_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs; num, kwargs...) @@ -91,30 +89,37 @@ Create and optimise infinite quasiparticle states. - `sector=one(sectortype(left_ψ))`: charge of the quasiparticle state - `parallel=true`: enable multi-threading over different momenta """ -function excitations(H, alg::QuasiparticleAnsatz, momentum::Number, lmps::InfiniteMPS, - lenvs=environments(lmps, H), rmps::InfiniteMPS=lmps, - renvs=lmps === rmps ? lenvs : environments(rmps, H); - sector=one(sectortype(lmps)), kwargs...) +function excitations( + H, alg::QuasiparticleAnsatz, momentum::Number, lmps::InfiniteMPS, + lenvs = environments(lmps, H), rmps::InfiniteMPS = lmps, + renvs = lmps === rmps ? lenvs : environments(rmps, H); + sector = one(sectortype(lmps)), kwargs... + ) ϕ₀ = LeftGaugedQP(rand, lmps, rmps; sector, momentum) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) end -function excitations(H, alg::QuasiparticleAnsatz, momenta, lmps, - lenvs=environments(lmps, H), rmps=lmps, - renvs=lmps === rmps ? lenvs : environments(rmps, H); - verbosity=Defaults.verbosity, num=1, - sector=one(sectortype(lmps)), parallel=true, kwargs...) - Toutput = Core.Compiler.return_type(excitations, - Tuple{typeof(H),typeof(alg), - eltype(momenta),typeof(lmps), - typeof(lenvs), - typeof(rmps),typeof(renvs)}) +function excitations( + H, alg::QuasiparticleAnsatz, momenta, lmps, + lenvs = environments(lmps, H), rmps = lmps, + renvs = lmps === rmps ? lenvs : environments(rmps, H); + verbosity = Defaults.verbosity, num = 1, + sector = one(sectortype(lmps)), parallel = true, kwargs... + ) + Toutput = Core.Compiler.return_type( + excitations, + Tuple{ + typeof(H), typeof(alg), eltype(momenta), typeof(lmps), + typeof(lenvs), typeof(rmps), typeof(renvs), + } + ) results = similar(momenta, Toutput) scheduler = parallel ? :greedy : :serial tmap!(results, momenta; scheduler) do momentum - E, ϕ = excitations(H, alg, momentum, lmps, lenvs, rmps, renvs; num, kwargs..., - sector) - verbosity ≥ VERBOSE_CONV && - @info "Found excitations for momentum = $(momentum)" + E, ϕ = excitations( + H, alg, momentum, lmps, lenvs, rmps, renvs; num, kwargs..., + sector + ) + verbosity ≥ VERBOSE_CONV && @info "Found excitations for momentum = $(momentum)" return E, ϕ end @@ -128,9 +133,12 @@ end # Finite Excitations # ################################################################################ -function excitations(H, alg::QuasiparticleAnsatz, ϕ₀::FiniteQP, - lenvs=environments(ϕ₀.left_gs, H), - renvs=ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H); num=1) +function excitations( + H, alg::QuasiparticleAnsatz, ϕ₀::FiniteQP, + lenvs = environments(ϕ₀.left_gs, H), + renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H); + num = 1 + ) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) H_eff = EffectiveExcitationHamiltonian(H, lenvs, renvs, E) Es, ϕs, convhist = eigsolve(ϕ₀, num, :SR, alg.alg) do ϕ @@ -161,10 +169,12 @@ Create and optimise finite quasiparticle states. - `num::Int`: number of excited states to compute - `sector=one(sectortype(left_ψ))`: charge of the quasiparticle state """ -function excitations(H, alg::QuasiparticleAnsatz, lmps::FiniteMPS, - lenvs=environments(lmps, H), rmps::FiniteMPS=lmps, - renvs=lmps === rmps ? lenvs : environments(rmps, H); - sector=one(sectortype(lmps)), num=1) +function excitations( + H, alg::QuasiparticleAnsatz, lmps::FiniteMPS, + lenvs = environments(lmps, H), rmps::FiniteMPS = lmps, + renvs = lmps === rmps ? lenvs : environments(rmps, H); + sector = one(sectortype(lmps)), num = 1 + ) ϕ₀ = LeftGaugedQP(rand, lmps, rmps; sector) return excitations(H, alg, ϕ₀, lenvs, renvs; num) end @@ -173,8 +183,10 @@ end # Statmech Excitations # ################################################################################ -function excitations(H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::MultilineQP, - lenvs, renvs; num=1) +function excitations( + H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::MultilineQP, lenvs, renvs; + num = 1 + ) H_effs = map(parent(H), parent(ϕ₀), parent(lenvs), parent(renvs)) do h, ϕ, lenv, renv E₀ = effective_excitation_renormalization_energy(h, ϕ, lenv, renv) return EffectiveExcitationHamiltonian(h, lenv, renv, E₀) @@ -190,8 +202,10 @@ function excitations(H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::Multiline return Es, ϕs end -function excitations(H::InfiniteMPO, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; - num=1) +function excitations( + H::InfiniteMPO, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP, lenvs, renvs; + num = 1 + ) E = effective_excitation_renormalization_energy(H, ϕ₀, lenvs, renvs) H_eff = EffectiveExcitationHamiltonian(H_eff, lenvs, renvs, E) @@ -204,24 +218,29 @@ function excitations(H::InfiniteMPO, alg::QuasiparticleAnsatz, ϕ₀::InfiniteQP return Es, ϕs end -function excitations(H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::MultilineQP, - lenvs; kwargs...) +function excitations( + H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::MultilineQP, lenvs; + kwargs... + ) # Infer `renvs` in function body as it depends on `solver`. renvs = ϕ₀.trivial ? lenvs : environments(ϕ₀.right_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) end -function excitations(H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::MultilineQP; - num=1, kwargs...) +function excitations( + H::MultilineMPO, alg::QuasiparticleAnsatz, ϕ₀::MultilineQP; + num = 1, kwargs... + ) # Infer `lenvs` in function body as it depends on `solver`. lenvs = environments(ϕ₀.left_gs, H; kwargs...) return excitations(H, alg, ϕ₀, lenvs; num, kwargs...) end -function excitations(H::MPO, alg::QuasiparticleAnsatz, momentum::Real, - lmps::InfiniteMPS, - lenvs=environments(lmps, H), rmps::InfiniteMPS=lmps, - renvs=lmps === rmps ? lenvs : environments(rmps, H); - kwargs...) +function excitations( + H::MPO, alg::QuasiparticleAnsatz, momentum::Real, lmps::InfiniteMPS, + lenvs = environments(lmps, H), rmps::InfiniteMPS = lmps, + renvs = lmps === rmps ? lenvs : environments(rmps, H); + kwargs... + ) multiline_H = convert(MultilineMPO, H) multiline_lmps = convert(MultilineMPS, lmps) lenvs′ = Multiline([lenvs]) @@ -233,15 +252,18 @@ function excitations(H::MPO, alg::QuasiparticleAnsatz, momentum::Real, renvs′ = Multiline([renvs]) end - return excitations(multiline_H, alg, momentum, multiline_lmps, lenvs′, - multiline_rmps, renvs′; kwargs...) + return excitations( + multiline_H, alg, momentum, multiline_lmps, lenvs′, multiline_rmps, renvs′; + kwargs... + ) end -function excitations(H::MultilineMPO, alg::QuasiparticleAnsatz, momentum::Real, - lmps::MultilineMPS, - lenvs=environments(lmps, H), rmps=lmps, - renvs=lmps === rmps ? lenvs : environments(rmps, H); - sector=one(sectortype(lmps)), kwargs...) +function excitations( + H::MultilineMPO, alg::QuasiparticleAnsatz, momentum::Real, lmps::MultilineMPS, + lenvs = environments(lmps, H), rmps = lmps, + renvs = lmps === rmps ? lenvs : environments(rmps, H); + sector = one(sectortype(lmps)), kwargs... + ) ϕ₀ = LeftGaugedQP(randn, lmps, rmps; sector, momentum) return excitations(H, alg, ϕ₀, lenvs, renvs; kwargs...) end @@ -250,7 +272,7 @@ end # H_eff # ################################################################################ -struct EffectiveExcitationHamiltonian{TO,TGL,TGR,E} +struct EffectiveExcitationHamiltonian{TO, TGL, TGR, E} operator::TO lenvs::TGL renvs::TGR @@ -267,28 +289,32 @@ function (H::Multiline{<:EffectiveExcitationHamiltonian})(ϕ::MultilineQP; kwarg return Multiline(map((x, y) -> x(y; kwargs...), parent(H), parent(ϕ))) end -function effective_excitation_hamiltonian(H, ϕ, envs=environments(ϕ, H)) +function effective_excitation_hamiltonian(H, ϕ, envs = environments(ϕ, H)) E₀ = effective_excitation_renormalization_energy(H, ϕ, envs.leftenvs, envs.rightenvs) return effective_excitation_hamiltonian(H, ϕ, envs, E₀) end function effective_excitation_hamiltonian(H, ϕ, qp_envs, E) ϕ′ = similar(ϕ) - tforeach(1:length(ϕ); scheduler=Defaults.scheduler[]) do loc + tforeach(1:length(ϕ); scheduler = Defaults.scheduler[]) do loc ϕ′[loc] = _effective_excitation_local_apply(loc, ϕ, H, E[loc], qp_envs) return nothing end return ϕ′ end -function effective_excitation_hamiltonian(H::MultilineMPO, ϕ::MultilineQP, - envs=environments(ϕ, H)) - E₀ = map(effective_excitation_renormalization_energy, parent(H), parent(ϕ), - parent(envs).leftenvs, parent(envs).rightenvs) +function effective_excitation_hamiltonian( + H::MultilineMPO, ϕ::MultilineQP, envs = environments(ϕ, H) + ) + E₀ = map( + effective_excitation_renormalization_energy, parent(H), parent(ϕ), + parent(envs).leftenvs, parent(envs).rightenvs + ) return effective_excitation_hamiltonian(H, ϕ, envs, E₀) end function effective_excitation_hamiltonian(H::MultilineMPO, ϕ::MultilineQP, envs, E) - return Multiline(map(effective_excitation_hamiltonian, - parent(H), parent(ϕ), parent(envs), E)) + return Multiline( + map(effective_excitation_hamiltonian, parent(H), parent(ϕ), parent(envs), E) + ) end function _effective_excitation_local_apply(site, ϕ, H::MPOHamiltonian, E::Number, envs) @@ -300,29 +326,20 @@ function _effective_excitation_local_apply(site, ϕ, H::MPOHamiltonian, E::Numbe B′ = scale(B, -E) # B in center - @plansor B′[-1 -2; -3 -4] += GL[-1 5; 4] * - B[4 2; -3 1] * - H[site][5 -2; 2 3] * - GR[1 3; -4] + @plansor B′[-1 -2; -3 -4] += GL[-1 5; 4] * B[4 2; -3 1] * H[site][5 -2; 2 3] * GR[1 3; -4] # B to the left if site > 1 || ϕ isa InfiniteQP AR = ϕ.right_gs.AR[site] GBL = envs.leftBenvs[site] - @plansor B′[-1 -2; -3 -4] += GBL[-1 4; -3 5] * - AR[5 2; 1] * - H[site][4 -2; 2 3] * - GR[1 3; -4] + @plansor B′[-1 -2; -3 -4] += GBL[-1 4; -3 5] * AR[5 2; 1] * H[site][4 -2; 2 3] * GR[1 3; -4] end # B to the right if site < length(ϕ.left_gs) || ϕ isa InfiniteQP AL = ϕ.left_gs.AL[site] GBR = envs.rightBenvs[site] - @plansor B′[-1 -2; -3 -4] += GL[-1 2; 1] * - AL[1 3; 4] * - H[site][2 -2; 3 5] * - GBR[4 5; -3 -4] + @plansor B′[-1 -2; -3 -4] += GL[-1 2; 1] * AL[1 3; 4] * H[site][2 -2; 3 5] * GBR[4 5; -3 -4] end return B′ @@ -336,20 +353,13 @@ function _effective_excitation_local_apply(site, ϕ, H::MPO, E::Number, envs) GL = leftenv(envs.leftenvs, site, ϕ.left_gs) GR = rightenv(envs.rightenvs, site, ϕ.right_gs) - @plansor T[-1 -2; -3 -4] := GL[-1 5; 4] * - B[4 2; -3 1] * - H[site][5 -2; 2 3] * - GR[1 3; -4] + @plansor T[-1 -2; -3 -4] := GL[-1 5; 4] * B[4 2; -3 1] * H[site][5 -2; 2 3] * GR[1 3; -4] @plansor T[-1 -2; -3 -4] += envs.leftBenvs[site][-1 4; -3 5] * - right_gs.AR[site][5 2; 1] * - H[site][4 -2; 2 3] * - GR[1 3; -4] + right_gs.AR[site][5 2; 1] * H[site][4 -2; 2 3] * GR[1 3; -4] - @plansor T[-1 -2; -3 -4] += GL[-1 2; 1] * - left_gs.AL[site][1 3; 4] * - H[site][2 -2; 3 5] * - envs.rightBenvs[site][4 5; -3 -4] + @plansor T[-1 -2; -3 -4] += GL[-1 2; 1] * left_gs.AL[site][1 3; 4] * + H[site][2 -2; 3 5] * envs.rightBenvs[site][4 5; -3 -4] return scale!(T, inv(E)) end @@ -359,11 +369,13 @@ function effective_excitation_renormalization_energy(H, ϕ, lenvs, renvs) ψ_right = ϕ.right_gs E = Vector{scalartype(ϕ)}(undef, length(ϕ)) for i in eachindex(E) - E[i] = contract_mpo_expval(ψ_left.AC[i], leftenv(lenvs, i, ψ_left), - H[i], rightenv(lenvs, i, ψ_left)) + E[i] = contract_mpo_expval( + ψ_left.AC[i], leftenv(lenvs, i, ψ_left), H[i], rightenv(lenvs, i, ψ_left) + ) if !ϕ.trivial - E[i] += contract_mpo_expval(ψ_right.AC[i], leftenv(renvs, i, ψ_right), - H[i], rightenv(renvs, i, ψ_right)) + E[i] += contract_mpo_expval( + ψ_right.AC[i], leftenv(renvs, i, ψ_right), H[i], rightenv(renvs, i, ψ_right) + ) E[i] /= 2 end end diff --git a/src/algorithms/expval.jl b/src/algorithms/expval.jl index c0c830b4e..2881eeb69 100644 --- a/src/algorithms/expval.jl +++ b/src/algorithms/expval.jl @@ -39,7 +39,7 @@ function expectation_value(ψ::AbstractMPS, (inds, O)::Pair) sites, local_mpo = instantiate_operator(physicalspace(ψ), inds => O) @assert _firstspace(first(local_mpo)) == oneunit(_firstspace(first(local_mpo))) == - dual(_lastspace(last(local_mpo))) + dual(_lastspace(last(local_mpo))) for (site, o) in zip(sites, local_mpo) if o isa MPOTensor physicalspace(ψ)[site] == physicalspace(o) || @@ -52,26 +52,25 @@ function expectation_value(ψ::AbstractMPS, (inds, O)::Pair) # some special cases that avoid using transfer matrices if length(sites) == 1 AC = ψ.AC[sites[1]] - E = @plansor conj(AC[4 5; 6]) * - conj(Ut[1]) * local_mpo[1][1 5; 3 2] * Ut[2] * - AC[4 3; 6] + E = @plansor conj(AC[4 5; 6]) * conj(Ut[1]) * local_mpo[1][1 5; 3 2] * Ut[2] * + AC[4 3; 6] elseif length(sites) == 2 && (sites[1] + 1 == sites[2]) AC = ψ.AC[sites[1]] AR = ψ.AR[sites[2]] O1, O2 = local_mpo E = @plansor conj(AC[4 5; 10]) * conj(Ut[1]) * O1[1 5; 3 8] * AC[4 3; 6] * - conj(AR[10 9; 11]) * Ut[2] * O2[8 9; 7 2] * AR[6 7; 11] + conj(AR[10 9; 11]) * Ut[2] * O2[8 9; 7 2] * AR[6 7; 11] else # generic case: write as Vl * T^N * Vr # left side T = storagetype(site_type(ψ)) - @plansor Vl[-1 -2; -3] := isomorphism(T, - left_virtualspace(ψ, sites[1]), - left_virtualspace(ψ, sites[1]))[-1; -3] * - conj(Ut[-2]) + @plansor Vl[-1 -2; -3] := isomorphism( + T, left_virtualspace(ψ, sites[1]), left_virtualspace(ψ, sites[1]) + )[-1; -3] * + conj(Ut[-2]) # middle - M = foldl(zip(sites, local_mpo); init=Vl) do v, (site, o) + M = foldl(zip(sites, local_mpo); init = Vl) do v, (site, o) if o isa Number return scale!(v * TransferMatrix(ψ.AL[site], ψ.AL[site]), o) else @@ -81,7 +80,7 @@ function expectation_value(ψ::AbstractMPS, (inds, O)::Pair) # right side E = @plansor M[1 2; 3] * Ut[2] * ψ.C[sites[end]][3; 4] * - conj(ψ.C[sites[end]][1; 4]) + conj(ψ.C[sites[end]][1; 4]) end return E / norm(ψ)^2 @@ -89,22 +88,27 @@ end # MPOHamiltonian # -------------- -function contract_mpo_expval(AC::MPSTensor, GL::MPSTensor, O::MPOTensor, GR::MPSTensor, - ACbar::MPSTensor=AC) - return @plansor GL[1 2; 3] * AC[3 7; 5] * GR[5 8; 6] * - O[2 4; 7 8] * conj(ACbar[1 4; 6]) +function contract_mpo_expval( + AC::MPSTensor, GL::MPSTensor, O::MPOTensor, GR::MPSTensor, ACbar::MPSTensor = AC + ) + return @plansor GL[1 2; 3] * AC[3 7; 5] * GR[5 8; 6] * O[2 4; 7 8] * conj(ACbar[1 4; 6]) end -function expectation_value(ψ::FiniteMPS, H::FiniteMPOHamiltonian, - envs::AbstractMPSEnvironments=environments(ψ, H)) +function expectation_value( + ψ::FiniteMPS, H::FiniteMPOHamiltonian, + envs::AbstractMPSEnvironments = environments(ψ, H) + ) return dot(ψ, H, ψ, envs) / dot(ψ, ψ) end -function expectation_value(ψ::InfiniteMPS, H::InfiniteMPOHamiltonian, - envs::AbstractMPSEnvironments=environments(ψ, H)) +function expectation_value( + ψ::InfiniteMPS, H::InfiniteMPOHamiltonian, + envs::AbstractMPSEnvironments = environments(ψ, H) + ) return sum(1:length(ψ)) do i - return contract_mpo_expval(ψ.AC[i], envs.GLs[i], H[i][:, 1, 1, end], - envs.GRs[i][end]) + return contract_mpo_expval( + ψ.AC[i], envs.GLs[i], H[i][:, 1, 1, end], envs.GRs[i][end] + ) end end @@ -119,8 +123,10 @@ end function expectation_value(ψ::InfiniteMPS, mpo::InfiniteMPO, envs...) return expectation_value(convert(MultilineMPS, ψ), convert(MultilineMPO, mpo), envs...) end -function expectation_value(ψ::MultilineMPS, O::MultilineMPO{<:InfiniteMPO}, - envs::MultilineEnvironments=environments(ψ, O)) +function expectation_value( + ψ::MultilineMPS, O::MultilineMPO{<:InfiniteMPO}, + envs::MultilineEnvironments = environments(ψ, O) + ) return prod(product(1:size(ψ, 1), 1:size(ψ, 2))) do (i, j) GL = envs[i].GLs[j] GR = envs[i].GRs[j] @@ -150,15 +156,19 @@ function expectation_value(ψ, ops::LazySum, envs::MultipleEnvironments) end # for now we also have LinearCombination -function expectation_value(ψ, H::LinearCombination, envs::LazyLincoCache=environments(ψ, H)) - return sum(((c, op, env),) -> c * expectation_value(ψ, op, env), - zip(H.coeffs, H.opps, envs.envs)) +function expectation_value(ψ, H::LinearCombination, envs::LazyLincoCache = environments(ψ, H)) + return sum( + ((c, op, env),) -> c * expectation_value(ψ, op, env), + zip(H.coeffs, H.opps, envs.envs) + ) end # ProjectionOperator # ------------------ -function expectation_value(ψ::FiniteMPS, O::ProjectionOperator, - envs::FiniteEnvironments=environments(ψ, O)) +function expectation_value( + ψ::FiniteMPS, O::ProjectionOperator, + envs::FiniteEnvironments = environments(ψ, O) + ) ens = zeros(scalartype(ψ), length(ψ)) for i in 1:length(ψ) operator = AC_hamiltonian(i, ψ, O, ψ, envs) diff --git a/src/algorithms/fidelity_susceptibility.jl b/src/algorithms/fidelity_susceptibility.jl index aecd45e69..6743a5d5a 100644 --- a/src/algorithms/fidelity_susceptibility.jl +++ b/src/algorithms/fidelity_susceptibility.jl @@ -12,10 +12,11 @@ Hamiltonian ``H = H₀ + ∑ᵢ aᵢ Vᵢ``. Returns a matrix containing the overlaps of the elementary excitations on top of `state` corresponding to each of the perturbing Hamiltonians. """ -function fidelity_susceptibility(state::Union{FiniteMPS,InfiniteMPS}, H₀::T, - Vs::AbstractVector{T}, henvs=environments(state, H₀); - maxiter=Defaults.maxiter, - tol=Defaults.tol) where {T<:MPOHamiltonian} +function fidelity_susceptibility( + state::Union{FiniteMPS, InfiniteMPS}, H₀::T, + Vs::AbstractVector{T}, henvs = environments(state, H₀); + maxiter = Defaults.maxiter, tol = Defaults.tol + ) where {T <: MPOHamiltonian} tangent_vecs = map(Vs) do V venvs = environments(state, V) @@ -26,7 +27,7 @@ function fidelity_susceptibility(state::Union{FiniteMPS,InfiniteMPS}, H₀::T, @plansor Tos[i][-1 -2; -3 -4] := temp[-1 -2; -4] * help[-3] end - (vec, convhist) = linsolve(Tos, Tos, GMRES(; maxiter=maxiter, tol=tol)) do x + vec, convhist = linsolve(Tos, Tos, GMRES(; maxiter = maxiter, tol = tol)) do x return effective_excitation_hamiltonian(H₀, x, environments(x, H₀, henvs)) end convhist.converged == 0 && @warn "failed to converge: normres = $(convhist.normres)" @@ -34,7 +35,7 @@ function fidelity_susceptibility(state::Union{FiniteMPS,InfiniteMPS}, H₀::T, return vec end - map(product(tangent_vecs, tangent_vecs)) do (a, b) + return map(product(tangent_vecs, tangent_vecs)) do (a, b) return dot(a, b) end end diff --git a/src/algorithms/grassmann.jl b/src/algorithms/grassmann.jl index 9d3956ed1..7dadfcf88 100644 --- a/src/algorithms/grassmann.jl +++ b/src/algorithms/grassmann.jl @@ -12,7 +12,7 @@ module GrassmannMPS using ..MPSKit using ..MPSKit: AbstractMPSEnvironments, InfiniteEnvironments, MultilineEnvironments, - AC_hamiltonian, recalculate! + AC_hamiltonian, recalculate! using TensorKit using OhMyThreads import TensorKitManifolds.Grassmann @@ -27,7 +27,7 @@ function rmul(Δ::GrassmannTangent, C::AbstractTensorMap) end # TODO: implement VectorInterface support for TensorKitManifolds -function add!(x::GrassmannTangent, y::GrassmannTangent, α::Number=One(), β::Number=One()) +function add!(x::GrassmannTangent, y::GrassmannTangent, α::Number = One(), β::Number = One()) checkbase(x, y) VectorInterface.add!(x.Z, y.Z, α, β) Base.setfield!(x, :U, nothing) @@ -35,8 +35,9 @@ function add!(x::GrassmannTangent, y::GrassmannTangent, α::Number=One(), β::Nu Base.setfield!(x, :V, nothing) return x end -function add!(x::AbstractArray, y::AbstractArray, α::Number=One(), β::Number=One()) - return (add!.(x, y, α, β); x) +function add!(x::AbstractArray, y::AbstractArray, α::Number = One(), β::Number = One()) + add!.(x, y, α, β) + return x end function scale!(x::GrassmannTangent, α::Number) @@ -77,7 +78,7 @@ matrix. function precondition(state, g) g′ = similar(g) rtolmin = eps(real(scalartype(state)))^(3 / 4) - tforeach(eachindex(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tforeach(eachindex(state); scheduler = MPSKit.Defaults.scheduler[]) do i rtol = max(rtolmin, norm(g[i])) ρ = rho_inv_regularized(state.C[i]; rtol) g′[i] = rmul(g[i], ρ) @@ -104,7 +105,7 @@ end function retract(state::InfiniteMPS, g, α::Real) AL′ = similar(state.AL) g′ = similar(g) - tforeach(eachindex(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tforeach(eachindex(state); scheduler = MPSKit.Defaults.scheduler[]) do i AL′[i], g′[i] = Grassmann.retract(state.AL[i], g[i], α) return nothing end @@ -114,7 +115,7 @@ end function retract(state::MultilineMPS, g, α::Real) AL′ = similar(state.AL) g′ = similar(g) - tforeach(eachindex(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tforeach(eachindex(state); scheduler = MPSKit.Defaults.scheduler[]) do i AL′[i], g′[i] = Grassmann.retract(state.AL[i], g[i], α) return nothing end @@ -128,7 +129,7 @@ end In-place transport of a tangent vector `g` at a point `state`, to a new point `state′`. """ function transport!(h, state, g, α::Real, state′) - tforeach(eachindex(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tforeach(eachindex(state); scheduler = MPSKit.Defaults.scheduler[]) do i h[i] = Grassmann.transport!(h[i], state.AL[i], g[i], α, state′.AL[i]) return nothing end @@ -140,10 +141,12 @@ end Compute the cost function and the tangent vector with respect to the `AL` parameters of the state. """ -function fg(state::FiniteMPS, operator::Union{O,LazySum{O}}, - envs::AbstractMPSEnvironments=environments(state, operator)) where {O<:FiniteMPOHamiltonian} +function fg( + state::FiniteMPS, operator::Union{O, LazySum{O}}, + envs::AbstractMPSEnvironments = environments(state, operator) + ) where {O <: FiniteMPOHamiltonian} f = expectation_value(state, operator, envs) - isapprox(imag(f), 0; atol=eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" + isapprox(imag(f), 0; atol = eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" gs = map(1:length(state)) do i AC′ = AC_hamiltonian(i, state, operator, state, envs) * state.AC[i] g = Grassmann.project(AC′, state.AL[i]) @@ -151,46 +154,52 @@ function fg(state::FiniteMPS, operator::Union{O,LazySum{O}}, end return real(f), gs end -function fg(state::InfiniteMPS, operator::Union{O,LazySum{O}}, - envs::AbstractMPSEnvironments=environments(state, operator)) where {O<:InfiniteMPOHamiltonian} +function fg( + state::InfiniteMPS, operator::Union{O, LazySum{O}}, + envs::AbstractMPSEnvironments = environments(state, operator) + ) where {O <: InfiniteMPOHamiltonian} recalculate!(envs, state, operator, state) f = expectation_value(state, operator, envs) - isapprox(imag(f), 0; atol=eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" + isapprox(imag(f), 0; atol = eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" - A = Core.Compiler.return_type(Grassmann.project, Tuple{eltype(state),eltype(state)}) + A = Core.Compiler.return_type(Grassmann.project, Tuple{eltype(state), eltype(state)}) gs = Vector{A}(undef, length(state)) - tmap!(gs, 1:length(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tmap!(gs, 1:length(state); scheduler = MPSKit.Defaults.scheduler[]) do i AC′ = AC_hamiltonian(i, state, operator, state, envs) * state.AC[i] g = Grassmann.project(AC′, state.AL[i]) return rmul(g, state.C[i]') end return real(f), gs end -function fg(state::InfiniteMPS, operator::Union{O,LazySum{O}}, - envs::AbstractMPSEnvironments=environments(state, operator)) where {O<:InfiniteMPO} +function fg( + state::InfiniteMPS, operator::Union{O, LazySum{O}}, + envs::AbstractMPSEnvironments = environments(state, operator) + ) where {O <: InfiniteMPO} recalculate!(envs, state, operator, state) f = expectation_value(state, operator, envs) - isapprox(imag(f), 0; atol=eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" + isapprox(imag(f), 0; atol = eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" - A = Core.Compiler.return_type(Grassmann.project, Tuple{eltype(state),eltype(state)}) + A = Core.Compiler.return_type(Grassmann.project, Tuple{eltype(state), eltype(state)}) gs = Vector{A}(undef, length(state)) - tmap!(gs, eachindex(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tmap!(gs, eachindex(state); scheduler = MPSKit.Defaults.scheduler[]) do i AC′ = AC_hamiltonian(i, state, operator, state, envs) * state.AC[i] g = rmul!(Grassmann.project(AC′, state.AL[i]), -inv(f)) return rmul(g, state.C[i]') end return -log(real(f)), gs end -function fg(state::MultilineMPS, operator::MultilineMPO, - envs::MultilineEnvironments=environments(state, operator)) +function fg( + state::MultilineMPS, operator::MultilineMPO, + envs::MultilineEnvironments = environments(state, operator) + ) @assert length(state) == 1 "not implemented" recalculate!(envs, state, operator, state) f = expectation_value(state, operator, envs) - isapprox(imag(f), 0; atol=eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" + isapprox(imag(f), 0; atol = eps(abs(f))^(3 / 4)) || @warn "MPO might not be Hermitian: $f" - A = Core.Compiler.return_type(Grassmann.project, Tuple{eltype(state),eltype(state)}) + A = Core.Compiler.return_type(Grassmann.project, Tuple{eltype(state), eltype(state)}) gs = Matrix{A}(undef, size(state)) - tforeach(eachindex(state); scheduler=MPSKit.Defaults.scheduler[]) do i + tforeach(eachindex(state); scheduler = MPSKit.Defaults.scheduler[]) do i AC′ = AC_hamiltonian(i, state, operator, state, envs) * state.AC[i] g = rmul!(Grassmann.project(AC′, state.AL[i]), -inv(f)) gs[i] = rmul(g, state.C[i]') @@ -206,28 +215,29 @@ Compute the (regularized) inverse of the MPS fixed point `ρ = C * C'`. Here we use the Tikhonov regularization, i.e. `inv(ρ) = inv(C * C' + δ²1)`, where the regularization parameter is `δ = rtol * norm(C)`. """ -function rho_inv_regularized(C; rtol=eps(real(scalartype(C)))^(3 / 4)) +function rho_inv_regularized(C; rtol = eps(real(scalartype(C)))^(3 / 4)) U, S, _ = tsvd(C) return U * pinv_tikhonov!!(S; rtol) * U' end -function pinv_tikhonov!!(S::DiagonalTensorMap{<:Real}; rtol=zero(scalartype(S))) - δ² = (rtol * maximum(maximum ∘ last, blocks(S); init=zero(scalartype(S))))^2 +function pinv_tikhonov!!(S::DiagonalTensorMap{<:Real}; rtol = zero(scalartype(S))) + δ² = (rtol * maximum(maximum ∘ last, blocks(S); init = zero(scalartype(S))))^2 for (_, b) in blocks(S) b.diag .= inv.(b.diag .^ 2 .+ δ²) end return S end # TensorKit v0.13 still outputs AbstractTensorMap so define fallback -function pinv_tikhonov!!(S::AbstractTensorMap{<:Real}; rtol=zero(scalartype(S))) +function pinv_tikhonov!!(S::AbstractTensorMap{<:Real}; rtol = zero(scalartype(S))) δ² = (rtol * norm(S, Inf))^2 return inv(S^2 + δ² * one(S)) end # utility test function -function optimtest(ψ, O, envs=environments(ψ, O); alpha=-0.1:0.001:0.1, - retract=retract, - inner=inner) +function optimtest( + ψ, O, envs = environments(ψ, O); + alpha = -0.1:0.001:0.1, retract = retract, inner = inner + ) _fg(x) = fg(x, O, envs) return OptimKit.optimtest(_fg, ψ; alpha, retract, inner) end diff --git a/src/algorithms/groundstate/dmrg.jl b/src/algorithms/groundstate/dmrg.jl index 27164c133..abb253bfa 100644 --- a/src/algorithms/groundstate/dmrg.jl +++ b/src/algorithms/groundstate/dmrg.jl @@ -7,7 +7,7 @@ Single-site DMRG algorithm for finding the dominant eigenvector. $(TYPEDFIELDS) """ -struct DMRG{A,F} <: Algorithm +struct DMRG{A, F} <: Algorithm "tolerance for convergence criterium" tol::Float64 @@ -23,14 +23,16 @@ struct DMRG{A,F} <: Algorithm "callback function applied after each iteration, of signature `finalize(iter, ψ, H, envs) -> ψ, envs`" finalize::F end -function DMRG(; tol=Defaults.tol, maxiter=Defaults.maxiter, alg_eigsolve=(;), - verbosity=Defaults.verbosity, finalize=Defaults._finalize) +function DMRG(; + tol = Defaults.tol, maxiter = Defaults.maxiter, alg_eigsolve = (;), + verbosity = Defaults.verbosity, finalize = Defaults._finalize + ) alg_eigsolve′ = alg_eigsolve isa NamedTuple ? Defaults.alg_eigsolve(; alg_eigsolve...) : - alg_eigsolve + alg_eigsolve return DMRG(tol, maxiter, verbosity, alg_eigsolve′, finalize) end -function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG, envs=environments(ψ, H)) +function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG, envs = environments(ψ, H)) ϵs = map(pos -> calc_galerkin(pos, ψ, H, ψ, envs), 1:length(ψ)) ϵ = maximum(ϵs) log = IterLog("DMRG") @@ -49,7 +51,7 @@ function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG, envs=environment end ϵ = maximum(ϵs) - ψ, envs = alg.finalize(iter, ψ, H, envs)::Tuple{typeof(ψ),typeof(envs)} + ψ, envs = alg.finalize(iter, ψ, H, envs)::Tuple{typeof(ψ), typeof(envs)} if ϵ <= alg.tol @infov 2 logfinish!(log, iter, ϵ, expectation_value(ψ, H, envs)) @@ -74,7 +76,7 @@ Two-site DMRG algorithm for finding the dominant eigenvector. $(TYPEDFIELDS) """ -struct DMRG2{A,S,F} <: Algorithm +struct DMRG2{A, S, F} <: Algorithm "tolerance for convergence criterium" tol::Float64 @@ -97,15 +99,17 @@ struct DMRG2{A,S,F} <: Algorithm finalize::F end # TODO: find better default truncation -function DMRG2(; tol=Defaults.tol, maxiter=Defaults.maxiter, verbosity=Defaults.verbosity, - alg_eigsolve=(;), alg_svd=Defaults.alg_svd(), trscheme, - finalize=Defaults._finalize) +function DMRG2(; + tol = Defaults.tol, maxiter = Defaults.maxiter, verbosity = Defaults.verbosity, + alg_eigsolve = (;), alg_svd = Defaults.alg_svd(), trscheme, + finalize = Defaults._finalize + ) alg_eigsolve′ = alg_eigsolve isa NamedTuple ? Defaults.alg_eigsolve(; alg_eigsolve...) : - alg_eigsolve + alg_eigsolve return DMRG2(tol, maxiter, verbosity, alg_eigsolve′, alg_svd, trscheme, finalize) end -function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG2, envs=environments(ψ, H)) +function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG2, envs = environments(ψ, H)) ϵs = map(pos -> calc_galerkin(pos, ψ, H, ψ, envs), 1:length(ψ)) ϵ = maximum(ϵs) log = IterLog("DMRG2") @@ -121,10 +125,10 @@ function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG2, envs=environmen Hac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs) _, newA2center = fixedpoint(Hac2, ac2, :SR, alg_eigsolve) - al, c, ar, = tsvd!(newA2center; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(newA2center; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) v = @plansor ac2[1 2; 3 4] * conj(al[1 2; 5]) * conj(c[5; 6]) * - conj(ar[6; 3 4]) + conj(ar[6; 3 4]) ϵs[pos] = max(ϵs[pos], abs(1 - abs(v))) ψ.AC[pos] = (al, complex(c)) @@ -137,10 +141,10 @@ function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG2, envs=environmen Hac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs) _, newA2center = fixedpoint(Hac2, ac2, :SR, alg_eigsolve) - al, c, ar, = tsvd!(newA2center; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(newA2center; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) v = @plansor ac2[1 2; 3 4] * conj(al[1 2; 5]) * conj(c[5; 6]) * - conj(ar[6; 3 4]) + conj(ar[6; 3 4]) ϵs[pos] = max(ϵs[pos], abs(1 - abs(v))) ψ.AC[pos + 1] = (complex(c), _transpose_front(ar)) @@ -148,7 +152,7 @@ function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG2, envs=environmen end ϵ = maximum(ϵs) - ψ, envs = alg.finalize(iter, ψ, H, envs)::Tuple{typeof(ψ),typeof(envs)} + ψ, envs = alg.finalize(iter, ψ, H, envs)::Tuple{typeof(ψ), typeof(envs)} if ϵ <= alg.tol @infov 2 logfinish!(log, iter, ϵ, expectation_value(ψ, H, envs)) @@ -164,6 +168,6 @@ function find_groundstate!(ψ::AbstractFiniteMPS, H, alg::DMRG2, envs=environmen return ψ, envs, ϵ end -function find_groundstate(ψ, H, alg::Union{DMRG,DMRG2}, envs...; kwargs...) +function find_groundstate(ψ, H, alg::Union{DMRG, DMRG2}, envs...; kwargs...) return find_groundstate!(copy(ψ), H, alg, envs...; kwargs...) end diff --git a/src/algorithms/groundstate/find_groundstate.jl b/src/algorithms/groundstate/find_groundstate.jl index de56ddeaa..30a5abcfd 100644 --- a/src/algorithms/groundstate/find_groundstate.jl +++ b/src/algorithms/groundstate/find_groundstate.jl @@ -21,24 +21,23 @@ optimization algorithm will be attempted based on the supplied keywords. - `environments`: environments corresponding to the converged state - `ϵ::Float64`: final convergence error upon terminating the algorithm """ -function find_groundstate(ψ::AbstractMPS, H, - envs::AbstractMPSEnvironments=environments(ψ, H); - tol=Defaults.tol, maxiter=Defaults.maxiter, - verbosity=Defaults.verbosity, trscheme=nothing) +function find_groundstate( + ψ::AbstractMPS, H, envs::AbstractMPSEnvironments = environments(ψ, H); + tol = Defaults.tol, maxiter = Defaults.maxiter, + verbosity = Defaults.verbosity, trscheme = nothing + ) if isa(ψ, InfiniteMPS) - alg = VUMPS(; tol=max(1e-4, tol), verbosity, maxiter) - if tol < 1e-4 - alg = alg & - GradientGrassmann(; tol=tol, maxiter, verbosity) + alg = VUMPS(; tol = max(1.0e-4, tol), verbosity, maxiter) + if tol < 1.0e-4 + alg = alg & GradientGrassmann(; tol = tol, maxiter, verbosity) end if !isnothing(trscheme) - alg = IDMRG2(; tol=min(1e-2, 100tol), verbosity, - trscheme) & alg + alg = IDMRG2(; tol = min(1.0e-2, 100tol), verbosity, trscheme) & alg end elseif isa(ψ, AbstractFiniteMPS) alg = DMRG(; tol, maxiter, verbosity) if !isnothing(trscheme) - alg = DMRG2(; tol=min(1e-2, 100tol), verbosity, trscheme) & alg + alg = DMRG2(; tol = min(1.0e-2, 100tol), verbosity, trscheme) & alg end else throw(ArgumentError("Unknown input state type")) diff --git a/src/algorithms/groundstate/gradient_grassmann.jl b/src/algorithms/groundstate/gradient_grassmann.jl index 0e44c86a1..478fe339c 100644 --- a/src/algorithms/groundstate/gradient_grassmann.jl +++ b/src/algorithms/groundstate/gradient_grassmann.jl @@ -26,49 +26,53 @@ $(TYPEDFIELDS) - `maxiter::Int`: maximum amount of iterations - `verbosity::Int`: level of information display """ -struct GradientGrassmann{O<:OptimKit.OptimizationAlgorithm,F} <: Algorithm +struct GradientGrassmann{O <: OptimKit.OptimizationAlgorithm, F} <: Algorithm "optimization algorithm" method::O "callback function applied after each iteration, of signature `finalize!(x, f, g, numiter) -> x, f, g`" finalize!::F - function GradientGrassmann(; method=ConjugateGradient, (finalize!)=OptimKit._finalize!, - tol=Defaults.tol, maxiter=Defaults.maxiter, - verbosity=Defaults.verbosity - 1) + function GradientGrassmann(; + method = ConjugateGradient, (finalize!) = OptimKit._finalize!, + tol = Defaults.tol, maxiter = Defaults.maxiter, + verbosity = Defaults.verbosity - 1 + ) if isa(method, OptimKit.OptimizationAlgorithm) # We were given an optimisation method, just use it. m = method elseif method <: OptimKit.OptimizationAlgorithm # We were given an optimisation method type, construct an instance of it. # restrict linesearch maxiter - linesearch = OptimKit.HagerZhangLineSearch(; verbosity=verbosity - 2, - maxiter=100) - m = method(; maxiter, verbosity, gradtol=tol, linesearch) + linesearch = OptimKit.HagerZhangLineSearch(; + verbosity = verbosity - 2, maxiter = 100 + ) + m = method(; maxiter, verbosity, gradtol = tol, linesearch) else msg = "method should be either an instance or a subtype of `OptimKit.OptimizationAlgorithm`." throw(ArgumentError(msg)) end - return new{typeof(m),typeof(finalize!)}(m, finalize!) + return new{typeof(m), typeof(finalize!)}(m, finalize!) end end -function find_groundstate(ψ::S, H, alg::GradientGrassmann, - envs::P=environments(ψ, H))::Tuple{S,P,Float64} where {S,P} - !isa(ψ, FiniteMPS) || - dim(ψ.C[end]) == 1 || +function find_groundstate( + ψ::S, H, alg::GradientGrassmann, envs::P = environments(ψ, H) + )::Tuple{S, P, Float64} where {S, P} + !isa(ψ, FiniteMPS) || dim(ψ.C[end]) == 1 || @warn "This is not fully supported - split the mps up in a sum of mps's and optimize seperately" normalize!(ψ) fg(x) = GrassmannMPS.fg(x, H, envs) - x, _, _, _, normgradhistory = optimize(fg, ψ, - alg.method; - GrassmannMPS.transport!, - GrassmannMPS.retract, - GrassmannMPS.inner, - GrassmannMPS.scale!, - GrassmannMPS.add!, - GrassmannMPS.precondition, - alg.finalize!, - isometrictransport=true) + x, _, _, _, normgradhistory = optimize( + fg, ψ, alg.method; + GrassmannMPS.transport!, + GrassmannMPS.retract, + GrassmannMPS.inner, + GrassmannMPS.scale!, + GrassmannMPS.add!, + GrassmannMPS.precondition, + alg.finalize!, + isometrictransport = true + ) return x, envs, normgradhistory[end] end diff --git a/src/algorithms/groundstate/idmrg.jl b/src/algorithms/groundstate/idmrg.jl index 0d0fd1843..3fd4653fc 100644 --- a/src/algorithms/groundstate/idmrg.jl +++ b/src/algorithms/groundstate/idmrg.jl @@ -24,7 +24,7 @@ $(TYPEDFIELDS) alg_eigsolve::A = Defaults.alg_eigsolve() end -function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG, envs=environments(ost, H)) +function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG, envs = environments(ost, H)) ϵ::Float64 = calc_galerkin(ost, H, ost, envs) ψ = copy(ost) log = IterLog("IDMRG") @@ -90,7 +90,7 @@ Two-site infinite DMRG algorithm for finding the dominant eigenvector. $(TYPEDFIELDS) """ -@kwdef struct IDMRG2{A,S} <: Algorithm +@kwdef struct IDMRG2{A, S} <: Algorithm "tolerance for convergence criterium" tol::Float64 = Defaults.tol @@ -113,7 +113,7 @@ $(TYPEDFIELDS) trscheme::TruncationScheme end -function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs=environments(ost, H)) +function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs = environments(ost, H)) length(ost) < 2 && throw(ArgumentError("unit cell should be >= 2")) ϵ::Float64 = calc_galerkin(ost, H, ost, envs) @@ -129,11 +129,11 @@ function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs=environments(os # sweep from left to right for pos in 1:(length(ψ) - 1) - ac2 = AC2(ψ, pos; kind=:ACAR) + ac2 = AC2(ψ, pos; kind = :ACAR) h_ac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs) _, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve) - al, c, ar, = tsvd!(ac2′; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[pos] = al @@ -148,11 +148,11 @@ function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs=environments(os # update the edge ψ.AL[end] = ψ.AC[end] / ψ.C[end] ψ.AC[1] = _mul_tail(ψ.AL[1], ψ.C[1]) - ac2 = AC2(ψ, 0; kind=:ALAC) + ac2 = AC2(ψ, 0; kind = :ALAC) h_ac2 = AC2_hamiltonian(0, ψ, H, ψ, envs) _, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve) - al, c, ar, = tsvd!(ac2′; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[end] = al @@ -171,11 +171,11 @@ function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs=environments(os # sweep from right to left for pos in (length(ψ) - 1):-1:1 - ac2 = AC2(ψ, pos; kind=:ALAC) + ac2 = AC2(ψ, pos; kind = :ALAC) h_ac2 = AC2_hamiltonian(pos, ψ, H, ψ, envs) _, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve) - al, c, ar, = tsvd!(ac2′; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[pos] = al @@ -191,10 +191,10 @@ function find_groundstate(ost::InfiniteMPS, H, alg::IDMRG2, envs=environments(os # update the edge ψ.AC[end] = _mul_front(ψ.C[end - 1], ψ.AR[end]) ψ.AR[1] = _transpose_front(ψ.C[end] \ _transpose_tail(ψ.AC[1])) - ac2 = AC2(ψ, 0; kind=:ACAR) + ac2 = AC2(ψ, 0; kind = :ACAR) h_ac2 = AC2_hamiltonian(0, ψ, H, ψ, envs) _, ac2′ = fixedpoint(h_ac2, ac2, :SR, alg_eigsolve) - al, c, ar, = tsvd!(ac2′; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[end] = al diff --git a/src/algorithms/groundstate/vumps.jl b/src/algorithms/groundstate/vumps.jl index a79bc7849..59edde95b 100644 --- a/src/algorithms/groundstate/vumps.jl +++ b/src/algorithms/groundstate/vumps.jl @@ -36,7 +36,7 @@ $(TYPEDFIELDS) end # Internal state of the VUMPS algorithm -struct VUMPSState{S,O,E} +struct VUMPSState{S, O, E} mps::S operator::O envs::E @@ -45,13 +45,16 @@ struct VUMPSState{S,O,E} which::Symbol end -function find_groundstate(mps::InfiniteMPS, operator, alg::VUMPS, - envs=environments(mps, operator)) - return dominant_eigsolve(operator, mps, alg, envs; which=:SR) +function find_groundstate( + mps::InfiniteMPS, operator, alg::VUMPS, envs = environments(mps, operator) + ) + return dominant_eigsolve(operator, mps, alg, envs; which = :SR) end -function dominant_eigsolve(operator, mps, alg::VUMPS, envs=environments(mps, operator); - which) +function dominant_eigsolve( + operator, mps, alg::VUMPS, envs = environments(mps, operator); + which + ) log = IterLog("VUMPS") iter = 0 ϵ = calc_galerkin(mps, operator, mps, envs) @@ -81,7 +84,7 @@ function dominant_eigsolve(operator, mps, alg::VUMPS, envs=environments(mps, ope end end -function Base.iterate(it::IterativeSolver{<:VUMPS}, state=it.state) +function Base.iterate(it::IterativeSolver{<:VUMPS}, state = it.state) ACs = localupdate_step!(it, state) mps = gauge_step!(it, state, ACs) envs = envs_step!(it, state, mps) @@ -98,8 +101,9 @@ function Base.iterate(it::IterativeSolver{<:VUMPS}, state=it.state) return (mps, envs, ϵ), it.state end -function localupdate_step!(it::IterativeSolver{<:VUMPS}, state, - scheduler=Defaults.scheduler[]) +function localupdate_step!( + it::IterativeSolver{<:VUMPS}, state, scheduler = Defaults.scheduler[] + ) alg_eigsolve = updatetol(it.alg_eigsolve, state.iter, state.ϵ) alg_orth = QRpos() @@ -110,24 +114,26 @@ function localupdate_step!(it::IterativeSolver{<:VUMPS}, state, dst_ACs = mps isa Multiline ? eachcol(ACs) : ACs tforeach(eachsite(mps), src_ACs, src_Cs; scheduler) do site, AC₀, C₀ - dst_ACs[site] = _localupdate_vumps_step!(site, mps, state.operator, state.envs, - AC₀, C₀; parallel=false, alg_orth, - state.which, alg_eigsolve) + dst_ACs[site] = _localupdate_vumps_step!( + site, mps, state.operator, state.envs, AC₀, C₀; + parallel = false, alg_orth, state.which, alg_eigsolve + ) return nothing end return ACs end -function _localupdate_vumps_step!(site, mps, operator, envs, AC₀, C₀; - parallel::Bool=false, alg_orth=QRpos(), - alg_eigsolve=Defaults.eigsolver, which) +function _localupdate_vumps_step!( + site, mps, operator, envs, AC₀, C₀; + parallel::Bool = false, alg_orth = QRpos(), alg_eigsolve = Defaults.eigsolver, which + ) if !parallel Hac = AC_hamiltonian(site, mps, operator, mps, envs) _, AC = fixedpoint(Hac, AC₀, which, alg_eigsolve) Hc = C_hamiltonian(site, mps, operator, mps, envs) _, C = fixedpoint(Hc, C₀, which, alg_eigsolve) - return regauge!(AC, C; alg=alg_orth) + return regauge!(AC, C; alg = alg_orth) end local AC, C @@ -141,7 +147,7 @@ function _localupdate_vumps_step!(site, mps, operator, envs, AC₀, C₀; _, C = fixedpoint(Hc, C₀, which, alg_eigsolve) end end - return regauge!(AC, C; alg=alg_orth) + return regauge!(AC, C; alg = alg_orth) end function gauge_step!(it::IterativeSolver{<:VUMPS}, state, ACs::AbstractVector) diff --git a/src/algorithms/propagator/corvector.jl b/src/algorithms/propagator/corvector.jl index e4b1f20b9..f4936c002 100644 --- a/src/algorithms/propagator/corvector.jl +++ b/src/algorithms/propagator/corvector.jl @@ -19,7 +19,7 @@ $(TYPEDFIELDS) * [Jeckelmann. Phys. Rev. B 66 (2002)](@cite jeckelmann2002) """ -@kwdef struct DynamicalDMRG{F<:DDMRG_Flavour,S} <: Algorithm +@kwdef struct DynamicalDMRG{F <: DDMRG_Flavour, S} <: Algorithm "flavour of the algorithm to use, either of type [`NaiveInvert`](@ref) or [`Jeckelmann`](@ref)" flavour::F = NaiveInvert() "algorithm used for the linear solvers" @@ -58,8 +58,10 @@ See also [`Jeckelmann`](@ref) for the original approach. """ struct NaiveInvert <: DDMRG_Flavour end -function propagator(A::AbstractFiniteMPS, z::Number, H::FiniteMPOHamiltonian, - alg::DynamicalDMRG{NaiveInvert}; init=copy(A)) +function propagator( + A::AbstractFiniteMPS, z::Number, H::FiniteMPOHamiltonian, + alg::DynamicalDMRG{NaiveInvert}; init = copy(A) + ) h_envs = environments(init, H) # environments for h mixedenvs = environments(init, A) # environments for @@ -116,8 +118,10 @@ See also [`NaiveInvert`](@ref) for a less costly but less accurate alternative. """ struct Jeckelmann <: DDMRG_Flavour end -function propagator(A::AbstractFiniteMPS, z, H::FiniteMPOHamiltonian, - alg::DynamicalDMRG{Jeckelmann}; init=copy(A)) +function propagator( + A::AbstractFiniteMPS, z, H::FiniteMPOHamiltonian, + alg::DynamicalDMRG{Jeckelmann}; init = copy(A) + ) ω = real(z) η = imag(z) @@ -164,15 +168,16 @@ function propagator(A::AbstractFiniteMPS, z, H::FiniteMPOHamiltonian, b = zero(a) for i in 1:length(cb) b += @plansor cb[i][1 2; 3] * init.C[end][3; 4] * - rightenv(envs1, length(A), A)[i][4 2; 5] * conj(A.C[end][1; 5]) + rightenv(envs1, length(A), A)[i][4 2; 5] * conj(A.C[end][1; 5]) end v = b / η - ω / η * a + 1im * a return v, init end -function squaredenvs(state::AbstractFiniteMPS, H::FiniteMPOHamiltonian, - envs=environments(state, H)) +function squaredenvs( + state::AbstractFiniteMPS, H::FiniteMPOHamiltonian, envs = environments(state, H) + ) H² = conj(H) * H L = length(state) diff --git a/src/algorithms/statmech/gradient_grassmann.jl b/src/algorithms/statmech/gradient_grassmann.jl index 0540f0b72..c69b2bb6b 100644 --- a/src/algorithms/statmech/gradient_grassmann.jl +++ b/src/algorithms/statmech/gradient_grassmann.jl @@ -1,17 +1,21 @@ -function leading_boundary(state::MultilineMPS, - operator::MultilineMPO, - alg::GradientGrassmann, - envs::MultilineEnvironments=environments(state, H)) +function leading_boundary( + state::MultilineMPS, + operator::MultilineMPO, + alg::GradientGrassmann, + envs::MultilineEnvironments = environments(state, H) + ) fg(x) = GrassmannMPS.fg(x, operator, envs) - x, _, _, _, normgradhistory = optimize(fg, state, - alg.method; - GrassmannMPS.transport!, - GrassmannMPS.retract, - GrassmannMPS.inner, - GrassmannMPS.scale!, - GrassmannMPS.add!, - GrassmannMPS.precondition, - alg.finalize!, - isometrictransport=true) + x, _, _, _, normgradhistory = optimize( + fg, state, + alg.method; + GrassmannMPS.transport!, + GrassmannMPS.retract, + GrassmannMPS.inner, + GrassmannMPS.scale!, + GrassmannMPS.add!, + GrassmannMPS.precondition, + alg.finalize!, + isometrictransport = true + ) return x, envs, normgradhistory[end] end diff --git a/src/algorithms/statmech/idmrg.jl b/src/algorithms/statmech/idmrg.jl index 515cb805d..372373a30 100644 --- a/src/algorithms/statmech/idmrg.jl +++ b/src/algorithms/statmech/idmrg.jl @@ -1,5 +1,6 @@ -function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG, - envs=environments(ψ, operator)) +function leading_boundary( + ψ::MultilineMPS, operator, alg::IDMRG, envs = environments(ψ, operator) + ) log = IterLog("IDMRG") ϵ::Float64 = 2 * alg.tol local iter @@ -60,8 +61,9 @@ function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG, return ψ, envs, ϵ end -function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG2, - envs=environments(ψ, operator)) +function leading_boundary( + ψ::MultilineMPS, operator, alg::IDMRG2, envs = environments(ψ, operator) + ) size(ψ, 2) < 2 && throw(ArgumentError("unit cell should be >= 2")) ϵ::Float64 = 2 * alg.tol log = IterLog("IDMRG2") @@ -75,12 +77,12 @@ function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG2, # sweep from left to right for site in 1:(size(ψ, 2) - 1) - ac2 = AC2(ψ, site; kind=:ACAR) + ac2 = AC2(ψ, site; kind = :ACAR) h = AC2_hamiltonian(site, ψ, operator, ψ, envs) _, ac2′ = fixedpoint(h, ac2, :LM, alg_eigsolve) for row in 1:size(ψ, 1) - al, c, ar, = tsvd!(ac2′[row]; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′[row]; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[row + 1, site] = al @@ -99,12 +101,12 @@ function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG2, site = size(ψ, 2) ψ.AL[:, end] .= ψ.AC[:, end] ./ ψ.C[:, end] ψ.AC[:, 1] .= _mul_tail.(ψ.AL[:, 1], ψ.C[:, 1]) - ac2 = AC2(ψ, site; kind=:ALAC) + ac2 = AC2(ψ, site; kind = :ALAC) h = AC2_hamiltonian(site, ψ, operator, ψ, envs) _, ac2′ = fixedpoint(h, ac2, :LM, alg_eigsolve) for row in 1:size(ψ, 1) - al, c, ar, = tsvd!(ac2′[row]; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′[row]; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[row + 1, site] = al @@ -124,12 +126,12 @@ function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG2, # sweep from right to left for site in reverse(1:(size(ψ, 2) - 1)) - ac2 = AC2(ψ, site; kind=:ALAC) + ac2 = AC2(ψ, site; kind = :ALAC) h = AC2_hamiltonian(site, ψ, operator, ψ, envs) _, ac2′ = fixedpoint(h, ac2, :LM, alg_eigsolve) for row in 1:size(ψ, 1) - al, c, ar, = tsvd!(ac2′[row]; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′[row]; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[row + 1, site] = al @@ -146,20 +148,21 @@ function leading_boundary(ψ::MultilineMPS, operator, alg::IDMRG2, # update the edge ψ.AC[:, end] .= _mul_front.(ψ.C[:, end - 1], ψ.AR[:, end]) ψ.AR[:, 1] .= _transpose_front.(ψ.C[:, end] .\ _transpose_tail.(ψ.AC[:, 1])) - ac2 = AC2(ψ, 0; kind=:ACAR) + ac2 = AC2(ψ, 0; kind = :ACAR) h = AC2_hamiltonian(0, ψ, operator, ψ, envs) _, ac2′ = fixedpoint(h, ac2, :LM, alg_eigsolve) for row in 1:size(ψ, 1) - al, c, ar, = tsvd!(ac2′[row]; trunc=alg.trscheme, alg=alg.alg_svd) + al, c, ar, = tsvd!(ac2′[row]; trunc = alg.trscheme, alg = alg.alg_svd) normalize!(c) ψ.AL[row + 1, end] = al ψ.C[row + 1, end] = complex(c) ψ.AR[row + 1, 1] = _transpose_front(ar) - ψ.AR[row + 1, end] = _transpose_front(ψ.C[row + 1, end - 1] \ - _transpose_tail(al * c)) + ψ.AR[row + 1, end] = _transpose_front( + ψ.C[row + 1, end - 1] \ _transpose_tail(al * c) + ) ψ.AC[row + 1, 1] = _transpose_front(c * ar) end diff --git a/src/algorithms/statmech/leading_boundary.jl b/src/algorithms/statmech/leading_boundary.jl index 9ca7ebd13..943e7ce03 100644 --- a/src/algorithms/statmech/leading_boundary.jl +++ b/src/algorithms/statmech/leading_boundary.jl @@ -1,4 +1,3 @@ - @doc """ leading_boundary(ψ₀, O, [environments]; kwargs...) -> (ψ, environments, ϵ) leading_boundary(ψ₀, O, algorithm, environments) -> (ψ, environments, ϵ) @@ -26,13 +25,16 @@ optimization algorithm will be attempted based on the supplied keywords. # TODO: alg selector # implementation always in terms of Multiline objects -function leading_boundary(state::InfiniteMPS, operator::InfiniteMPO, alg, - envs=environments(state, operator)) +function leading_boundary( + state::InfiniteMPS, operator::InfiniteMPO, alg, envs = environments(state, operator) + ) state_multi = convert(MultilineMPS, state) operator_multi = convert(MultilineMPO, operator) envs_multi = Multiline([envs]) - state_multi′, envs_multi′, err = leading_boundary(state_multi, operator_multi, alg, - envs_multi) + state_multi′, envs_multi′, err = leading_boundary( + state_multi, operator_multi, alg, + envs_multi + ) state′ = convert(InfiniteMPS, state_multi′) return state′, envs, err end diff --git a/src/algorithms/statmech/vomps.jl b/src/algorithms/statmech/vomps.jl index 1b582c9d4..b259fe19d 100644 --- a/src/algorithms/statmech/vomps.jl +++ b/src/algorithms/statmech/vomps.jl @@ -34,7 +34,7 @@ $(TYPEDFIELDS) end # Internal state of the VOMPS algorithm -struct VOMPSState{S,O,E} +struct VOMPSState{S, O, E} mps::S operator::O envs::E @@ -42,13 +42,16 @@ struct VOMPSState{S,O,E} ϵ::Float64 end -function leading_boundary(ψ::MultilineMPS, O::MultilineMPO, alg::VOMPS, - envs=environments(ψ, O)) - return dominant_eigsolve(O, ψ, alg, envs; which=:LM) +function leading_boundary( + ψ::MultilineMPS, O::MultilineMPO, alg::VOMPS, envs = environments(ψ, O) + ) + return dominant_eigsolve(O, ψ, alg, envs; which = :LM) end -function dominant_eigsolve(operator, mps, alg::VOMPS, envs=environments(mps, operator); - which) +function dominant_eigsolve( + operator, mps, alg::VOMPS, envs = environments(mps, operator); + which + ) @assert which === :LM "VOMPS only supports the LM eigenvalue problem" log = IterLog("VOMPS") iter = 0 @@ -96,8 +99,9 @@ function Base.iterate(it::IterativeSolver{<:VOMPS}, state) return (mps, envs, ϵ), it.state end -function localupdate_step!(::IterativeSolver{<:VOMPS}, state, - scheduler=Defaults.scheduler[]) +function localupdate_step!( + ::IterativeSolver{<:VOMPS}, state, scheduler = Defaults.scheduler[] + ) alg_orth = QRpos() mps = state.mps src_Cs = mps isa Multiline ? eachcol(mps.C) : mps.C @@ -106,20 +110,23 @@ function localupdate_step!(::IterativeSolver{<:VOMPS}, state, dst_ACs = state.mps isa Multiline ? eachcol(ACs) : ACs tforeach(eachsite(mps), src_ACs, src_Cs; scheduler) do site, AC₀, C₀ - dst_ACs[site] = _localupdate_vomps_step!(site, mps, state.operator, state.envs, - AC₀, C₀; alg_orth, parallel=false) + dst_ACs[site] = _localupdate_vomps_step!( + site, mps, state.operator, state.envs, + AC₀, C₀; alg_orth, parallel = false + ) return nothing end return ACs end -function _localupdate_vomps_step!(site, mps, operator, envs, AC₀, C₀; parallel::Bool=false, - alg_orth=QRpos()) +function _localupdate_vomps_step!( + site, mps, operator, envs, AC₀, C₀; parallel::Bool = false, alg_orth = QRpos() + ) if !parallel AC = AC_hamiltonian(site, mps, operator, mps, envs) * AC₀ C = C_hamiltonian(site, mps, operator, mps, envs) * C₀ - return regauge!(AC, C; alg=alg_orth) + return regauge!(AC, C; alg = alg_orth) end local AC, C @@ -127,7 +134,7 @@ function _localupdate_vomps_step!(site, mps, operator, envs, AC₀, C₀; parall @spawn AC = AC_hamiltonian(site, mps, operator, mps, envs) * AC₀ @spawn C = C_hamiltonian(site, mps, operator, mps, envs) * C₀ end - return regauge!(AC, C; alg=alg_orth) + return regauge!(AC, C; alg = alg_orth) end function gauge_step!(it::IterativeSolver{<:VOMPS}, state, ACs::AbstractVector) diff --git a/src/algorithms/statmech/vumps.jl b/src/algorithms/statmech/vumps.jl index b56511c21..83abac739 100644 --- a/src/algorithms/statmech/vumps.jl +++ b/src/algorithms/statmech/vumps.jl @@ -3,12 +3,13 @@ Approximate the leading eigenvector for opp. """ -function leading_boundary(ψ::InfiniteMPS, H, alg, envs=environments(ψ, H)) - (st, pr, de) = leading_boundary(convert(MultilineMPS, ψ), Multiline([H]), alg, envs) +function leading_boundary(ψ::InfiniteMPS, H, alg, envs = environments(ψ, H)) + st, pr, de = leading_boundary(convert(MultilineMPS, ψ), Multiline([H]), alg, envs) return convert(InfiniteMPS, st), pr, de end -function leading_boundary(mps::MultilineMPS, operator, alg::VUMPS, - envs=environments(mps, operator)) - return dominant_eigsolve(operator, mps, alg, envs; which=:LM) +function leading_boundary( + mps::MultilineMPS, operator, alg::VUMPS, envs = environments(mps, operator) + ) + return dominant_eigsolve(operator, mps, alg, envs; which = :LM) end diff --git a/src/algorithms/timestep/integrators.jl b/src/algorithms/timestep/integrators.jl index da8693af9..7d19591d8 100644 --- a/src/algorithms/timestep/integrators.jl +++ b/src/algorithms/timestep/integrators.jl @@ -17,7 +17,7 @@ function integrate end _eval_t(f, t::Number) = Base.Fix2(f, t) _eval_x(f, x) = Base.Fix1(f, x) -function integrate(f, y₀, t::Number, dt::Number, alg::Union{Arnoldi,Lanczos}) +function integrate(f, y₀, t::Number, dt::Number, alg::Union{Arnoldi, Lanczos}) y, convhist = exponentiate(_eval_t(f, t + dt / 2), -1im * dt, y₀, alg) convhist.converged == 0 && @warn "integrator failed to converge: normres = $(convhist.normres)" diff --git a/src/algorithms/timestep/taylorcluster.jl b/src/algorithms/timestep/taylorcluster.jl index 029eada83..cbad1ee13 100644 --- a/src/algorithms/timestep/taylorcluster.jl +++ b/src/algorithms/timestep/taylorcluster.jl @@ -25,10 +25,11 @@ end First order Taylor expansion for a time-evolution MPO. """ -const WI = TaylorCluster(; N=1, extension=false, compression=false) +const WI = TaylorCluster(; N = 1, extension = false, compression = false) -function make_time_mpo(H::MPOHamiltonian, dt::Number, alg::TaylorCluster; - tol=eps(real(scalartype(H)))) +function make_time_mpo( + H::MPOHamiltonian, dt::Number, alg::TaylorCluster; tol = eps(real(scalartype(H))) + ) N = alg.N τ = -1im * dt @@ -68,9 +69,7 @@ function make_time_mpo(H::MPOHamiltonian, dt::Number, alg::TaylorCluster; # TODO: use VectorInterface for memory efficiency slice[linds_left[a], 1, 1, linds_right[b]] += factor * - H_next[i][linds_next_left[aₑ...], - 1, 1, - linds_next_right[bₑ...]] + H_next[i][linds_next_left[aₑ...], 1, 1, linds_next_right[bₑ...]] end end end @@ -102,7 +101,7 @@ function make_time_mpo(H::MPOHamiltonian, dt::Number, alg::TaylorCluster; linds_left = linds[i] for c in CartesianIndices(linds_left) c_lin = linds_left[c] - s_c = CartesianIndex(sort(collect(c.I); by=(!=(1)))...) + s_c = CartesianIndex(sort(collect(c.I); by = (!=(1)))...) n1 = count(==(1), c.I) n3 = count(==(V_left), c.I) @@ -122,7 +121,7 @@ function make_time_mpo(H::MPOHamiltonian, dt::Number, alg::TaylorCluster; linds_right = linds[i + 1] for c in CartesianIndices(linds_right) c_lin = linds_right[c] - s_r = CartesianIndex(sort(collect(c.I); by=(!=(V_right)))...) + s_r = CartesianIndex(sort(collect(c.I); by = (!=(V_right)))...) n1 = count(==(1), c.I) n3 = count(==(V_right), c.I) @@ -179,8 +178,10 @@ function make_time_mpo(H::MPOHamiltonian, dt::Number, alg::TaylorCluster; end # Hack to treat FiniteMPOhamiltonians as Infinite -function make_time_mpo(H::FiniteMPOHamiltonian, dt::Number, alg::TaylorCluster; - tol=eps(real(scalartype(H)))) +function make_time_mpo( + H::FiniteMPOHamiltonian, dt::Number, alg::TaylorCluster; + tol = eps(real(scalartype(H))) + ) H′ = copy(parent(H)) V_left = left_virtualspace(H[1]) diff --git a/src/algorithms/timestep/tdvp.jl b/src/algorithms/timestep/tdvp.jl index 125c7a286..b711d022c 100644 --- a/src/algorithms/timestep/tdvp.jl +++ b/src/algorithms/timestep/tdvp.jl @@ -11,7 +11,7 @@ $(TYPEDFIELDS) * [Haegeman et al. Phys. Rev. Lett. 107 (2011)](@cite haegeman2011) """ -@kwdef struct TDVP{A,F} <: Algorithm +@kwdef struct TDVP{A, F} <: Algorithm "algorithm used in the exponential solvers" integrator::A = Defaults.alg_expsolve() @@ -25,9 +25,11 @@ $(TYPEDFIELDS) finalize::F = Defaults._finalize end -function timestep(ψ_::InfiniteMPS, H, t::Number, dt::Number, alg::TDVP, - envs::AbstractMPSEnvironments=environments(ψ_, H); - leftorthflag=true) +function timestep( + ψ_::InfiniteMPS, H, t::Number, dt::Number, alg::TDVP, + envs::AbstractMPSEnvironments = environments(ψ_, H); + leftorthflag = true + ) ψ = complex(ψ_) temp_ACs = similar(ψ.AC) temp_Cs = similar(ψ.C) @@ -47,8 +49,7 @@ function timestep(ψ_::InfiniteMPS, H, t::Number, dt::Number, alg::TDVP, Threads.@spawn begin temp_ACs = tmap!(temp_ACs, 1:length(ψ); scheduler) do loc Hac = AC_hamiltonian(loc, ψ, H, ψ, envs) - return integrate(Hac, ψ.AC[loc], t, dt, - alg.integrator) + return integrate(Hac, ψ.AC[loc], t, dt, alg.integrator) end end Threads.@spawn begin @@ -61,20 +62,22 @@ function timestep(ψ_::InfiniteMPS, H, t::Number, dt::Number, alg::TDVP, end if leftorthflag - regauge!.(temp_ACs, temp_Cs; alg=TensorKit.QRpos()) - ψ′ = InfiniteMPS(temp_ACs, ψ.C[end]; tol=alg.tolgauge, maxiter=alg.gaugemaxiter) + regauge!.(temp_ACs, temp_Cs; alg = TensorKit.QRpos()) + ψ′ = InfiniteMPS(temp_ACs, ψ.C[end]; tol = alg.tolgauge, maxiter = alg.gaugemaxiter) else circshift!(temp_Cs, 1) - regauge!.(temp_Cs, temp_ACs; alg=TensorKit.LQpos()) - ψ′ = InfiniteMPS(ψ.C[0], temp_ACs; tol=alg.tolgauge, maxiter=alg.gaugemaxiter) + regauge!.(temp_Cs, temp_ACs; alg = TensorKit.LQpos()) + ψ′ = InfiniteMPS(ψ.C[0], temp_ACs; tol = alg.tolgauge, maxiter = alg.gaugemaxiter) end recalculate!(envs, ψ′, H) return ψ′, envs end -function timestep!(ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP, - envs::AbstractMPSEnvironments=environments(ψ, H)) +function timestep!( + ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP, + envs::AbstractMPSEnvironments = environments(ψ, H) + ) # sweep left to right for i in 1:(length(ψ) - 1) @@ -118,7 +121,7 @@ $(TYPEDFIELDS) * [Haegeman et al. Phys. Rev. Lett. 107 (2011)](@cite haegeman2011) """ -@kwdef struct TDVP2{A,S,F} <: Algorithm +@kwdef struct TDVP2{A, S, F} <: Algorithm "algorithm used in the exponential solvers" integrator::A = Defaults.alg_expsolve() @@ -138,8 +141,10 @@ $(TYPEDFIELDS) finalize::F = Defaults._finalize end -function timestep!(ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP2, - envs::AbstractMPSEnvironments=environments(ψ, H)) +function timestep!( + ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP2, + envs::AbstractMPSEnvironments = environments(ψ, H) + ) # sweep left to right for i in 1:(length(ψ) - 1) @@ -147,7 +152,7 @@ function timestep!(ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP2, Hac2 = AC2_hamiltonian(i, ψ, H, ψ, envs) ac2′ = integrate(Hac2, ac2, t, dt / 2, alg.integrator) - nal, nc, nar = tsvd!(ac2′; trunc=alg.trscheme, alg=alg.alg_svd) + nal, nc, nar = tsvd!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd) ψ.AC[i] = (nal, complex(nc)) ψ.AC[i + 1] = (complex(nc), _transpose_front(nar)) @@ -163,7 +168,7 @@ function timestep!(ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP2, Hac2 = AC2_hamiltonian(i - 1, ψ, H, ψ, envs) ac2′ = integrate(Hac2, ac2, t + dt / 2, dt / 2, alg.integrator) - nal, nc, nar = tsvd!(ac2′; trunc=alg.trscheme, alg=alg.alg_svd) + nal, nc, nar = tsvd!(ac2′; trunc = alg.trscheme, alg = alg.alg_svd) ψ.AC[i - 1] = (nal, complex(nc)) ψ.AC[i] = (complex(nc), _transpose_front(nar)) @@ -177,9 +182,11 @@ function timestep!(ψ::AbstractFiniteMPS, H, t::Number, dt::Number, alg::TDVP2, end #copying version -function timestep(ψ::AbstractFiniteMPS, H, time::Number, timestep::Number, - alg::Union{TDVP,TDVP2}, envs::AbstractMPSEnvironments...; - kwargs...) +function timestep( + ψ::AbstractFiniteMPS, H, time::Number, timestep::Number, + alg::Union{TDVP, TDVP2}, envs::AbstractMPSEnvironments...; + kwargs... + ) isreal = scalartype(ψ) <: Real ψ′ = isreal ? complex(ψ) : copy(ψ) if length(envs) != 0 && isreal diff --git a/src/algorithms/timestep/time_evolve.jl b/src/algorithms/timestep/time_evolve.jl index a5d22dc2f..a9b06eeda 100644 --- a/src/algorithms/timestep/time_evolve.jl +++ b/src/algorithms/timestep/time_evolve.jl @@ -15,9 +15,10 @@ through each of the time points obtained by iterating t_span. function time_evolve end, function time_evolve! end for (timestep, time_evolve) in zip((:timestep, :timestep!), (:time_evolve, :time_evolve!)) - @eval function $time_evolve(ψ, H, t_span::AbstractVector{<:Number}, alg, - envs=environments(ψ, H); - verbosity::Int=0) + @eval function $time_evolve( + ψ, H, t_span::AbstractVector{<:Number}, alg, envs = environments(ψ, H); + verbosity::Int = 0 + ) log = IterLog("TDVP") LoggingExtras.withlevel(; verbosity) do @infov 2 loginit!(log, 0, t) @@ -26,7 +27,7 @@ for (timestep, time_evolve) in zip((:timestep, :timestep!), (:time_evolve, :time dt = t_span[iter + 1] - t ψ, envs = $timestep(ψ, H, t, dt, alg, envs) - ψ, envs = alg.finalize(t, ψ, H, envs)::Tuple{typeof(ψ),typeof(envs)} + ψ, envs = alg.finalize(t, ψ, H, envs)::Tuple{typeof(ψ), typeof(envs)} @infov 3 logiter!(log, iter, 0, t) end diff --git a/src/algorithms/timestep/wii.jl b/src/algorithms/timestep/wii.jl index 82aec465f..cd02352d5 100644 --- a/src/algorithms/timestep/wii.jl +++ b/src/algorithms/timestep/wii.jl @@ -30,46 +30,29 @@ function make_time_mpo(H::InfiniteMPOHamiltonian, dt::Number, alg::WII) Wnew = map(1:length(H)) do i for j in 2:(size(H[i], 1) - 1), k in 2:(size(H[i], 4) - 1) - init = (isometry(storagetype(WD[i]), codomain(WD[i]), domain(WD[i])), - zero(H[i][1, 1, 1, k]), - zero(H[i][j, 1, 1, end]), - zero(H[i][j, 1, 1, k])) + init = ( + isometry(storagetype(WD[i]), codomain(WD[i]), domain(WD[i])), + zero(H[i][1, 1, 1, k]), zero(H[i][j, 1, 1, end]), zero(H[i][j, 1, 1, k]), + ) y, convhist = exponentiate(1.0, init, exp_alg) do (x1, x2, x3, x4) @plansor y1[-1 -2; -3 -4] := δ * x1[-1 1; -3 -4] * - H[i][1, 1, 1, end][2 3; 1 4] * - τ[-2 4; 2 3] + H[i][1, 1, 1, end][2 3; 1 4] * τ[-2 4; 2 3] @plansor y2[-1 -2; -3 -4] := δ * x2[-1 1; -3 -4] * - H[i][1, 1, 1, end][2 3; 1 4] * - τ[-2 4; 2 3] + - sqrt(δ) * - x1[1 2; -3 4] * - H[i][1, 1, 1, k][-1 -2; 3 -4] * - τ[3 4; 1 2] + H[i][1, 1, 1, end][2 3; 1 4] * τ[-2 4; 2 3] + + sqrt(δ) * x1[1 2; -3 4] * H[i][1, 1, 1, k][-1 -2; 3 -4] * τ[3 4; 1 2] @plansor y3[-1 -2; -3 -4] := δ * x3[-1 1; -3 -4] * - H[i][1, 1, 1, end][2 3; 1 4] * - τ[-2 4; 2 3] + - sqrt(δ) * - x1[1 2; -3 4] * - H[i][j, 1, 1, end][-1 -2; 3 -4] * - τ[3 4; 1 2] - - @plansor y4[-1 -2; -3 -4] := (δ * x4[-1 1; -3 -4] * - H[i][1, 1, 1, end][2 3; 1 4] * - τ[-2 4; 2 3] + - x1[1 2; -3 4] * - H[i][j, 1, 1, k][-1 -2; 3 -4] * - τ[3 4; 1 2]) + - (sqrt(δ) * - x2[1 2; -3 -4] * - H[i][j, 1, 1, end][-1 -2; 3 4] * - τ[3 4; 1 2]) + - (sqrt(δ) * - x3[-1 4; -3 3] * - H[i][1, 1, 1, k][2 -2; 1 -4] * - τ[3 4; 1 2]) + H[i][1, 1, 1, end][2 3; 1 4] * τ[-2 4; 2 3] + + sqrt(δ) * x1[1 2; -3 4] * H[i][j, 1, 1, end][-1 -2; 3 -4] * τ[3 4; 1 2] + + @plansor y4[-1 -2; -3 -4] := ( + δ * x4[-1 1; -3 -4] * H[i][1, 1, 1, end][2 3; 1 4] * τ[-2 4; 2 3] + + x1[1 2; -3 4] * H[i][j, 1, 1, k][-1 -2; 3 -4] * τ[3 4; 1 2] + ) + ( + sqrt(δ) * x2[1 2; -3 -4] * H[i][j, 1, 1, end][-1 -2; 3 4] * τ[3 4; 1 2] + ) + (sqrt(δ) * x3[-1 4; -3 3] * H[i][1, 1, 1, k][2 -2; 1 -4] * τ[3 4; 1 2]) return y1, y2, y3, y4 end diff --git a/src/algorithms/toolbox.jl b/src/algorithms/toolbox.jl index 66fc44122..dff979e65 100644 --- a/src/algorithms/toolbox.jl +++ b/src/algorithms/toolbox.jl @@ -6,7 +6,7 @@ given, the entropy is across the entanglement cut to the right of site `site`. O vector of entropies is returned, one for each site. """ entropy(state::InfiniteMPS) = map(Base.Fix1(entropy, state), 1:length(state)) -function entropy(state::Union{FiniteMPS,WindowMPS,InfiniteMPS}, loc::Int) +function entropy(state::Union{FiniteMPS, WindowMPS, InfiniteMPS}, loc::Int) S = zero(real(scalartype(state))) tol = eps(typeof(S)) for (c, b) in entanglement_spectrum(state, loc) @@ -46,26 +46,34 @@ Concretely, this is the overlap of the current state with the single-site deriva \\epsilon = |VL * (VL' * \\frac{above}{\\partial AC_{pos}})| ``` """ -function calc_galerkin(pos::Int, below::Union{InfiniteMPS,FiniteMPS,WindowMPS}, operator, - above, envs) +function calc_galerkin( + pos::Int, below::Union{InfiniteMPS, FiniteMPS, WindowMPS}, operator, above, envs + ) AC´ = AC_hamiltonian(pos, below, operator, above, envs) * above.AC[pos] normalize!(AC´) out = add!(AC´, below.AL[pos] * below.AL[pos]' * AC´, -1) return norm(out) end -function calc_galerkin(pos::CartesianIndex{2}, below::MultilineMPS, operator::MultilineMPO, - above::MultilineMPS, envs::MultilineEnvironments) +function calc_galerkin( + pos::CartesianIndex{2}, below::MultilineMPS, operator::MultilineMPO, + above::MultilineMPS, envs::MultilineEnvironments + ) row, col = pos.I return calc_galerkin(col, below[row + 1], operator[row], above[row], envs[row]) end -function calc_galerkin(below::Union{InfiniteMPS,FiniteMPS,WindowMPS}, operator, - above, envs) +function calc_galerkin( + below::Union{InfiniteMPS, FiniteMPS, WindowMPS}, operator, above, envs + ) return maximum(pos -> calc_galerkin(pos, below, operator, above, envs), 1:length(above)) end -function calc_galerkin(below::MultilineMPS, operator::MultilineMPO, above::MultilineMPS, - envs::MultilineEnvironments) - return maximum(pos -> calc_galerkin(pos, below, operator, above, envs), - CartesianIndices(size(above))) +function calc_galerkin( + below::MultilineMPS, operator::MultilineMPO, above::MultilineMPS, + envs::MultilineEnvironments + ) + return maximum( + pos -> calc_galerkin(pos, below, operator, above, envs), + CartesianIndices(size(above)) + ) end """ @@ -79,15 +87,22 @@ Specifically, an auxiliary space `ℂ[typeof(sector)](sector => 1)'` will be add domain of each eigenvector. The `tol` and `num_vals` keyword arguments are passed to `KrylovKit.eigolve` """ -function transfer_spectrum(above::InfiniteMPS; below=above, tol=Defaults.tol, num_vals=20, - sector=first(sectors(oneunit(left_virtualspace(above, 1))))) - init = randomize!(similar(above.AL[1], left_virtualspace(below, 1), - ℂ[typeof(sector)](sector => 1)' * left_virtualspace(above, 1))) +function transfer_spectrum( + above::InfiniteMPS; below = above, tol = Defaults.tol, num_vals = 20, + sector = first(sectors(oneunit(left_virtualspace(above, 1)))) + ) + init = randomize!( + similar( + above.AL[1], left_virtualspace(below, 1), + ℂ[typeof(sector)](sector => 1)' * left_virtualspace(above, 1) + ) + ) transferspace = fuse(left_virtualspace(above, 1) * left_virtualspace(below, 1)') num_vals = min(dim(transferspace, sector), num_vals) # we can ask at most this many values - eigenvals, eigenvecs, convhist = eigsolve(flip(TransferMatrix(above.AL, below.AL)), - init, num_vals, :LM; tol=tol) + eigenvals, eigenvecs, convhist = eigsolve( + flip(TransferMatrix(above.AL, below.AL)), init, num_vals, :LM; tol = tol + ) convhist.converged < num_vals && @warn "correlation length failed to converge: normres = $(convhist.normres)" @@ -105,7 +120,7 @@ For `InfiniteMPS` and `WindowMPS` the default value for `site` is 0. For `FiniteMPS` no default value for `site` is given, it is up to the user to specify. """ -function entanglement_spectrum(st::Union{InfiniteMPS,WindowMPS}, site::Int=0) +function entanglement_spectrum(st::Union{InfiniteMPS, WindowMPS}, site::Int = 0) checkbounds(st, site) return LinearAlgebra.svdvals(st.C[site]) end @@ -117,9 +132,9 @@ end """ Find the closest fractions of π, differing at most ```tol_angle``` """ -function approx_angles(spectrum; tol_angle=0.1) +function approx_angles(spectrum; tol_angle = 0.1) angles = angle.(spectrum) ./ π # ∈ ]-1, 1] - angles_approx = rationalize.(angles, tol=tol_angle) # ∈ [-1, 1] + angles_approx = rationalize.(angles, tol = tol_angle) # ∈ [-1, 1] # Remove the effects of the branchcut. angles_approx[findall(angles_approx .== -1)] .= 1 # ∈ ]-1, 1] @@ -131,19 +146,19 @@ end Given an InfiniteMPS, compute the gap ```ϵ``` for the asymptotics of the transfer matrix, as well as the Marek gap ```δ``` as a scaling measure of the bond dimension. """ -function marek_gap(above::InfiniteMPS; tol_angle=0.1, kwargs...) +function marek_gap(above::InfiniteMPS; tol_angle = 0.1, kwargs...) spectrum = transfer_spectrum(above; kwargs...) return marek_gap(spectrum; tol_angle) end -function marek_gap(spectrum; tol_angle=0.1) +function marek_gap(spectrum; tol_angle = 0.1) # Remove 1s from the spectrum - inds = findall(abs.(spectrum) .< 1 - 1e-12) + inds = findall(abs.(spectrum) .< 1 - 1.0e-12) length(spectrum) - length(inds) < 2 || @warn "Non-injective mps?" spectrum = spectrum[inds] - angles = approx_angles(spectrum; tol_angle=tol_angle) + angles = approx_angles(spectrum; tol_angle = tol_angle) θ = first(angles) spectrum_at_angle = spectrum[findall(angles .== θ)] @@ -184,31 +199,31 @@ Compute the variance of the energy of the state with respect to the hamiltonian. """ function variance end -function variance(state::InfiniteMPS, H::InfiniteMPOHamiltonian, - envs=environments(state, H)) +function variance( + state::InfiniteMPS, H::InfiniteMPOHamiltonian, envs = environments(state, H) + ) e_local = map(1:length(state)) do i - return contract_mpo_expval(state.AC[i], envs.GLs[i], H[i][:, :, :, end], - envs.GRs[i][end]) + return contract_mpo_expval( + state.AC[i], envs.GLs[i], H[i][:, :, :, end], envs.GRs[i][end] + ) end lattice = physicalspace.(Ref(state), 1:length(state)) - H_renormalized = InfiniteMPOHamiltonian(lattice, - i => e * - id(storagetype(eltype(H)), lattice[i]) - for (i, e) in enumerate(e_local)) + H_renormalized = InfiniteMPOHamiltonian( + lattice, i => e * id(storagetype(eltype(H)), lattice[i]) for (i, e) in enumerate(e_local) + ) return real(expectation_value(state, (H - H_renormalized)^2)) end -function variance(state::FiniteMPS, H::FiniteMPOHamiltonian, envs=environments(state, H)) +function variance(state::FiniteMPS, H::FiniteMPOHamiltonian, envs = environments(state, H)) H2 = H * H - return real(expectation_value(state, H2) - - expectation_value(state, H, envs)^2) + return real(expectation_value(state, H2) - expectation_value(state, H, envs)^2) end function variance(state::FiniteQP, H::FiniteMPOHamiltonian, args...) return variance(convert(FiniteMPS, state), H) end -function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs=environments(state, H)) +function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs = environments(state, H)) # I remember there being an issue here @gertian? state.trivial || throw(ArgumentError("variance of domain wall excitations is not implemented")) @@ -220,10 +235,9 @@ function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs=environment return contract_mpo_expval(gs.AC[i], GL, H[i][:, :, :, end], GR[end]) end lattice = physicalspace.(Ref(gs), 1:length(state)) - H_regularized = H - InfiniteMPOHamiltonian(lattice, - i => e * - id(storagetype(eltype(H)), lattice[i]) - for (i, e) in enumerate(e_local)) + H_regularized = H - InfiniteMPOHamiltonian( + lattice, i => e * id(storagetype(eltype(H)), lattice[i]) for (i, e) in enumerate(e_local) + ) # I don't remember where the formula came from # TODO: this is probably broken @@ -236,12 +250,13 @@ function variance(state::InfiniteQP, H::InfiniteMPOHamiltonian, envs=environment H2 = H_regularized^2 - return real(dot(state, effective_excitation_hamiltonian(H2, state)) - - 2 * (E_f + E_ex) * E_ex + E_ex^2) + return real( + dot(state, effective_excitation_hamiltonian(H2, state)) - 2 * (E_f + E_ex) * E_ex + E_ex^2 + ) end -function variance(ψ, H::LazySum, envs=environments(ψ, sum(H))) - # TODO: avoid throwing error and just compute correct environments +function variance(ψ, H::LazySum, envs = environments(ψ, sum(H))) + # TODO: avoid throwing error and just compute correct environments envs isa MultipleEnvironments && throw(ArgumentError("The environment cannot be Lazy i.e. use environments of sum(H)")) return variance(ψ, sum(H), envs) @@ -252,8 +267,7 @@ end Convert an infinite MPO into a finite MPO of length `L`, by mapping periodic boundary conditions onto an open system. """ -function periodic_boundary_conditions(mpo::InfiniteMPO{O}, - L=length(mpo)) where {O} +function periodic_boundary_conditions(mpo::InfiniteMPO{O}, L = length(mpo)) where {O} mod(L, length(mpo)) == 0 || throw(ArgumentError("length $L is not a multiple of the infinite unitcell")) # allocate output @@ -268,23 +282,20 @@ function periodic_boundary_conditions(mpo::InfiniteMPO{O}, for i in 1:L V_left = i == 1 ? oneunit(V_wrap) : fuse(V_wrap ⊗ left_virtualspace(mpo, i)) V_right = i == L ? oneunit(V_wrap) : fuse(V_wrap ⊗ right_virtualspace(mpo, i)) - output[i] = similar(mpo[i], - V_left * physicalspace(mpo, i) ← - physicalspace(mpo, i) * V_right) + output[i] = similar( + mpo[i], V_left * physicalspace(mpo, i) ← physicalspace(mpo, i) * V_right + ) F_left = i == 1 ? cup : F_right - F_right = i == L ? cup : - isomorphism(ST, V_right ← V_wrap' * right_virtualspace(mpo, i)) - @plansor output[i][-1 -2; -3 -4] = F_left[-1; 1 2] * - τ[-3 1; 4 3] * - mpo[i][2 -2; 3 5] * - conj(F_right[-4; 4 5]) + F_right = i == L ? cup : isomorphism(ST, V_right ← V_wrap' * right_virtualspace(mpo, i)) + @plansor output[i][-1 -2; -3 -4] = F_left[-1; 1 2] * τ[-3 1; 4 3] * + mpo[i][2 -2; 3 5] * conj(F_right[-4; 4 5]) end return remove_orphans!(FiniteMPO(output)) end function _indmap_pbc(chi) - indmap = Dict{NTuple{3,Int},Int}() + indmap = Dict{NTuple{3, Int}, Int}() chi_ = 0 for b in reverse(2:chi), c in b:chi chi_ += 1 @@ -297,7 +308,7 @@ function _indmap_pbc(chi) return indmap, chi_ end -function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) +function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L = length(H)) mod(L, length(H)) == 0 || throw(ArgumentError("length $L is not a multiple of the infinite unitcell")) # @assert size(H[1], 1) > 2 "Not implemented" @@ -311,17 +322,15 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) # compute all fusers V_wrap = left_virtualspace(H, 1) - fusers = PeriodicVector(map(1:L) do i - V_top = left_virtualspace(H, i) - V_bot = left_virtualspace(H, i) - return map(Iterators.product(V_top.spaces, V_wrap.spaces, - V_bot.spaces)) do (v_top, - v_wrap, - v_bot) - return isomorphism(A, fuse(v_top ⊗ v_wrap' ⊗ v_bot), - v_top ⊗ v_wrap' ⊗ v_bot) - end - end) + fusers = PeriodicVector( + map(1:L) do i + V_top = left_virtualspace(H, i) + V_bot = left_virtualspace(H, i) + return map(Iterators.product(V_top.spaces, V_wrap.spaces, V_bot.spaces)) do (v_top, v_wrap, v_bot) + return isomorphism(A, fuse(v_top ⊗ v_wrap' ⊗ v_bot), v_top ⊗ v_wrap' ⊗ v_bot) + end + end + ) # allocate output output = Vector{O}(undef, L) @@ -344,9 +353,9 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) end SumSpace(vs) end - output[site] = similar(H[site], - V_left ⊗ physicalspace(H, site) ← - physicalspace(H, site) ⊗ V_right) + output[site] = similar( + H[site], V_left ⊗ physicalspace(H, site) ← physicalspace(H, site) ⊗ V_right + ) end # bulk changes @@ -363,12 +372,9 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) F_right = fusers[site + 1][k, i, l] j′ = indmap[j, i, l] k′ = indmap[k, i, l] - ((j′ == 1 && k′ == 1) || - (j′ == size(output[site], 1) && k′ == size(output[site], 4))) && continue - @plansor o[-1 -2; -3 -4] := h[1 2; -3 6] * - F_left[-1; 1 3 5] * - conj(F_right[-4; 6 7 8]) * - τ[2 3; 7 4] * τ[4 5; 8 -2] + ((j′ == 1 && k′ == 1) || (j′ == size(output[site], 1) && k′ == size(output[site], 4))) && continue + @plansor o[-1 -2; -3 -4] := h[1 2; -3 6] * F_left[-1; 1 3 5] * + conj(F_right[-4; 6 7 8]) * τ[2 3; 7 4] * τ[4 5; 8 -2] output[site][j′, 1, 1, k′] = o end @@ -379,12 +385,9 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) F_right = fusers[site + 1][i, l, k] j′ = indmap[i, l, j] k′ = indmap[i, l, k] - ((j′ == 1 && k′ == 1) || - (j′ == size(output[site], 1) && k′ == size(output[site], 4))) && continue - @plansor o[-1 -2; -3 -4] := h[1 -2; 3 6] * - F_left[-1; 4 2 1] * - conj(F_right[-4; 8 7 6]) * - τ[5 2; 7 3] * τ[-3 4; 8 5] + ((j′ == 1 && k′ == 1) || (j′ == size(output[site], 1) && k′ == size(output[site], 4))) && continue + @plansor o[-1 -2; -3 -4] := h[1 -2; 3 6] * F_left[-1; 4 2 1] * + conj(F_right[-4; 8 7 6]) * τ[5 2; 7 3] * τ[-3 4; 8 5] output[site][j′, 1, 1, k′] = o end end @@ -407,9 +410,8 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) F_right = fusers[2][1, j, k] j′ = indmap[1, j, k] j′ == 1 && continue - @plansor o[-1 -2; -3 -4] := h[4 -2; 3 1] * - conj(F_right[-4; 6 2 1]) * - τ[5 4; 2 3] * τ[-3 -1; 6 5] + @plansor o[-1 -2; -3 -4] := h[4 -2; 3 1] * conj(F_right[-4; 6 2 1]) * + τ[5 4; 2 3] * τ[-3 -1; 6 5] output[1][1, 1, 1, j′] = o end end @@ -422,7 +424,7 @@ function periodic_boundary_conditions(H::InfiniteMPOHamiltonian, L=length(H)) k′ = indmap[j, k, chi] k′ == size(output[end], 1) && continue @plansor o[-1 -2; -3 -4] := F_left[-1; 1 2 6] * h[1 3; -3 4] * τ[3 2; 4 5] * - τ[5 6; -4 -2] + τ[5 6; -4 -2] output[end][k′, 1, 1, 1] = o end end @@ -435,8 +437,7 @@ end Convert an infinite MPO into a finite MPO of length `L`, by applying open boundary conditions. """ -function open_boundary_conditions(mpo::InfiniteMPO{O}, - L=length(mpo)) where {O<:SparseBlockTensorMap} +function open_boundary_conditions(mpo::InfiniteMPO{O}, L = length(mpo)) where {O <: SparseBlockTensorMap} mod(L, length(mpo)) == 0 || throw(ArgumentError("length $L is not a multiple of the infinite unitcell")) @@ -456,8 +457,7 @@ end Convert an infinite MPO into a finite MPO of length `L`, by applying open boundary conditions. """ -function open_boundary_conditions(mpo::InfiniteMPOHamiltonian, - L=length(mpo)) +function open_boundary_conditions(mpo::InfiniteMPOHamiltonian, L = length(mpo)) mod(L, length(mpo)) == 0 || throw(ArgumentError("length $L is not a multiple of the infinite unitcell")) diff --git a/src/algorithms/unionalg.jl b/src/algorithms/unionalg.jl index 040f2b517..201f24d91 100644 --- a/src/algorithms/unionalg.jl +++ b/src/algorithms/unionalg.jl @@ -7,7 +7,7 @@ Algorithm wrapper representing the sequential application of two algorithms. $(TYPEDFIELDS) """ -struct UnionAlg{A,B} <: Algorithm +struct UnionAlg{A, B} <: Algorithm "first algorithm" alg1::A "second algorithm" @@ -22,13 +22,13 @@ function changebonds(state, alg::UnionAlg) return state end -function changebonds(state, H, alg::UnionAlg, envs=environments(state, H)) - (state, envs) = changebonds(state, H, alg.alg1, envs) - (state, envs) = changebonds(state, H, alg.alg2, envs) - return (state, envs) +function changebonds(state, H, alg::UnionAlg, envs = environments(state, H)) + state, envs = changebonds(state, H, alg.alg1, envs) + state, envs = changebonds(state, H, alg.alg2, envs) + return state, envs end -function find_groundstate(state, H, alg::UnionAlg, envs=environments(state, H)) - (state, envs) = find_groundstate(state, H, alg.alg1, envs) - return (state, envs, delta) = find_groundstate(state, H, alg.alg2, envs) +function find_groundstate(state, H, alg::UnionAlg, envs = environments(state, H)) + state, envs = find_groundstate(state, H, alg.alg1, envs) + return find_groundstate(state, H, alg.alg2, envs) end diff --git a/src/environments/abstract_envs.jl b/src/environments/abstract_envs.jl index 0efc0e1ce..cbdcafee1 100644 --- a/src/environments/abstract_envs.jl +++ b/src/environments/abstract_envs.jl @@ -68,23 +68,26 @@ end Determine an appropriate algorithm for computing the environments, based on the given `kwargs...`. """ -function environment_alg(::Union{InfiniteMPS,MultilineMPS}, - ::Union{InfiniteMPO,MultilineMPO}, - ::Union{InfiniteMPS,MultilineMPS}; - tol=Defaults.tol, maxiter=Defaults.maxiter, - krylovdim=Defaults.krylovdim, verbosity=Defaults.VERBOSE_NONE, - eager=true) +function environment_alg( + ::Union{InfiniteMPS, MultilineMPS}, ::Union{InfiniteMPO, MultilineMPO}, + ::Union{InfiniteMPS, MultilineMPS}; + tol = Defaults.tol, maxiter = Defaults.maxiter, krylovdim = Defaults.krylovdim, + verbosity = Defaults.VERBOSE_NONE, eager = true + ) return Arnoldi(; tol, maxiter, krylovdim, verbosity, eager) end -function environment_alg(below, ::InfiniteMPOHamiltonian, above; - tol=Defaults.tol, maxiter=Defaults.maxiter, - krylovdim=Defaults.krylovdim, verbosity=Defaults.VERBOSE_NONE) +function environment_alg( + below, ::InfiniteMPOHamiltonian, above; + tol = Defaults.tol, maxiter = Defaults.maxiter, krylovdim = Defaults.krylovdim, + verbosity = Defaults.VERBOSE_NONE + ) return GMRES(; tol, maxiter, krylovdim, verbosity) end -function environment_alg(::Union{InfiniteQP,MultilineQP}, - ::Union{InfiniteMPO,MultilineMPO}, - ::Union{InfiniteQP,MultilineQP}; - tol=Defaults.tol, maxiter=Defaults.maxiter, - krylovdim=Defaults.krylovdim, verbosity=Defaults.VERBOSE_NONE) +function environment_alg( + ::Union{InfiniteQP, MultilineQP}, ::Union{InfiniteMPO, MultilineMPO}, + ::Union{InfiniteQP, MultilineQP}; + tol = Defaults.tol, maxiter = Defaults.maxiter, krylovdim = Defaults.krylovdim, + verbosity = Defaults.VERBOSE_NONE + ) return GMRES(; tol, maxiter, krylovdim, verbosity) end diff --git a/src/environments/finite_envs.jl b/src/environments/finite_envs.jl index 2fd437970..7f26c8ddf 100644 --- a/src/environments/finite_envs.jl +++ b/src/environments/finite_envs.jl @@ -4,7 +4,7 @@ Environment manager for `FiniteMPS` and `WindowMPS`. This structure is responsable for automatically checking if the queried environment is still correctly cached and if not recalculates. """ -struct FiniteEnvironments{A,B,C,D} <: AbstractMPSEnvironments +struct FiniteEnvironments{A, B, C, D} <: AbstractMPSEnvironments above::A operator::B #the operator @@ -28,14 +28,17 @@ function environments(below, operator, above, leftstart, rightstart) rightenvs = [i == N ? rightstart : similar(rightstart) for i in 0:length(below)] t = similar(below.AL[1]) - return FiniteEnvironments(above, operator, fill(t, length(below)), - fill(t, length(below)), - leftenvs, - rightenvs) + return FiniteEnvironments( + above, operator, fill(t, length(below)), + fill(t, length(below)), + leftenvs, + rightenvs + ) end -function environments(below::FiniteMPS{S}, O::Union{FiniteMPO,FiniteMPOHamiltonian}, - above=nothing) where {S} +function environments( + below::FiniteMPS{S}, O::Union{FiniteMPO, FiniteMPOHamiltonian}, above = nothing + ) where {S} Vl_bot = left_virtualspace(below, 1) Vl_mid = left_virtualspace(O, 1) Vl_top = isnothing(above) ? left_virtualspace(below, 1) : left_virtualspace(above, 1) @@ -49,17 +52,18 @@ function environments(below::FiniteMPS{S}, O::Union{FiniteMPO,FiniteMPOHamiltoni return environments(below, O, above, leftstart, rightstart) end -function environments(below::WindowMPS, O::Union{InfiniteMPOHamiltonian,InfiniteMPO}, - above=nothing; - lenvs=environments(below.left_gs, O), - renvs=environments(below.right_gs, O)) +function environments( + below::WindowMPS, O::Union{InfiniteMPOHamiltonian, InfiniteMPO}, above = nothing; + lenvs = environments(below.left_gs, O), + renvs = environments(below.right_gs, O) + ) leftstart = copy(lenvs.GLs[1]) rightstart = copy(renvs.GRs[end]) return environments(below, O, above, leftstart, rightstart) end -function environments(below::S, above::S) where {S<:Union{FiniteMPS,WindowMPS}} +function environments(below::S, above::S) where {S <: Union{FiniteMPS, WindowMPS}} S isa WindowMPS && (above.left_gs == below.left_gs || throw(ArgumentError("left gs differs"))) S isa WindowMPS && @@ -69,13 +73,15 @@ function environments(below::S, above::S) where {S<:Union{FiniteMPS,WindowMPS}} return environments(below, operator, above, l_LL(above), r_RR(above)) end -function environments(state::Union{FiniteMPS,WindowMPS}, operator::ProjectionOperator) +function environments(state::Union{FiniteMPS, WindowMPS}, operator::ProjectionOperator) @plansor leftstart[-1; -2 -3 -4] := l_LL(operator.ket)[-3; -4] * - l_LL(operator.ket)[-1; -2] + l_LL(operator.ket)[-1; -2] @plansor rightstart[-1; -2 -3 -4] := r_RR(operator.ket)[-1; -2] * - r_RR(operator.ket)[-3; -4] - return environments(state, fill(nothing, length(state)), operator.ket, leftstart, - rightstart) + r_RR(operator.ket)[-3; -4] + return environments( + state, fill(nothing, length(state)), operator.ket, leftstart, + rightstart + ) end #notify the cache that we updated in-place, so it should invalidate the dependencies @@ -94,7 +100,7 @@ function rightenv(ca::FiniteEnvironments, ind, state) for j in a:-1:(ind + 1) above = isnothing(ca.above) ? state.AR[j] : ca.above.AR[j] ca.GRs[j] = TransferMatrix(above, ca.operator[j], state.AR[j]) * - ca.GRs[j + 1] + ca.GRs[j + 1] ca.rdependencies[j] = state.AR[j] end end @@ -110,7 +116,7 @@ function leftenv(ca::FiniteEnvironments, ind, state) for j in a:(ind - 1) above = isnothing(ca.above) ? state.AL[j] : ca.above.AL[j] ca.GLs[j + 1] = ca.GLs[j] * - TransferMatrix(above, ca.operator[j], state.AL[j]) + TransferMatrix(above, ca.operator[j], state.AL[j]) ca.ldependencies[j] = state.AL[j] end end diff --git a/src/environments/infinite_envs.jl b/src/environments/infinite_envs.jl index 2f301a2b1..3cd249ba1 100644 --- a/src/environments/infinite_envs.jl +++ b/src/environments/infinite_envs.jl @@ -8,7 +8,7 @@ T_RR[i] * GRs[i] = λ GRs[i - 1] ``` where `T_LL` and `T_RR` are the (regularized) transfer matrix operators on a give site for `AL-O-AL` and `AR-O-AR` respectively. """ -struct InfiniteEnvironments{V<:GenericMPSTensor} <: AbstractMPSEnvironments +struct InfiniteEnvironments{V <: GenericMPSTensor} <: AbstractMPSEnvironments GLs::PeriodicVector{V} GRs::PeriodicVector{V} end @@ -18,33 +18,41 @@ Base.length(envs::InfiniteEnvironments) = length(envs.GLs) leftenv(envs::InfiniteEnvironments, site::Int, state) = envs.GLs[site] rightenv(envs::InfiniteEnvironments, site::Int, state) = envs.GRs[site] -function environments(below::InfiniteMPS, - operator::Union{InfiniteMPO,InfiniteMPOHamiltonian}, - above::InfiniteMPS=below; - kwargs...) +function environments( + below::InfiniteMPS, operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, + above::InfiniteMPS = below; kwargs... + ) GLs, GRs = initialize_environments(below, operator, above) envs = InfiniteEnvironments(GLs, GRs) return recalculate!(envs, below, operator, above; kwargs...) end -function issamespace(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::Union{InfiniteMPO,InfiniteMPOHamiltonian}, - above::InfiniteMPS) +function issamespace( + envs::InfiniteEnvironments, below::InfiniteMPS, + operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, above::InfiniteMPS + ) L = check_length(below, operator, above) for i in 1:L space(envs.GLs[i]) == - (left_virtualspace(below, i) ⊗ left_virtualspace(operator, i)' ← - left_virtualspace(above, i)) || return false + ( + left_virtualspace(below, i) ⊗ left_virtualspace(operator, i)' ← + left_virtualspace(above, i) + ) || return false space(envs.GRs[i]) == - (right_virtualspace(above, i) ⊗ right_virtualspace(operator, i) ← - right_virtualspace(below, i)) || return false + ( + right_virtualspace(above, i) ⊗ right_virtualspace(operator, i) ← + right_virtualspace(below, i) + ) || return false end return true end -function recalculate!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::Union{InfiniteMPO,InfiniteMPOHamiltonian}, - above::InfiniteMPS=below; kwargs...) +function recalculate!( + envs::InfiniteEnvironments, below::InfiniteMPS, + operator::Union{InfiniteMPO, InfiniteMPOHamiltonian}, + above::InfiniteMPS = below; + kwargs... + ) if !issamespace(envs, below, operator, above) # TODO: in-place initialization? GLs, GRs = initialize_environments(below, operator, above) @@ -65,36 +73,43 @@ end # InfiniteMPO environments # ------------------------ -function initialize_environments(below::InfiniteMPS, operator::InfiniteMPO, - above::InfiniteMPS=below) +function initialize_environments( + below::InfiniteMPS, operator::InfiniteMPO, above::InfiniteMPS = below + ) L = check_length(below, operator, above) GLs = PeriodicVector([randomize!(allocate_GL(below, operator, above, i)) for i in 1:L]) GRs = PeriodicVector([randomize!(allocate_GR(below, operator, above, i)) for i in 1:L]) return GLs, GRs end -function compute_leftenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::InfiniteMPO, above::InfiniteMPS, alg) +function compute_leftenvs!( + envs::InfiniteEnvironments, below::InfiniteMPS, + operator::InfiniteMPO, above::InfiniteMPS, alg + ) # compute eigenvector T = TransferMatrix(above.AL, operator, below.AL) λ, envs.GLs[1] = fixedpoint(flip(T), envs.GLs[1], :LM, alg) # push through unitcell for i in 2:length(operator) envs.GLs[i] = envs.GLs[i - 1] * - TransferMatrix(above.AL[i - 1], operator[i - 1], below.AL[i - 1]) + TransferMatrix(above.AL[i - 1], operator[i - 1], below.AL[i - 1]) end return λ, envs end -function compute_rightenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::InfiniteMPO, above::InfiniteMPS, alg) +function compute_rightenvs!( + envs::InfiniteEnvironments, below::InfiniteMPS, operator::InfiniteMPO, + above::InfiniteMPS, alg + ) # compute eigenvector T = TransferMatrix(above.AR, operator, below.AR) λ, envs.GRs[end] = fixedpoint(T, envs.GRs[end], :LM, alg) # push through unitcell for i in reverse(1:(length(operator) - 1)) - envs.GRs[i] = TransferMatrix(above.AR[i + 1], operator[i + 1], - below.AR[i + 1]) * envs.GRs[i + 1] + envs.GRs[i] = TransferMatrix( + above.AR[i + 1], operator[i + 1], + below.AR[i + 1] + ) * envs.GRs[i + 1] end return λ, envs end @@ -104,8 +119,10 @@ end # - normalize the left environment to have overlap 1 # this avoids catastrophic blow-up of norms, while keeping the total normalized # and does not lead to issues for negative overlaps and real entries. -function TensorKit.normalize!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::InfiniteMPO, above::InfiniteMPS) +function TensorKit.normalize!( + envs::InfiniteEnvironments, below::InfiniteMPS, operator::InfiniteMPO, + above::InfiniteMPS + ) for i in 1:length(operator) normalize!(envs.GRs[i]) Hc = C_hamiltonian(i, below, operator, above, envs) @@ -117,8 +134,10 @@ end # InfiniteMPOHamiltonian environments # ----------------------------------- -function initialize_environments(below::InfiniteMPS, operator::InfiniteMPOHamiltonian, - above::InfiniteMPS=below) +function initialize_environments( + below::InfiniteMPS, operator::InfiniteMPOHamiltonian, + above::InfiniteMPS = below + ) L = check_length(above, operator, below) GLs = PeriodicVector([allocate_GL(below, operator, above, i) for i in 1:L]) GRs = PeriodicVector([allocate_GR(below, operator, above, i) for i in 1:L]) @@ -146,8 +165,10 @@ function initialize_environments(below::InfiniteMPS, operator::InfiniteMPOHamilt return GLs, GRs end -function compute_leftenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::InfiniteMPOHamiltonian, above::InfiniteMPS, alg) +function compute_leftenvs!( + envs::InfiniteEnvironments, below::InfiniteMPS, + operator::InfiniteMPOHamiltonian, above::InfiniteMPS, alg + ) L = check_length(below, above, operator) GLs = envs.GLs vsize = length(first(GLs)) @@ -181,8 +202,7 @@ function compute_leftenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, # go through the unitcell, again subtracting fixpoints for site in 1:L @plansor GLs[site][i][-1 -2; -3] -= GLs[site][i][1 -2; 2] * - r_LL(above, site - 1)[2; 1] * - l_LL(above, site)[-1; -3] + r_LL(above, site - 1)[2; 1] * l_LL(above, site)[-1; -3] end else @@ -200,20 +220,24 @@ function compute_leftenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, return GLs end -function left_cyclethrough!(index::Int, GL, below::InfiniteMPS, H::InfiniteMPOHamiltonian, - above::InfiniteMPS=below) +function left_cyclethrough!( + index::Int, GL, below::InfiniteMPS, H::InfiniteMPOHamiltonian, + above::InfiniteMPS = below + ) # TODO: efficient transfer matrix slicing for large unitcells leftinds = 1:index for site in eachindex(GL) - GL[site + 1][index] = GL[site][leftinds] * TransferMatrix(above.AL[site], - H[site][leftinds, 1, 1, index], - below.AL[site]) + GL[site + 1][index] = GL[site][leftinds] * TransferMatrix( + above.AL[site], H[site][leftinds, 1, 1, index], below.AL[site] + ) end return GL end -function compute_rightenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::InfiniteMPOHamiltonian, above::InfiniteMPS, alg) +function compute_rightenvs!( + envs::InfiniteEnvironments, below::InfiniteMPS, + operator::InfiniteMPOHamiltonian, above::InfiniteMPS, alg + ) L = check_length(above, operator, below) GRs = envs.GRs vsize = length(last(GRs)) @@ -248,8 +272,7 @@ function compute_rightenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, # go through the unitcell, again subtracting fixpoints for site in 1:L @plansor GRs[site][i][-1 -2; -3] -= GRs[site][i][1 -2; 2] * - l_RR(above, site + 1)[2; 1] * - r_RR(above, site)[-1; -3] + l_RR(above, site + 1)[2; 1] * r_RR(above, site)[-1; -3] end else if !isemptylevel(operator, i) @@ -267,22 +290,25 @@ function compute_rightenvs!(envs::InfiniteEnvironments, below::InfiniteMPS, return GRs end -function right_cyclethrough!(index::Int, GR, below::InfiniteMPS, - operator::InfiniteMPOHamiltonian, - above::InfiniteMPS=below) +function right_cyclethrough!( + index::Int, GR, below::InfiniteMPS, operator::InfiniteMPOHamiltonian, + above::InfiniteMPS = below + ) # TODO: efficient transfer matrix slicing for large unitcells for site in reverse(eachindex(GR)) rightinds = index:length(GR[site]) - GR[site - 1][index] = TransferMatrix(above.AR[site], - operator[site][index, 1, 1, rightinds], - below.AR[site]) * GR[site][rightinds] + GR[site - 1][index] = TransferMatrix( + above.AR[site], operator[site][index, 1, 1, rightinds], below.AR[site] + ) * GR[site][rightinds] end return GR end # no normalization necessary -- for consistant interface -function TensorKit.normalize!(envs::InfiniteEnvironments, below::InfiniteMPS, - operator::InfiniteMPOHamiltonian, above::InfiniteMPS) +function TensorKit.normalize!( + envs::InfiniteEnvironments, below::InfiniteMPS, + operator::InfiniteMPOHamiltonian, above::InfiniteMPS + ) return envs end diff --git a/src/environments/lazylincocache.jl b/src/environments/lazylincocache.jl index 890026c3d..d4fafd913 100644 --- a/src/environments/lazylincocache.jl +++ b/src/environments/lazylincocache.jl @@ -1,8 +1,8 @@ -struct LazyLincoCache{A<:LinearCombination,C<:Tuple} <: AbstractMPSEnvironments +struct LazyLincoCache{A <: LinearCombination, C <: Tuple} <: AbstractMPSEnvironments operator::A envs::C end function environments(state, H::LinearCombination) return LazyLincoCache(H, broadcast(o -> environments(state, o), H.opps)) -end; +end diff --git a/src/environments/multiline_envs.jl b/src/environments/multiline_envs.jl index 5d19666ce..128f64b55 100644 --- a/src/environments/multiline_envs.jl +++ b/src/environments/multiline_envs.jl @@ -1,7 +1,9 @@ -const MultilineEnvironments{E<:AbstractMPSEnvironments} = Multiline{E} +const MultilineEnvironments{E <: AbstractMPSEnvironments} = Multiline{E} -function environments(below::MultilineMPS, operator::MultilineMPO, - above::MultilineMPS=below; kwargs...) +function environments( + below::MultilineMPS, operator::MultilineMPO, above::MultilineMPS = below; + kwargs... + ) (rows = size(above, 1)) == size(operator, 1) == size(below, 1) || throw(ArgumentError("Incompatible sizes")) envs = map(1:rows) do row @@ -10,8 +12,11 @@ function environments(below::MultilineMPS, operator::MultilineMPO, return Multiline(PeriodicVector(envs)) end -function recalculate!(envs::MultilineEnvironments, below::MultilineMPS, - operator::MultilineMPO, above::MultilineMPS=below; kwargs...) +function recalculate!( + envs::MultilineEnvironments, below::MultilineMPS, + operator::MultilineMPO, above::MultilineMPS = below; + kwargs... + ) (rows = size(above, 1)) == size(operator, 1) == size(below, 1) || throw(ArgumentError("Incompatible sizes")) @threads for row in 1:rows @@ -19,8 +24,9 @@ function recalculate!(envs::MultilineEnvironments, below::MultilineMPS, end return envs end -function recalculate!(envs::MultilineEnvironments, below, (operator, above)::Tuple; - kwargs...) +function recalculate!( + envs::MultilineEnvironments, below, (operator, above)::Tuple; kwargs... + ) return recalculate!(envs, below, operator, above; kwargs...) end @@ -59,7 +65,8 @@ function transfer_rightenv!(envs::MultilineEnvironments, below, operator, above, end return envs end -function transfer_rightenv!(envs::MultilineEnvironments, below, (O, above)::Tuple, - site::Int) +function transfer_rightenv!( + envs::MultilineEnvironments, below, (O, above)::Tuple, site::Int + ) return transfer_rightenv!(envs, below, O, above, site) end diff --git a/src/environments/multiple_envs.jl b/src/environments/multiple_envs.jl index c1d806f5b..cc2c6460e 100644 --- a/src/environments/multiple_envs.jl +++ b/src/environments/multiple_envs.jl @@ -14,22 +14,25 @@ function environments(state, H::LazySum; kwargs...) return MultipleEnvironments(map(Base.Fix1(environments, state), H.ops)) end -function environments(st::WindowMPS, - H::LazySum; - lenvs=environments(st.left_gs, H), - renvs=environments(st.right_gs, H)) - return MultipleEnvironments(map((op, sublenv, subrenv) -> environments(st, op; - lenvs=sublenv, - renvs=subrenv), - H.ops, lenvs, renvs)) +function environments( + st::WindowMPS, H::LazySum; + lenvs = environments(st.left_gs, H), renvs = environments(st.right_gs, H) + ) + return MultipleEnvironments( + map( + (op, sublenv, subrenv) -> environments(st, op; lenvs = sublenv, renvs = subrenv), + H.ops, lenvs, renvs + ) + ) end # we need to define how to recalculate """ Recalculate in-place each sub-env in MultipleEnvironments """ -function recalculate!(envs::MultipleEnvironments, below, operator::LazySum, above=below; - kwargs...) +function recalculate!( + envs::MultipleEnvironments, below, operator::LazySum, above = below; kwargs... + ) for (subenvs, subO) in zip(envs.envs, operator) recalculate!(subenvs, below, subO, above; kwargs...) end @@ -45,16 +48,20 @@ function Base.getproperty(envs::MultipleEnvironments, prop::Symbol) end end -function transfer_rightenv!(envs::MultipleEnvironments{<:InfiniteEnvironments}, - below, operator, above, pos::Int) +function transfer_rightenv!( + envs::MultipleEnvironments{<:InfiniteEnvironments}, + below, operator, above, pos::Int + ) for (subH, subenv) in zip(operator, envs.envs) transfer_rightenv!(subenv, below, subH, above, pos) end return envs end -function transfer_leftenv!(envs::MultipleEnvironments{<:InfiniteEnvironments}, - below, operator, above, pos::Int) +function transfer_leftenv!( + envs::MultipleEnvironments{<:InfiniteEnvironments}, + below, operator, above, pos::Int + ) for (subH, subenv) in zip(operator, envs.envs) transfer_leftenv!(subenv, below, subH, above, pos) end diff --git a/src/environments/qp_envs.jl b/src/environments/qp_envs.jl index 3e06d7c67..acc9fa9f3 100644 --- a/src/environments/qp_envs.jl +++ b/src/environments/qp_envs.jl @@ -8,7 +8,7 @@ T_BR[i] * GRs[i] + T_LR[i] * GBRs[i] = GBRs[i - 1] ``` where `T_BL`, `T_BR`, `T_RL` and `T_LR` are the (regularized) transfer matrix operators on a given site for `B-O-AL`, `B-O-AR`, `AR-O-AL` and `AL-O-AR` respectively. """ -struct InfiniteQPEnvironments{A,B} <: AbstractMPSEnvironments +struct InfiniteQPEnvironments{A, B} <: AbstractMPSEnvironments leftBenvs::PeriodicVector{A} rightBenvs::PeriodicVector{A} @@ -28,12 +28,11 @@ end # Explicitly define optional arguments as these depend on kwargs, # which needs to come after these arguments. -function environments(exci::Union{InfiniteQP,MultilineQP}, H; kwargs...) +function environments(exci::Union{InfiniteQP, MultilineQP}, H; kwargs...) lenvs = environments(exci.left_gs, H; kwargs...) return environments(exci, H, lenvs; kwargs...) end -function environments(exci::Union{InfiniteQP,MultilineQP}, H, lenvs; - kwargs...) +function environments(exci::Union{InfiniteQP, MultilineQP}, H, lenvs; kwargs...) renvs = exci.trivial ? lenvs : environments(exci.right_gs, H; kwargs...) return environments(exci, H, lenvs, renvs; kwargs...) end @@ -46,9 +45,7 @@ function environments(qp::MultilineQP, operator::MultilineMPO, lenvs, renvs; kwa return Multiline(PeriodicVector(envs)) end -function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, - lenvs, renvs; - kwargs...) +function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, lenvs, renvs; kwargs...) ids = findall(Base.Fix1(isidentitylevel, H), 2:(size(H[1], 1) - 1)) solver = environment_alg(exci, H, exci; kwargs...) @@ -61,19 +58,15 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, zerovector!(lBs[1]) for pos in 1:length(exci) lBs[pos + 1] = lBs[pos] * TransferMatrix(AR[pos], H[pos], AL[pos]) / - cis(exci.momentum) + cis(exci.momentum) lBs[pos + 1] += leftenv(lenvs, pos, exci.left_gs) * - TransferMatrix(exci[pos], H[pos], AL[pos]) / - cis(exci.momentum) + TransferMatrix(exci[pos], H[pos], AL[pos]) / cis(exci.momentum) if exci.trivial # regularization of trivial excitations for i in ids @plansor lBs[pos + 1][i][-1 -2; -3 -4] -= lBs[pos + 1][i][1 4; -3 2] * - r_RL(exci.left_gs, pos)[2; 3] * - τ[3 4; 5 1] * - l_RL(exci.left_gs, pos + 1)[-1; - 6] * - τ[5 6; -4 -2] + r_RL(exci.left_gs, pos)[2; 3] * τ[3 4; 5 1] * + l_RL(exci.left_gs, pos + 1)[ -1; 6 ] * τ[5 6; -4 -2] end end end @@ -81,29 +74,27 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, zerovector!(rBs[end]) for pos in length(exci):-1:1 rBs[pos - 1] = TransferMatrix(AL[pos], H[pos], AR[pos]) * - rBs[pos] * cis(exci.momentum) + rBs[pos] * cis(exci.momentum) rBs[pos - 1] += TransferMatrix(exci[pos], H[pos], AR[pos]) * - rightenv(renvs, pos, exci.right_gs) * cis(exci.momentum) + rightenv(renvs, pos, exci.right_gs) * cis(exci.momentum) if exci.trivial for i in ids ρ_left = l_LR(exci.left_gs, pos) ρ_right = r_LR(exci.left_gs, pos - 1) @plansor rBs[pos - 1][i][-1 -2; -3 -4] -= τ[6 4; 1 3] * - rBs[pos - 1][i][1 3; -3 2] * - ρ_left[2; 4] * - ρ_right[-1; 5] * - τ[-2 -4; 5 6] + rBs[pos - 1][i][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] end end end @sync begin - Threads.@spawn $lBs[1] = left_excitation_transfer_system($lBs[1], $H, $exci; - solver=$solver) - Threads.@spawn $rBs[end] = right_excitation_transfer_system($rBs[end], $H, - $exci; - solver=$solver) + Threads.@spawn $lBs[1] = left_excitation_transfer_system( + $lBs[1], $H, $exci; solver = $solver + ) + Threads.@spawn $rBs[end] = right_excitation_transfer_system( + $rBs[end], $H, $exci; solver = $solver + ) end lB_cur = lBs[1] @@ -115,10 +106,7 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, ρ_left = l_RL(exci.left_gs, i + 1) ρ_right = r_RL(exci.left_gs, i) @plansor lB_cur[k][-1 -2; -3 -4] -= lB_cur[k][1 4; -3 2] * - ρ_right[2; 3] * - τ[3 4; 5 1] * - ρ_left[-1; 6] * - τ[5 6; -4 -2] + ρ_right[2; 3] * τ[3 4; 5 1] * ρ_left[-1; 6] * τ[5 6; -4 -2] end end @@ -134,10 +122,7 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, ρ_left = l_LR(exci.left_gs, i) ρ_right = r_LR(exci.left_gs, i - 1) @plansor rB_cur[k][-1 -2; -3 -4] -= τ[6 4; 1 3] * - rB_cur[k][1 3; -3 2] * - ρ_left[2; 4] * - ρ_right[-1; 5] * - τ[-2 -4; 5 6] + rB_cur[k][1 3; -3 2] * ρ_left[2; 4] * ρ_right[-1; 5] * τ[-2 -4; 5 6] end end @@ -147,11 +132,12 @@ function environments(exci::InfiniteQP, H::InfiniteMPOHamiltonian, return InfiniteQPEnvironments(lBs, rBs, lenvs, renvs) end -function environments(exci::FiniteQP, - H::FiniteMPOHamiltonian, - lenvs=environments(exci.left_gs, H), - renvs=exci.trivial ? lenvs : environments(exci.right_gs, H); - kwargs...) +function environments( + exci::FiniteQP, H::FiniteMPOHamiltonian, + lenvs = environments(exci.left_gs, H), + renvs = exci.trivial ? lenvs : environments(exci.right_gs, H); + kwargs... + ) AL = exci.left_gs.AL AR = exci.right_gs.AR @@ -164,14 +150,14 @@ function environments(exci::FiniteQP, for pos in 1:(length(exci) - 1) lBs[pos + 1] = lBs[pos] * TransferMatrix(AR[pos], H[pos], AL[pos]) lBs[pos + 1] += leftenv(lenvs, pos, exci.left_gs) * - TransferMatrix(exci[pos], H[pos], AL[pos]) + TransferMatrix(exci[pos], H[pos], AL[pos]) end zerovector!(rBs[end]) for pos in length(exci):-1:2 rBs[pos - 1] = TransferMatrix(AL[pos], H[pos], AR[pos]) * rBs[pos] rBs[pos - 1] += TransferMatrix(exci[pos], H[pos], AR[pos]) * - rightenv(renvs, pos, exci.right_gs) + rightenv(renvs, pos, exci.right_gs) end return InfiniteQPEnvironments(lBs, rBs, lenvs, renvs) @@ -203,7 +189,7 @@ function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) for col in 1:length(exci) gbl = gbl * TransferMatrix(right_gs.AR[col], O[col], left_gs.AL[col]) gbl += leftenv(lenvs, col, left_gs) * - TransferMatrix(exci[col], O[col], left_gs.AL[col]) + TransferMatrix(exci[col], O[col], left_gs.AL[col]) gbl *= left_regularization[col] * cis(-exci.momentum) GBL[col] = gbl end @@ -212,7 +198,7 @@ function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) for col in reverse(1:length(exci)) gbr = TransferMatrix(left_gs.AL[col], O[col], right_gs.AR[col]) * gbr gbr += TransferMatrix(exci[col], O[col], right_gs.AR[col]) * - rightenv(renvs, col, right_gs) + rightenv(renvs, col, right_gs) gbr *= right_regularization[col] * cis(exci.momentum) GBR[col] = gbr end @@ -222,30 +208,32 @@ function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) if exci.trivial @plansor rvec[-1 -2; -3] := rightenv(lenvs, 0, left_gs)[-1 -2; 1] * - conj(left_gs.C[0][-3; 1]) + conj(left_gs.C[0][-3; 1]) @plansor lvec[-1 -2; -3] := leftenv(lenvs, 1, left_gs)[-1 -2; 1] * - left_gs.C[0][1; -3] + left_gs.C[0][1; -3] T_RL = regularize(T_RL, lvec, rvec) @plansor rvec[-1 -2; -3] := rightenv(renvs, 0, right_gs)[1 -2; -3] * - right_gs.C[0][-1; 1] + right_gs.C[0][-1; 1] @plansor lvec[-1 -2; -3] := conj(right_gs.C[0][-3; 1]) * - leftenv(renvs, 1, right_gs)[-1 -2; 1] + leftenv(renvs, 1, right_gs)[-1 -2; 1] T_LR = regularize(T_LR, lvec, rvec) end - GBL[end], convhist = linsolve(flip(T_RL), gbl, gbl, solver, 1, - -cis(-length(exci) * exci.momentum) * - prod(left_regularization)) + GBL[end], convhist = linsolve( + flip(T_RL), gbl, gbl, solver, 1, + -cis(-length(exci) * exci.momentum) * prod(left_regularization) + ) convhist.converged == 0 && @warn "GBL failed to converge: normres = $(convhist.normres)" - GBR[1], convhist = linsolve(T_LR, gbr, gbr, GMRES(), 1, - -cis(length(exci) * exci.momentum) * - prod(right_regularization)) + GBR[1], convhist = linsolve( + T_LR, gbr, gbr, GMRES(), 1, + -cis(length(exci) * exci.momentum) * prod(right_regularization) + ) convhist.converged == 0 && @warn "GBR failed to converge: normres = $(convhist.normres)" @@ -253,14 +241,13 @@ function environments(exci::InfiniteQP, O::InfiniteMPO, lenvs, renvs; kwargs...) right_cur = GBR[1] for col in 1:(length(exci) - 1) left_cur = left_regularization[col] * left_cur * - TransferMatrix(right_gs.AR[col], O[col], - left_gs.AL[col]) * cis(-exci.momentum) + TransferMatrix(right_gs.AR[col], O[col], left_gs.AL[col]) * + cis(-exci.momentum) GBL[col] += left_cur col = length(exci) - col + 1 - right_cur = TransferMatrix(left_gs.AL[col], O[col], - right_gs.AR[col]) * right_cur * - cis(exci.momentum) * right_regularization[col] + right_cur = TransferMatrix(left_gs.AL[col], O[col], right_gs.AR[col]) * right_cur * + cis(exci.momentum) * right_regularization[col] GBR[col] += right_cur end diff --git a/src/operators/abstractmpo.jl b/src/operators/abstractmpo.jl index 5ea9ca739..4cbaa4ec9 100644 --- a/src/operators/abstractmpo.jl +++ b/src/operators/abstractmpo.jl @@ -8,7 +8,7 @@ Abstract supertype for Matrix Product Operators (MPOs). abstract type AbstractMPO{O} <: AbstractVector{O} end # useful union types -const SparseMPO{O<:SparseBlockTensorMap} = AbstractMPO{O} +const SparseMPO{O <: SparseBlockTensorMap} = AbstractMPO{O} Base.isfinite(O::AbstractMPO) = isfinite(typeof(O)) # By default, define things in terms of parent @@ -32,13 +32,13 @@ physicalspace(mpo::AbstractMPO) = map(physicalspace, mpo) for ftype in (:spacetype, :sectortype, :storagetype) @eval TensorKit.$ftype(mpo::AbstractMPO) = $ftype(typeof(mpo)) - @eval TensorKit.$ftype(::Type{MPO}) where {MPO<:AbstractMPO} = $ftype(eltype(MPO)) + @eval TensorKit.$ftype(::Type{MPO}) where {MPO <: AbstractMPO} = $ftype(eltype(MPO)) end # Utility functions # ----------------- -remove_orphans!(mpo::AbstractMPO; tol=eps(real(scalartype(mpo)))^(3 / 4)) = mpo -function remove_orphans!(mpo::SparseMPO; tol=eps(real(scalartype(mpo)))^(3 / 4)) +remove_orphans!(mpo::AbstractMPO; tol = eps(real(scalartype(mpo)))^(3 / 4)) = mpo +function remove_orphans!(mpo::SparseMPO; tol = eps(real(scalartype(mpo)))^(3 / 4)) droptol!.(mpo, tol) if isfinite(mpo) @@ -70,7 +70,7 @@ function remove_orphans!(mpo::SparseMPO; tol=eps(real(scalartype(mpo)))^(3 / 4)) # slice empty columns on right or empty rows on left mask = filter(1:size(mpo[i], 4)) do j return j ∈ getindex.(nonzero_keys(mpo[i]), 4) && - j ∈ getindex.(nonzero_keys(mpo[i + 1]), 1) + j ∈ getindex.(nonzero_keys(mpo[i + 1]), 1) end changed |= length(mask) == size(mpo[i], 4) mpo[i] = mpo[i][:, :, :, mask] @@ -130,20 +130,14 @@ function _fuse_mpo_mpo(O1::MPOTensor, O2::MPOTensor, Fₗ, Fᵣ) physicalspace(O2) ⊗ fuse(right_virtualspace(O2) ⊗ right_virtualspace(O1)) return BraidingTensor{T}(V) elseif O1 isa BraidingTensor - @plansor O′[-1 -2; -3 -4] := Fₗ[-1; 1 2] * - O2[1 3; -3 5] * - τ[2 -2; 3 4] * - conj(Fᵣ[-4; 5 4]) + @plansor O′[-1 -2; -3 -4] := Fₗ[-1; 1 2] * O2[1 3; -3 5] * + τ[2 -2; 3 4] * conj(Fᵣ[-4; 5 4]) elseif O2 isa BraidingTensor - @plansor O′[-1 -2; -3 -4] := Fₗ[-1; 1 2] * - τ[1 3; -3 5] * - O1[2 -2; 3 4] * - conj(Fᵣ[-4; 5 4]) + @plansor O′[-1 -2; -3 -4] := Fₗ[-1; 1 2] * τ[1 3; -3 5] * + O1[2 -2; 3 4] * conj(Fᵣ[-4; 5 4]) else - @plansor O′[-1 -2; -3 -4] := Fₗ[-1; 1 2] * - O2[1 3; -3 5] * - O1[2 -2; 3 4] * - conj(Fᵣ[-4; 5 4]) + @plansor O′[-1 -2; -3 -4] := Fₗ[-1; 1 2] * O2[1 3; -3 5] * + O1[2 -2; 3 4] * conj(Fᵣ[-4; 5 4]) end end @@ -164,8 +158,9 @@ function fuse_mul_mpo(O1::BraidingTensor, O2::BraidingTensor) physicalspace(O2) ⊗ fuse(right_virtualspace(O2) ⊗ right_virtualspace(O1)) return BraidingTensor{T}(V) end -function fuse_mul_mpo(O1::AbstractBlockTensorMap{T₁,S,2,2}, - O2::AbstractBlockTensorMap{T₂,S,2,2}) where {T₁,T₂,S} +function fuse_mul_mpo( + O1::AbstractBlockTensorMap{T₁, S, 2, 2}, O2::AbstractBlockTensorMap{T₂, S, 2, 2} + ) where {T₁, T₂, S} TT = promote_type((eltype(O1)), eltype((O2))) V = fuse(left_virtualspace(O2) ⊗ left_virtualspace(O1)) ⊗ physicalspace(O1) ← physicalspace(O2) ⊗ fuse(right_virtualspace(O2) ⊗ right_virtualspace(O1)) @@ -174,10 +169,10 @@ function fuse_mul_mpo(O1::AbstractBlockTensorMap{T₁,S,2,2}, else O = BlockTensorMap{TT}(undef, V) end - cartesian_inds = reshape(CartesianIndices(O), - size(O2, 1), size(O1, 1), - size(O, 2), size(O, 3), - size(O2, 4), size(O1, 4)) + cartesian_inds = reshape( + CartesianIndices(O), + size(O2, 1), size(O1, 1), size(O, 2), size(O, 3), size(O2, 4), size(O1, 4) + ) for (I, o2) in nonzero_pairs(O2), (J, o1) in nonzero_pairs(O1) K = cartesian_inds[I[1], J[1], I[2], I[3], I[4], J[4]] O[K] = fuse_mul_mpo(o1, o2) @@ -190,8 +185,7 @@ function add_physical_charge(O::MPOTensor, charge::Sector) auxspace = Vect[typeof(charge)](charge => 1)' F = fuser(scalartype(O), physicalspace(O), auxspace) @plansor O_charged[-1 -2; -3 -4] := F[-2; 1 2] * - O[-1 1; 4 3] * - τ[3 2; 5 -4] * conj(F[-3; 4 5]) + O[-1 1; 4 3] * τ[3 2; 5 -4] * conj(F[-3; 4 5]) return O_charged end function add_physical_charge(O::BraidingTensor, charge::Sector) @@ -201,12 +195,14 @@ function add_physical_charge(O::BraidingTensor, charge::Sector) fuse(physicalspace(O), auxspace) ⊗ right_virtualspace(O) return BraidingTensor{scalartype(O)}(V) end -function add_physical_charge(O::AbstractBlockTensorMap{<:Any,<:Any,2,2}, charge::Sector) +function add_physical_charge(O::AbstractBlockTensorMap{<:Any, <:Any, 2, 2}, charge::Sector) sectortype(O) == typeof(charge) || throw(SectorMismatch()) auxspace = Vect[typeof(charge)](charge => 1)' - Odst = similar(O, - left_virtualspace(O) ⊗ fuse(physicalspace(O), auxspace) ← - fuse(physicalspace(O), auxspace) ⊗ right_virtualspace(O)) + Odst = similar( + O, + left_virtualspace(O) ⊗ fuse(physicalspace(O), auxspace) ← + fuse(physicalspace(O), auxspace) ⊗ right_virtualspace(O) + ) for (I, v) in nonzero_pairs(O) Odst[I] = add_physical_charge(v, charge) end @@ -216,9 +212,11 @@ end # Contractions # ------------ # This function usually does not require to be specified for many N, so @generated function is fine? -@generated function _instantiate_finitempo(L::AbstractTensorMap{<:Any,S,1,2}, - O::NTuple{N,MPOTensor{S}}, - R::AbstractTensorMap{<:Any,S,2,1}) where {N,S} +@generated function _instantiate_finitempo( + L::AbstractTensorMap{<:Any, S, 1, 2}, + O::NTuple{N, MPOTensor{S}}, + R::AbstractTensorMap{<:Any, S, 2, 1} + ) where {N, S} sites = N + 2 t_out = tensorexpr(:T, -(1:sites), -(1:sites) .- sites) t_left = tensorexpr(:L, -1, (-1 - sites, 1)) @@ -230,10 +228,12 @@ end return macroexpand(@__MODULE__, ex) end -@generated function _apply_finitempo(x::AbstractTensorMap{<:Any,S,M,A}, - L::AbstractTensorMap{<:Any,S,1,2}, - O::NTuple{N,MPOTensor{S}}, - R::AbstractTensorMap{<:Any,S,2,1}) where {N,M,S,A} +@generated function _apply_finitempo( + x::AbstractTensorMap{<:Any, S, M, A}, + L::AbstractTensorMap{<:Any, S, 1, 2}, + O::NTuple{N, MPOTensor{S}}, + R::AbstractTensorMap{<:Any, S, 2, 1} + ) where {N, M, S, A} M == N + 2 || throw(ArgumentError("Incompatible number of spaces")) t_out = tensorexpr(:y, -(1:M), -(1:A) .- M) t_in = tensorexpr(:x, 1:2:(2M - 1), -(1:A) .- M) diff --git a/src/operators/jordanmpotensor.jl b/src/operators/jordanmpotensor.jl index 4166fc1fc..e46356f35 100644 --- a/src/operators/jordanmpotensor.jl +++ b/src/operators/jordanmpotensor.jl @@ -11,20 +11,22 @@ A tensor map that represents the upper triangular block form of a matrix product \\end{pmatrix} ``` """ -struct JordanMPOTensor{E,S, - TA<:AbstractTensorMap{E,S,2,2}, - TB<:AbstractTensorMap{E,S,2,1}, - TC<:AbstractTensorMap{E,S,1,2}, - TD<:AbstractTensorMap{E,S,1,1}} <: AbstractBlockTensorMap{E,S,2,2} - V::TensorMapSumSpace{S,2,2} - A::SparseBlockTensorMap{TA,E,S,2,2,4} - B::SparseBlockTensorMap{TB,E,S,2,1,3} - C::SparseBlockTensorMap{TC,E,S,1,2,3} - D::SparseBlockTensorMap{TD,E,S,1,1,2} +struct JordanMPOTensor{ + E, S, + TA <: AbstractTensorMap{E, S, 2, 2}, + TB <: AbstractTensorMap{E, S, 2, 1}, + TC <: AbstractTensorMap{E, S, 1, 2}, + TD <: AbstractTensorMap{E, S, 1, 1}, + } <: AbstractBlockTensorMap{E, S, 2, 2} + V::TensorMapSumSpace{S, 2, 2} + A::SparseBlockTensorMap{TA, E, S, 2, 2, 4} + B::SparseBlockTensorMap{TB, E, S, 2, 1, 3} + C::SparseBlockTensorMap{TC, E, S, 1, 2, 3} + D::SparseBlockTensorMap{TD, E, S, 1, 1, 2} # uninitialized constructor - function JordanMPOTensor{E,S,TA,TB,TC,TD}(::UndefInitializer, - V::TensorMapSumSpace{S,2,2}) where {E,S,TA,TB, - TC,TD} + function JordanMPOTensor{E, S, TA, TB, TC, TD}( + ::UndefInitializer, V::TensorMapSumSpace{S, 2, 2} + ) where {E, S, TA, TB, TC, TD} allVs = eachspace(V) # Note that this is a bit of a hack using end to get the last index: @@ -41,36 +43,35 @@ struct JordanMPOTensor{E,S, VD = removeunit(removeunit(space(allVs[1, 1, 1, end:end]), 4), 1) D = SparseBlockTensorMap{TD}(undef, VD) - return new{E,S,TA,TB,TC,TD}(V, A, B, C, D) + return new{E, S, TA, TB, TC, TD}(V, A, B, C, D) end # constructor from data - function JordanMPOTensor{E,S,TA,TB,TC,TD}(V::TensorMapSumSpace, - A::SparseBlockTensorMap{TA,E,S,2,2}, - B::SparseBlockTensorMap{TB,E,S,2,1}, - C::SparseBlockTensorMap{TC,E,S,1,2}, - D::SparseBlockTensorMap{TD,E,S,1,1}) where {E, - S, - TA, - TB, - TC, - TD} - return new{E,S,TA,TB,TC,TD}(V, A, B, C, D) + function JordanMPOTensor{E, S, TA, TB, TC, TD}( + V::TensorMapSumSpace, + A::SparseBlockTensorMap{TA, E, S, 2, 2}, + B::SparseBlockTensorMap{TB, E, S, 2, 1}, + C::SparseBlockTensorMap{TC, E, S, 1, 2}, + D::SparseBlockTensorMap{TD, E, S, 1, 1} + ) where {E, S, TA, TB, TC, TD} + return new{E, S, TA, TB, TC, TD}(V, A, B, C, D) end end -function JordanMPOTensor{E,S}(::UndefInitializer, V::TensorMapSumSpace{S}) where {E,S} +function JordanMPOTensor{E, S}(::UndefInitializer, V::TensorMapSumSpace{S}) where {E, S} return jordanmpotensortype(S, E)(undef, V) end -function JordanMPOTensor{E}(::UndefInitializer, V::TensorMapSumSpace{S}) where {E,S} - return JordanMPOTensor{E,S}(undef, V) +function JordanMPOTensor{E}(::UndefInitializer, V::TensorMapSumSpace{S}) where {E, S} + return JordanMPOTensor{E, S}(undef, V) end -function JordanMPOTensor(V::TensorMapSumSpace{S,2,2}, - A::SparseBlockTensorMap{TA,E,S,2,2}, - B::SparseBlockTensorMap{TB,E,S,2,1}, - C::SparseBlockTensorMap{TC,E,S,1,2}, - D::SparseBlockTensorMap{TD,E,S,1,1}) where {E,S,TA,TB,TC,TD} +function JordanMPOTensor( + V::TensorMapSumSpace{S, 2, 2}, + A::SparseBlockTensorMap{TA, E, S, 2, 2}, + B::SparseBlockTensorMap{TB, E, S, 2, 1}, + C::SparseBlockTensorMap{TC, E, S, 1, 2}, + D::SparseBlockTensorMap{TD, E, S, 1, 1} + ) where {E, S, TA, TB, TC, TD} allVs = eachspace(V) VA = space(allVs[2:(end - 1), 1, 1, 2:(end - 1)]) VA == space(A) || throw(SpaceMismatch("A-block has incompatible spaces")) @@ -84,10 +85,10 @@ function JordanMPOTensor(V::TensorMapSumSpace{S,2,2}, VD = removeunit(removeunit(space(allVs[1, 1, 1, end:end]), 4), 1) VD == space(D) || throw(SpaceMismatch("D-block has incompatible spaces")) - return JordanMPOTensor{E,S,TA,TB,TC,TD}(V, A, B, C, D) + return JordanMPOTensor{E, S, TA, TB, TC, TD}(V, A, B, C, D) end -function JordanMPOTensor(W::SparseBlockTensorMap{TT,E,S,2,2}) where {TT,E,S} +function JordanMPOTensor(W::SparseBlockTensorMap{TT, E, S, 2, 2}) where {TT, E, S} @assert W[1, 1, 1, 1] isa BraidingTensor && W[end, 1, 1, end] isa BraidingTensor # @assert all(I -> I[1] ≤ I[4], nonzero_keys(W)) @@ -96,39 +97,39 @@ function JordanMPOTensor(W::SparseBlockTensorMap{TT,E,S,2,2}) where {TT,E,S} C = W[1, 1, 1, 2:(end - 1)] D = W[1, 1, 1, end:end] # ensure still blocktensor to allow for sparse - return JordanMPOTensor(space(W), - A, - removeunit(B, 4), - removeunit(C, 1), - removeunit(removeunit(D, 4), 1)) + return JordanMPOTensor( + space(W), A, removeunit(B, 4), removeunit(C, 1), removeunit(removeunit(D, 4), 1) + ) end -function jordanmpotensortype(::Type{S}, ::Type{E}) where {S<:VectorSpace,E<:Number} - TA = Union{tensormaptype(S, 2, 2, E),BraidingTensor{E,S}} +function jordanmpotensortype(::Type{S}, ::Type{E}) where {S <: VectorSpace, E <: Number} + TA = Union{tensormaptype(S, 2, 2, E), BraidingTensor{E, S}} TB = tensormaptype(S, 2, 1, E) TC = tensormaptype(S, 1, 2, E) TD = tensormaptype(S, 1, 1, E) - return JordanMPOTensor{E,S,TA,TB,TC,TD} + return JordanMPOTensor{E, S, TA, TB, TC, TD} end -function jordanmpotensortype(::Type{O}) where {O<:MPOTensor} +function jordanmpotensortype(::Type{O}) where {O <: MPOTensor} return jordanmpotensortype(spacetype(O), scalartype(O)) end -function Base.similar(W::JordanMPOTensor, ::Type{T}) where {T<:Number} +function Base.similar(W::JordanMPOTensor, ::Type{T}) where {T <: Number} return JordanMPOTensor{T}(undef, space(W)) end # Properties # ---------- TensorKit.space(W::JordanMPOTensor) = W.V -Base.eltype(::Type{JordanMPOTensor{E,S,TA,TB,TC,TD}}) where {E,S,TA,TB,TC,TD} = TA +Base.eltype(::Type{JordanMPOTensor{E, S, TA, TB, TC, TD}}) where {E, S, TA, TB, TC, TD} = TA function Base.haskey(W::JordanMPOTensor, I::CartesianIndex{4}) Base.checkbounds(W, I.I...) # only has braiding tensors if sizes are large enough sz = size(W) - (sz[1] > 1 && I == CartesianIndex(1, 1, 1, 1) || - sz[4] > 1 && I == CartesianIndex(sz[1], 1, 1, sz[4])) && return true + ( + sz[1] > 1 && I == CartesianIndex(1, 1, 1, 1) || + sz[4] > 1 && I == CartesianIndex(sz[1], 1, 1, sz[4]) + ) && return true row, col = I.I[1], I.I[4] @@ -153,8 +154,9 @@ BlockTensorKit.issparse(W::JordanMPOTensor) = true # ---------- function SparseBlockTensorMap(W::JordanMPOTensor) τ = BraidingTensor{scalartype(W)}(eachspace(W)[1]) - W′ = SparseBlockTensorMap{AbstractTensorMap{scalartype(W),spacetype(W),2,2}}(undef_blocks, - space(W)) + W′ = SparseBlockTensorMap{AbstractTensorMap{scalartype(W), spacetype(W), 2, 2}}( + undef_blocks, space(W) + ) if size(W, 4) > 1 W′[1, 1, 1, 1] = τ end @@ -185,12 +187,12 @@ end # -------- @inline Base.getindex(W::JordanMPOTensor, I::CartesianIndex{4}) = W[I.I...] -@propagate_inbounds function Base.getindex(W::JordanMPOTensor, I::Vararg{Int,4}) +@propagate_inbounds function Base.getindex(W::JordanMPOTensor, I::Vararg{Int, 4}) @assert I[2] == I[3] == 1 i = I[1] j = I[4] if (size(W, 4) > 1 && i == 1 && j == 1) || - (size(W, 1) > 1 && i == size(W, 1) && j == size(W, 4)) + (size(W, 1) > 1 && i == size(W, 1) && j == size(W, 4)) return BraidingTensor{scalartype(W)}(eachspace(W)[1]) elseif i == 1 && j == size(W, 4) return insertrightunit(insertleftunit(only(W.D), 1), 3) @@ -208,8 +210,9 @@ end @inline function Base.setindex!(W::JordanMPOTensor, v::MPOTensor, I::CartesianIndex{4}) return setindex!(W, v, I.I...) end -@propagate_inbounds function Base.setindex!(W::JordanMPOTensor, v::MPOTensor, - I::Vararg{Int,4}) +@propagate_inbounds function Base.setindex!( + W::JordanMPOTensor, v::MPOTensor, I::Vararg{Int, 4} + ) @assert I[2] == I[3] == 1 i = I[1] j = I[4] @@ -222,7 +225,7 @@ end elseif 1 < i < size(W, 1) && 1 < j < size(W, 4) W.A[i - 1, 1, 1, j - 1] = v elseif (size(W, 4) > 1 && i == 1 && j == 1) || - (size(W, 1) > 1 && i == size(W, 1) && j == size(W, 4)) + (size(W, 1) > 1 && i == size(W, 1) && j == size(W, 4)) v isa BraidingTensor || throw(ArgumentError("Cannot set BraidingTensor")) else throw(ArgumentError("Cannot set index ($i, 1, 1, $j)")) @@ -271,7 +274,7 @@ function BlockTensorKit.nonzero_pairs(W::JordanMPOTensor) end function BlockTensorKit.nonzero_length(W::JordanMPOTensor) return nonzero_length(W.A) + nonzero_length(W.B) + nonzero_length(W.C) + - nonzero_length(W.D) + Int(size(W, 1) > 1) + Int(size(W, 4) > 1) + nonzero_length(W.D) + Int(size(W, 1) > 1) + Int(size(W, 4) > 1) end # linalg @@ -289,10 +292,10 @@ function fuse_mul_mpo(O1::JordanMPOTensor, O2::JordanMPOTensor) V = fuse(left_virtualspace(O2) ⊗ left_virtualspace(O1)) ⊗ physicalspace(O1) ← physicalspace(O2) ⊗ fuse(right_virtualspace(O2) ⊗ right_virtualspace(O1)) O = jordanmpotensortype(TT)(undef, V) - cartesian_inds = reshape(CartesianIndices(O), - size(O2, 1), size(O1, 1), - size(O, 2), size(O, 3), - size(O2, 4), size(O1, 4)) + cartesian_inds = reshape( + CartesianIndices(O), + size(O2, 1), size(O1, 1), size(O, 2), size(O, 3), size(O2, 4), size(O1, 4) + ) for (I, o2) in nonzero_pairs(O2), (J, o1) in nonzero_pairs(O1) K = cartesian_inds[I[1], J[1], I[2], I[3], I[4], J[4]] O[K] = fuse_mul_mpo(o1, o2) @@ -313,8 +316,8 @@ function add_physical_charge(O::JordanMPOTensor, charge::Sector) sectortype(O) == typeof(charge) || throw(SectorMismatch()) auxspace = Vect[typeof(charge)](charge => 1)' Vdst = left_virtualspace(O) ⊗ - fuse(physicalspace(O), auxspace) ← - fuse(physicalspace(O), auxspace) ⊗ right_virtualspace(O) + fuse(physicalspace(O), auxspace) ← + fuse(physicalspace(O), auxspace) ⊗ right_virtualspace(O) Odst = JordanMPOTensor{scalartype(O)}(undef, Vdst) for (I, v) in nonzero_pairs(O) Odst[I] = add_physical_charge(v, charge) @@ -339,9 +342,9 @@ end # Avoid falling back to `norm(W1 - W2)` which has to convert to SparseBlockTensorMap function Base.isapprox(W1::JordanMPOTensor, W2::JordanMPOTensor; kwargs...) return isapprox(W1.A, W2.A; kwargs...) && - isapprox(W1.B, W2.B; kwargs...) && - isapprox(W1.C, W2.C; kwargs...) && - isapprox(W1.D, W2.D; kwargs...) + isapprox(W1.B, W2.B; kwargs...) && + isapprox(W1.C, W2.C; kwargs...) && + isapprox(W1.D, W2.D; kwargs...) end function Base.summary(io::IO, W::JordanMPOTensor) diff --git a/src/operators/mpo.jl b/src/operators/mpo.jl index 8deb2202d..e1d77bc68 100644 --- a/src/operators/mpo.jl +++ b/src/operators/mpo.jl @@ -5,7 +5,7 @@ Matrix Product Operator (MPO) acting on a tensor product space with a linear ord See also: [`FiniteMPO`](@ref), [`InfiniteMPO`](@ref) """ -struct MPO{TO,V<:AbstractVector{TO}} <: AbstractMPO{TO} +struct MPO{TO, V <: AbstractVector{TO}} <: AbstractMPO{TO} O::V end @@ -15,7 +15,7 @@ end Matrix Product Operator (MPO) acting on a finite tensor product space with a linear order. """ -const FiniteMPO{O} = MPO{O,Vector{O}} +const FiniteMPO{O} = MPO{O, Vector{O}} Base.isfinite(::Type{<:FiniteMPO}) = true function FiniteMPO(Os::AbstractVector{O}) where {O} @@ -26,7 +26,7 @@ function FiniteMPO(Os::AbstractVector{O}) where {O} return FiniteMPO{O}(Os) end -function FiniteMPO(O::AbstractTensorMap{T,S,N,N}) where {T,S,N} +function FiniteMPO(O::AbstractTensorMap{T, S, N, N}) where {T, S, N} return FiniteMPO(decompose_localmpo(add_util_leg(O))) end @@ -35,7 +35,7 @@ end Matrix Product Operator (MPO) acting on an infinite tensor product space with a linear order. """ -const InfiniteMPO{O} = MPO{O,PeriodicVector{O}} +const InfiniteMPO{O} = MPO{O, PeriodicVector{O}} Base.isfinite(::Type{<:InfiniteMPO}) = false function InfiniteMPO(Os::AbstractVector{O}) where {O} @@ -55,10 +55,10 @@ DenseMPO(mpo::MPO) = mpo isa DenseMPO ? copy(mpo) : MPO(map(TensorMap, parent(mp Base.parent(mpo::MPO) = mpo.O Base.copy(mpo::MPO) = MPO(copy.(parent(mpo))) -function Base.similar(mpo::MPO{<:MPOTensor}, ::Type{O}, L::Int) where {O<:MPOTensor} +function Base.similar(mpo::MPO{<:MPOTensor}, ::Type{O}, L::Int) where {O <: MPOTensor} return MPO(similar(parent(mpo), O, L)) end -function Base.similar(mpo::MPO, ::Type{T}) where {T<:Number} +function Base.similar(mpo::MPO, ::Type{T}) where {T <: Number} return MPO(similar.(parent(mpo), T)) end @@ -93,7 +93,7 @@ end function Base.convert(::Type{<:InfiniteMPO}, mps::InfiniteMPS) return InfiniteMPO(map(_mps_to_mpo, mps.AL)) end -function _mps_to_mpo(A::GenericMPSTensor{S,3}) where {S} +function _mps_to_mpo(A::GenericMPSTensor{S, 3}) where {S} @plansor O[-1 -2; -3 -4] := A[-1 -2 1; 2] * τ[-3 2; -4 1] return O end @@ -115,19 +115,21 @@ Base.:+(mpo::MPO) = MPO(map(+, parent(mpo))) function Base.:+(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) N = check_length(mpo1, mpo2) @assert left_virtualspace(mpo1, 1) == left_virtualspace(mpo2, 1) && - right_virtualspace(mpo1, N) == right_virtualspace(mpo2, N) + right_virtualspace(mpo1, N) == right_virtualspace(mpo2, N) halfN = N ÷ 2 A = storagetype(eltype(mpo1)) # left half - F₁ = isometry(A, (right_virtualspace(mpo1, 1) ⊕ right_virtualspace(mpo2, 1)), - right_virtualspace(mpo1, 1)) + F₁ = isometry( + A, (right_virtualspace(mpo1, 1) ⊕ right_virtualspace(mpo2, 1)), + right_virtualspace(mpo1, 1) + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == right_virtualspace(mpo2, 1)' @plansor O[-3 -1 -2; -4] := mpo1[1][-1 -2; -3 1] * conj(F₁[-4; 1]) + - mpo2[1][-1 -2; -3 1] * conj(F₂[-4; 1]) + mpo2[1][-1 -2; -3 1] * conj(F₂[-4; 1]) # making sure that the new operator is "full rank" O, R = leftorth!(O) @@ -141,12 +143,14 @@ function Base.:+(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) @plansor O₂[-1 -2; -3 -4] := R[-1; 1] * F₂[1; 2] * mpo2[i][2 -2; -3 -4] # incorporate fusers from right side - F₁ = isometry(A, (right_virtualspace(mpo1, i) ⊕ right_virtualspace(mpo2, i)), - right_virtualspace(mpo1, i)) + F₁ = isometry( + A, (right_virtualspace(mpo1, i) ⊕ right_virtualspace(mpo2, i)), + right_virtualspace(mpo1, i) + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == right_virtualspace(mpo2, i)' @plansor O[-3 -1 -2; -4] := O₁[-1 -2; -3 1] * conj(F₁[-4; 1]) + - O₂[-1 -2; -3 1] * conj(F₂[-4; 1]) + O₂[-1 -2; -3 1] * conj(F₂[-4; 1]) # making sure that the new operator is "full rank" O, R = leftorth!(O) @@ -156,13 +160,15 @@ function Base.:+(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) C₁, C₂ = F₁, F₂ # right half - F₁ = isometry(A, left_virtualspace(mpo1, N) ⊕ left_virtualspace(mpo2, N), - left_virtualspace(mpo1, N)) + F₁ = isometry( + A, left_virtualspace(mpo1, N) ⊕ left_virtualspace(mpo2, N), + left_virtualspace(mpo1, N) + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == left_virtualspace(mpo2, N)' @plansor O[-1; -3 -4 -2] := F₁[-1; 1] * mpo1[N][1 -2; -3 -4] + - F₂[-1; 1] * mpo2[N][1 -2; -3 -4] + F₂[-1; 1] * mpo2[N][1 -2; -3 -4] # making sure that the new operator is "full rank" L, O = rightorth!(O) @@ -174,12 +180,14 @@ function Base.:+(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) @plansor O₂[-1 -2; -3 -4] := mpo2[i][-1 -2; -3 2] * conj(F₂[1; 2]) * L[1; -4] # incorporate fusers from left side - F₁ = isometry(A, left_virtualspace(mpo1, i) ⊕ left_virtualspace(mpo2, i), - left_virtualspace(mpo1, i)) + F₁ = isometry( + A, left_virtualspace(mpo1, i) ⊕ left_virtualspace(mpo2, i), + left_virtualspace(mpo1, i) + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == left_virtualspace(mpo2, i)' @plansor O[-1; -3 -4 -2] := F₁[-1; 1] * O₁[1 -2; -3 -4] + - F₂[-1; 1] * O₂[1 -2; -3 -4] + F₂[-1; 1] * O₂[1 -2; -3 -4] # making sure that the new operator is "full rank" L, O = rightorth!(O) @@ -211,17 +219,15 @@ function Base.:*(mpo1::FiniteMPO{<:MPOTensor}, mpo2::FiniteMPO{<:MPOTensor}) N = check_length(mpo1, mpo2) (S = spacetype(mpo1)) == spacetype(mpo2) || throw(SectorMismatch()) - if (left_virtualspace(mpo1, 1) != oneunit(S) || - left_virtualspace(mpo2, 1) != oneunit(S)) || - (right_virtualspace(mpo1, N) != oneunit(S) || - right_virtualspace(mpo2, N) != oneunit(S)) + if (left_virtualspace(mpo1, 1) != oneunit(S) || left_virtualspace(mpo2, 1) != oneunit(S)) || + (right_virtualspace(mpo1, N) != oneunit(S) || right_virtualspace(mpo2, N) != oneunit(S)) @warn "left/right virtual space is not trivial, fusion may not be unique" # this is a warning because technically any isomorphism that fuses the left/right # would work and for now I dont feel like figuring out if this is important end O = map(fuse_mul_mpo, parent(mpo1), parent(mpo2)) - return changebonds!(FiniteMPO(O), SvdCut(; trscheme=notrunc())) + return changebonds!(FiniteMPO(O), SvdCut(; trscheme = notrunc())) end function Base.:*(mpo1::InfiniteMPO, mpo2::InfiniteMPO) check_length(mpo1, mpo2) @@ -241,25 +247,23 @@ function Base.:*(mpo::FiniteMPO, mps::FiniteMPS) return _fuse_mpo_mps(mpo[i], A1, Fₗ, Fᵣ) end trscheme = truncbelow(eps(real(T))) - return changebonds!(FiniteMPS(A2), SvdCut(; trscheme); normalize=false) + return changebonds!(FiniteMPS(A2), SvdCut(; trscheme); normalize = false) end function Base.:*(mpo::InfiniteMPO, mps::InfiniteMPS) L = check_length(mpo, mps) T = promote_type(scalartype(mpo), scalartype(mps)) A = TensorKit.similarstoragetype(eltype(mps), T) - fusers = PeriodicArray(fuser.(A, left_virtualspace.(Ref(mps), 1:L), - left_virtualspace.(Ref(mpo), 1:L))) + fusers = PeriodicArray( + fuser.(A, left_virtualspace.(Ref(mps), 1:L), left_virtualspace.(Ref(mpo), 1:L)) + ) As = map(1:L) do i return _fuse_mpo_mps(mpo[i], mps.AL[i], fusers[i], fusers[i + 1]) end - return changebonds(InfiniteMPS(As), SvdCut(; trscheme=notrunc())) + return changebonds(InfiniteMPS(As), SvdCut(; trscheme = notrunc())) end function _fuse_mpo_mps(O::MPOTensor, A::MPSTensor, Fₗ, Fᵣ) - @plansor A′[-1 -2; -3] := Fₗ[-1; 1 3] * - A[1 2; 4] * - O[3 -2; 2 5] * - conj(Fᵣ[-3; 4 5]) + @plansor A′[-1 -2; -3] := Fₗ[-1; 1 3] * A[1 2; 4] * O[3 -2; 2 5] * conj(Fᵣ[-3; 4 5]) return A′ isa AbstractBlockTensorMap ? TensorMap(A′) : A′ end @@ -276,40 +280,51 @@ end # TODO: I think the fastest order is to start from both ends, and take the overlap at the # largest virtual space cut, but it might be better to just multithread both sides and meet # in the middle -function TensorKit.dot(bra::FiniteMPS{T}, mpo::FiniteMPO{<:MPOTensor}, - ket::FiniteMPS{T}) where {T} +function TensorKit.dot( + bra::FiniteMPS{T}, mpo::FiniteMPO{<:MPOTensor}, ket::FiniteMPS{T} + ) where {T} N = check_length(bra, mpo, ket) Nhalf = N ÷ 2 # left half - ρ_left = isomorphism(storagetype(T), - left_virtualspace(bra, 1) ⊗ left_virtualspace(mpo, 1)', - left_virtualspace(ket, 1)) + ρ_left = isomorphism( + storagetype(T), + left_virtualspace(bra, 1) ⊗ left_virtualspace(mpo, 1)', + left_virtualspace(ket, 1) + ) T_left = TransferMatrix(ket.AL[1:Nhalf], mpo[1:Nhalf], bra.AL[1:Nhalf]) ρ_left = ρ_left * T_left # right half - ρ_right = isomorphism(storagetype(T), - right_virtualspace(ket, N) ⊗ right_virtualspace(mpo, N), - right_virtualspace(bra, N)) - T_right = TransferMatrix(ket.AR[(Nhalf + 1):end], mpo[(Nhalf + 1):end], - bra.AR[(Nhalf + 1):end]) + ρ_right = isomorphism( + storagetype(T), + right_virtualspace(ket, N) ⊗ right_virtualspace(mpo, N), + right_virtualspace(bra, N) + ) + T_right = TransferMatrix( + ket.AR[(Nhalf + 1):end], mpo[(Nhalf + 1):end], + bra.AR[(Nhalf + 1):end] + ) ρ_right = T_right * ρ_right # center return @plansor ρ_left[3 4; 1] * ket.C[Nhalf][1; 5] * ρ_right[5 4; 2] * - conj(bra.C[Nhalf][3; 2]) + conj(bra.C[Nhalf][3; 2]) end -function TensorKit.dot(bra::InfiniteMPS, mpo::InfiniteMPO, ket::InfiniteMPS; - ishermitian=false, krylovdim=30, kwargs...) +function TensorKit.dot( + bra::InfiniteMPS, mpo::InfiniteMPO, ket::InfiniteMPS; + ishermitian = false, krylovdim = 30, kwargs... + ) ρ₀ = allocate_GL(bra, mpo, ket, 1) randomize!(ρ₀) - val, = fixedpoint(TransferMatrix(ket.AL, parent(mpo), bra.AL), ρ₀, :LM; ishermitian, - krylovdim, kwargs...) + val, = fixedpoint( + TransferMatrix(ket.AL, parent(mpo), bra.AL), ρ₀, :LM; ishermitian, + krylovdim, kwargs... + ) return val end -function TensorKit.dot(mpo₁::FiniteMPO{TO}, mpo₂::FiniteMPO{TO}) where {TO<:MPOTensor} +function TensorKit.dot(mpo₁::FiniteMPO{TO}, mpo₂::FiniteMPO{TO}) where {TO <: MPOTensor} length(mpo₁) == length(mpo₂) || throw(ArgumentError("dimension mismatch")) N = length(mpo₁) Nhalf = N ÷ 2 @@ -317,13 +332,13 @@ function TensorKit.dot(mpo₁::FiniteMPO{TO}, mpo₂::FiniteMPO{TO}) where {TO<: @plansor ρ_left[-1; -2] := conj(mpo₁[1][1 2; 3 -1]) * mpo₂[1][1 2; 3 -2] for i in 2:Nhalf @plansor ρ_left[-1; -2] := ρ_left[1; 2] * conj(mpo₁[i][1 3; 4 -1]) * - mpo₂[i][2 3; 4 -2] + mpo₂[i][2 3; 4 -2] end # right half @plansor ρ_right[-1; -2] := conj(mpo₁[N][-2 1; 2 3]) * mpo₂[N][-1 1; 2 3] for i in (N - 1):-1:(Nhalf + 1) @plansor ρ_right[-1; -2] := ρ_right[1; 2] * conj(mpo₁[i][-2 4; 3 2]) * - mpo₂[i][-1 4; 3 1] + mpo₂[i][-1 4; 3 1] end return @plansor ρ_left[1; 2] * ρ_right[2; 1] end @@ -352,8 +367,10 @@ function LinearAlgebra.tr(mpo::MPO) return @plansor ρ_left[(); 1] * ρ_right[1; ()] end -function Base.isapprox(mpo₁::MPO, mpo₂::MPO; - atol::Real=0, rtol::Real=atol > 0 ? 0 : √eps(real(scalartype(mpo₁)))) +function Base.isapprox( + mpo₁::MPO, mpo₂::MPO; + atol::Real = 0, rtol::Real = atol > 0 ? 0 : √eps(real(scalartype(mpo₁))) + ) check_length(mpo₁, mpo₂) # computing ||mpo₁ - mpo₂|| without constructing mpo₁ - mpo₂ # ||mpo₁ - mpo₂||² = ||mpo₁||² + ||mpo₂||² - 2 ⟨mpo₁, mpo₂⟩ diff --git a/src/operators/mpohamiltonian.jl b/src/operators/mpohamiltonian.jl index 347b3bd48..0727f4ca3 100644 --- a/src/operators/mpohamiltonian.jl +++ b/src/operators/mpohamiltonian.jl @@ -28,14 +28,14 @@ H = MPOHamiltonian(lattice, (i, i+1) => O for i in 1:length(lattice)-1) See also [`instantiate_operator`](@ref), which is responsable for instantiating the local operators in a form that is compatible with this constructor. """ -struct MPOHamiltonian{TO<:JordanMPOTensor,V<:AbstractVector{TO}} <: AbstractMPO{TO} +struct MPOHamiltonian{TO <: JordanMPOTensor, V <: AbstractVector{TO}} <: AbstractMPO{TO} W::V end -const FiniteMPOHamiltonian{O<:MPOTensor} = MPOHamiltonian{O,Vector{O}} +const FiniteMPOHamiltonian{O <: MPOTensor} = MPOHamiltonian{O, Vector{O}} Base.isfinite(::Type{<:FiniteMPOHamiltonian}) = true -function FiniteMPOHamiltonian(Ws::AbstractVector{O}) where {O<:MPOTensor} +function FiniteMPOHamiltonian(Ws::AbstractVector{O}) where {O <: MPOTensor} for i in eachindex(Ws)[1:(end - 1)] right_virtualspace(Ws[i]) == left_virtualspace(Ws[i + 1]) || throw(ArgumentError("The virtual spaces of the MPO tensors at site $i do not match.")) @@ -43,10 +43,10 @@ function FiniteMPOHamiltonian(Ws::AbstractVector{O}) where {O<:MPOTensor} return FiniteMPOHamiltonian{O}(Ws) end -const InfiniteMPOHamiltonian{O<:MPOTensor} = MPOHamiltonian{O,PeriodicVector{O}} +const InfiniteMPOHamiltonian{O <: MPOTensor} = MPOHamiltonian{O, PeriodicVector{O}} Base.isfinite(::Type{<:InfiniteMPOHamiltonian}) = false -function InfiniteMPOHamiltonian(Ws::AbstractVector{O}) where {O<:MPOTensor} +function InfiniteMPOHamiltonian(Ws::AbstractVector{O}) where {O <: MPOTensor} for i in eachindex(Ws) right_virtualspace(Ws[i]) == left_virtualspace(Ws[mod1(i + 1, end)]) || throw(ArgumentError("The virtual spaces of the MPO tensors at site $i do not match.")) @@ -66,7 +66,7 @@ function FiniteMPOHamiltonian(Ws::Vector{<:Matrix}) W = jordanmpotensortype(T) return FiniteMPOHamiltonian{W}(Ws) end -function FiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:JordanMPOTensor} +function FiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O <: JordanMPOTensor} T = scalartype(O) L = length(W_mats) # initialize sumspaces @@ -108,7 +108,7 @@ function FiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:JordanMPOTe row, col = I.I if Welem isa MPOTensor V_left = left_virtualspace(Welem) - @assert Vs_left[row] == V_left "incompatible space between sites $(site-1) and $site at level $row" + @assert Vs_left[row] == V_left "incompatible space between sites $(site - 1) and $site at level $row" V_right = right_virtualspace(Welem) Vs_right[col] = V_right elseif !iszero(Welem) # Welem isa Number @@ -122,9 +122,10 @@ function FiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:JordanMPOTe # instantiate tensors Ws = map(enumerate(W_mats)) do (site, W_mat) - W = jordanmpotensortype(S, T)(undef, - Vspaces[site] ⊗ Pspaces[site] ← - Pspaces[site] ⊗ Vspaces[site + 1]) + W = jordanmpotensortype(S, T)( + undef, + Vspaces[site] ⊗ Pspaces[site] ← Pspaces[site] ⊗ Vspaces[site + 1] + ) for (I, v) in enumerate(W_mat) ismissing(v) && continue if v isa MPOTensor @@ -152,7 +153,7 @@ function InfiniteMPOHamiltonian(Ws::Vector{<:Matrix}) TW = jordanmpotensortype(T) return InfiniteMPOHamiltonian{TW}(Ws) end -function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:MPOTensor} +function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O <: MPOTensor} # InfiniteMPOHamiltonian only works for square matrices: for W_mat in W_mats size(W_mat, 1) == size(W_mat, 2) || @@ -178,7 +179,7 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:MPOTensor # note that we assume that the FSA does not contain "dead ends", as this would mess with the # ability to deduce spaces. # also assume spacecheck errors will happen when filling the BlockTensors - MissingS = Union{Missing,S} + MissingS = Union{Missing, S} Vspaces = PeriodicArray([Vector{MissingS}(missing, nlvls) for _ in 1:L]) for V in Vspaces V[1] = V[end] = oneunit(S) @@ -203,7 +204,7 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:MPOTensor Vs_left[row] = V_left haschanged = true else - @assert Vs_left[row] == V_left "incompatible space between sites $(site-1) and $site at level $row" + @assert Vs_left[row] == V_left "incompatible space between sites $(site - 1) and $site at level $row" end V_right = right_virtualspace(Welem) @@ -211,7 +212,7 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:MPOTensor Vs_right[col] = V_right haschanged = true else - @assert Vs_right[col] == V_right "incompatible space between sites $(site) and $(site+1) at level $col" + @assert Vs_right[col] == V_right "incompatible space between sites $(site) and $(site + 1) at level $col" end elseif !iszero(Welem) # Welem isa Number if ismissing(Vs_left[row]) && !ismissing(Vs_right[col]) @@ -221,7 +222,7 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:MPOTensor Vs_right[col] = Vs_left[row] haschanged = true else - @assert Vs_left[row] == Vs_right[col] "incompatible space between sites $(site-1) and $site at level $row" + @assert Vs_left[row] == Vs_right[col] "incompatible space between sites $(site - 1) and $site at level $row" end end end @@ -238,9 +239,10 @@ function InfiniteMPOHamiltonian{O}(W_mats::Vector{<:Matrix}) where {O<:MPOTensor # instantiate tensors Ws = map(enumerate(W_mats)) do (site, W_mat) - W = jordanmpotensortype(S, T)(undef, - Vsumspaces[site] ⊗ Pspaces[site] ← - Pspaces[site] ⊗ Vsumspaces[site + 1]) + W = jordanmpotensortype(S, T)( + undef, + Vsumspaces[site] ⊗ Pspaces[site] ← Pspaces[site] ⊗ Vsumspaces[site + 1] + ) for (I, v) in enumerate(W_mat) ismissing(v) && continue if v isa MPOTensor @@ -261,7 +263,7 @@ function _split_mpoham_types(W::Matrix)::Type{<:MPOTensor} T = eltype(W) if T <: MPOTensor return T - elseif T <: Union{Missing,Number,MPOTensor} + elseif T <: Union{Missing, Number, MPOTensor} Ts = collect(DataType, Base.uniontypes(T)) # find MPO type iTO = findall(x -> x <: MPOTensor, Ts) @@ -312,7 +314,7 @@ function instantiate_operator(lattice::AbstractArray{<:VectorSpace}, (inds′, O return Base._to_linear_index(lattice, Tuple(I)...) # this should mean all inds are valid... end T = eltype(mpo) - local_mpo = Union{T,scalartype(T)}[] + local_mpo = Union{T, scalartype(T)}[] sites = Int[] i = 1 @@ -333,13 +335,13 @@ end # yields the promoted tensortype of all tensors function _find_tensortype(nonzero_operators::AbstractArray) return mapreduce(promote_type, nonzero_operators) do x - return mapreduce(promote_type, x; init=Base.Bottom) do y + return mapreduce(promote_type, x; init = Base.Bottom) do y return y isa AbstractTensorMap ? typeof(y) : Base.Bottom end end end -function _find_channel(nonzero_keys; init=2) +function _find_channel(nonzero_keys; init = 2) init = max(init, 2) range = unique!(last.(nonzero_keys)) isempty(range) && return init @@ -349,13 +351,12 @@ function _find_channel(nonzero_keys; init=2) return max(maximum(range) + 1, init) end -function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, - local_operators) +function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_operators) # initialize vectors for storing the data # TODO: generalize to weird lattice types # nonzero_keys = similar(lattice, Vector{NTuple{2,Int}}) # nonzero_opps = similar(lattice, Vector{Any}) - nonzero_keys = Vector{Vector{NTuple{2,Int}}}(undef, length(lattice)) + nonzero_keys = Vector{Vector{NTuple{2, Int}}}(undef, length(lattice)) nonzero_opps = Vector{Vector{Any}}(undef, length(lattice)) for i in eachindex(nonzero_keys) nonzero_keys[i] = [] @@ -363,15 +364,16 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, end # partial sort by interaction range - local_mpos = sort!(map(Base.Fix1(instantiate_operator, lattice), - collect(local_operators)); by=x -> length(x[1])) + local_mpos = sort!( + map(Base.Fix1(instantiate_operator, lattice), collect(local_operators)); + by = x -> length(x[1]) + ) for (sites, local_mpo) in local_mpos local key_R # trick to define key_R before the first iteration for (i, (site, O)) in enumerate(zip(sites, local_mpo)) key_L = i == 1 ? 1 : key_R - key_R = i == length(local_mpo) ? 0 : - _find_channel(nonzero_keys[site]; init=key_L) + key_R = i == length(local_mpo) ? 0 : _find_channel(nonzero_keys[site]; init = key_L) push!(nonzero_keys[site], (key_L, key_R)) push!(nonzero_opps[site], O) end @@ -387,7 +389,7 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, virtualsumspaces[end] = SumSpace(fill(oneunit(S), 1)) for i in 1:(length(lattice) - 1) - n_channels = maximum(last, nonzero_keys[i]; init=1) + 1 + n_channels = maximum(last, nonzero_keys[i]; init = 1) + 1 V = SumSpace(fill(oneunit(S), n_channels)) if n_channels > 2 for ((key_L, key_R), O) in zip(nonzero_keys[i], nonzero_opps[i]) @@ -426,14 +428,13 @@ function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, return FiniteMPOHamiltonian(Os) end -function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, - local_operators) +function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, local_operators) lattice = PeriodicVector(lattice′) # initialize vectors for storing the data # TODO: generalize to weird lattice types # nonzero_keys = similar(lattice, Vector{NTuple{2,Int}}) # nonzero_opps = similar(lattice, Vector{Any}) - nonzero_keys = PeriodicVector{Vector{NTuple{2,Int}}}(undef, length(lattice)) + nonzero_keys = PeriodicVector{Vector{NTuple{2, Int}}}(undef, length(lattice)) nonzero_opps = PeriodicVector{Vector{Any}}(undef, length(lattice)) for i in eachindex(nonzero_keys) nonzero_keys[i] = [] @@ -441,15 +442,16 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, end # partial sort by interaction range - local_mpos = sort!(map(Base.Fix1(instantiate_operator, lattice), - collect(local_operators)); by=x -> length(x[1])) + local_mpos = sort!( + map(Base.Fix1(instantiate_operator, lattice), collect(local_operators)); + by = x -> length(x[1]) + ) for (sites, local_mpo) in local_mpos local key_R # trick to define key_R before the first iteration for (i, (site, O)) in enumerate(zip(sites, local_mpo)) key_L = i == 1 ? 1 : key_R - key_R = i == length(local_mpo) ? 0 : - _find_channel(nonzero_keys[site]; init=key_L) + key_R = i == length(local_mpo) ? 0 : _find_channel(nonzero_keys[site]; init = key_L) push!(nonzero_keys[site], (key_L, key_R)) push!(nonzero_opps[site], O) end @@ -461,10 +463,11 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, S = spacetype(T) # construct the virtual spaces - MissingS = Union{Missing,S} - operator_size = maximum(K -> maximum(last, K; init=1) + 1, nonzero_keys) - virtualspaces = PeriodicArray([Vector{MissingS}(missing, operator_size) - for _ in 1:length(nonzero_keys)]) + MissingS = Union{Missing, S} + operator_size = maximum(K -> maximum(last, K; init = 1) + 1, nonzero_keys) + virtualspaces = PeriodicArray( + [Vector{MissingS}(missing, operator_size) for _ in 1:length(nonzero_keys)] + ) for V in virtualspaces V[1] = oneunit(S) V[end] = oneunit(S) @@ -500,12 +503,12 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, key_R = key_R′ == 0 ? operator_size : key_R′ if !ismissing(virtualspaces[i - 1][key_L]) && - ismissing(virtualspaces[i][key_R]) + ismissing(virtualspaces[i][key_R]) virtualspaces[i][key_R] = virtualspaces[i - 1][key_L] ischanged = true end if ismissing(virtualspaces[i - 1][key_L]) && - !ismissing(virtualspaces[i][key_R]) + !ismissing(virtualspaces[i][key_R]) virtualspaces[i - 1][key_L] = virtualspaces[i][key_R] ischanged = true end @@ -543,17 +546,15 @@ function InfiniteMPOHamiltonian(lattice′::AbstractArray{<:VectorSpace}, return InfiniteMPOHamiltonian(PeriodicArray(Os)) end -function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, - local_operators::Pair...) +function FiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_operators::Pair...) return FiniteMPOHamiltonian(lattice, local_operators) end -function InfiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, - local_operators::Pair...) +function InfiniteMPOHamiltonian(lattice::AbstractArray{<:VectorSpace}, local_operators::Pair...) return InfiniteMPOHamiltonian(lattice, local_operators) end -function InfiniteMPOHamiltonian(local_operator::TensorMap{E,S,N,N}) where {E,S,N} +function InfiniteMPOHamiltonian(local_operator::TensorMap{E, S, N, N}) where {E, S, N} lattice_space = space(local_operator, 1) n_sites = length(domain(local_operator)) lattice = PeriodicArray([lattice_space]) @@ -585,7 +586,7 @@ function isidentitylevel(H::InfiniteMPOHamiltonian{<:JordanMPOTensor}, i::Int) else return all(H.A) do A return haskey(A, CartesianIndex(i - 1, 1, 1, i - 1)) && - A[i - 1, 1, 1, i - 1] isa BraidingTensor + A[i - 1, 1, 1, i - 1] isa BraidingTensor end end end @@ -602,13 +603,15 @@ function Base.convert(::Type{TensorMap}, H::FiniteMPOHamiltonian) return convert(TensorMap, _instantiate_finitempo(L, M, R)) end -function Base.convert(::Type{FiniteMPOHamiltonian{O1}}, - H::FiniteMPOHamiltonian{O2}) where {O1,O2} +function Base.convert( + ::Type{FiniteMPOHamiltonian{O1}}, H::FiniteMPOHamiltonian{O2} + ) where {O1, O2} O1 === O2 && return H return FiniteMPOHamiltonian(convert.(O1, parent(H))) end -function Base.convert(::Type{InfiniteMPOHamiltonian{O1}}, - H::InfiniteMPOHamiltonian{O2}) where {O1,O2} +function Base.convert( + ::Type{InfiniteMPOHamiltonian{O1}}, H::InfiniteMPOHamiltonian{O2} + ) where {O1, O2} O1 === O2 && return H return InfiniteMPOHamiltonian(convert.(O1, parent(H))) end @@ -641,46 +644,49 @@ function Base.complex(H::MPOHamiltonian) return MPOHamiltonian(H) end -function Base.similar(H::MPOHamiltonian, ::Type{O}, L::Int) where {O<:MPOTensor} +function Base.similar(H::MPOHamiltonian, ::Type{O}, L::Int) where {O <: MPOTensor} return MPOHamiltonian(similar(parent(H), O, L)) end -function Base.similar(H::MPOHamiltonian, ::Type{T}) where {T<:Number} +function Base.similar(H::MPOHamiltonian, ::Type{T}) where {T <: Number} return MPOHamiltonian(similar.(parent(H), T)) end # Linear Algebra # -------------- -function Base.:+(H₁::FiniteMPOHamiltonian{O}, - H₂::FiniteMPOHamiltonian{O}) where {O<:JordanMPOTensor} +function Base.:+( + H₁::FiniteMPOHamiltonian{O}, H₂::FiniteMPOHamiltonian{O} + ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) Vtriv = oneunit(spacetype(H₁)) for i in 1:N - A = cat(H₁[i].A, H₂[i].A; dims=(1, 4)) - B = cat(H₁[i].B, H₂[i].B; dims=1) - C = cat(H₁[i].C, H₂[i].C; dims=3) + A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) + B = cat(H₁[i].B, H₂[i].B; dims = 1) + C = cat(H₁[i].C, H₂[i].C; dims = 3) D = H₁[i].D + H₂[i].D Vleft = i == 1 ? left_virtualspace(H₁, 1) : - BlockTensorKit.oplus(Vtriv, left_virtualspace(A), Vtriv) + BlockTensorKit.oplus(Vtriv, left_virtualspace(A), Vtriv) Vright = i == N ? right_virtualspace(H₁, N) : - BlockTensorKit.oplus(Vtriv, right_virtualspace(A), Vtriv) + BlockTensorKit.oplus(Vtriv, right_virtualspace(A), Vtriv) V = Vleft ⊗ physicalspace(A) ← physicalspace(A) ⊗ Vright H[i] = eltype(H)(V, A, B, C, D) end return FiniteMPOHamiltonian(H) end -function Base.:+(H₁::InfiniteMPOHamiltonian{O}, - H₂::InfiniteMPOHamiltonian{O}) where {O<:JordanMPOTensor} +function Base.:+( + H₁::InfiniteMPOHamiltonian{O}, + H₂::InfiniteMPOHamiltonian{O} + ) where {O <: JordanMPOTensor} N = check_length(H₁, H₂) H = similar(parent(H₁)) Vtriv = oneunit(spacetype(H₁)) for i in 1:N - A = cat(H₁[i].A, H₂[i].A; dims=(1, 4)) - B = cat(H₁[i].B, H₂[i].B; dims=1) - C = cat(H₁[i].C, H₂[i].C; dims=3) + A = cat(H₁[i].A, H₂[i].A; dims = (1, 4)) + B = cat(H₁[i].B, H₂[i].B; dims = 1) + C = cat(H₁[i].C, H₂[i].C; dims = 3) D = H₁[i].D + H₂[i].D Vleft = BlockTensorKit.oplus(Vtriv, left_virtualspace(A), Vtriv) @@ -696,16 +702,20 @@ function Base.:+(H::FiniteMPOHamiltonian, λs::AbstractVector{<:Number}) check_length(H, λs) lattice = [physicalspace(H, i) for i in 1:length(H)] M = storagetype(H) - Hλ = FiniteMPOHamiltonian(lattice, - i => scale!(id(M, lattice[i]), λs[i]) for i in 1:length(H)) + Hλ = FiniteMPOHamiltonian( + lattice, + i => scale!(id(M, lattice[i]), λs[i]) for i in 1:length(H) + ) return H + Hλ end function Base.:+(H::InfiniteMPOHamiltonian, λs::AbstractVector{<:Number}) check_length(H, λs) lattice = [physicalspace(H, i) for i in 1:length(H)] M = storagetype(H) - Hλ = InfiniteMPOHamiltonian(lattice, - i => scale!(id(M, lattice[i]), λs[i]) for i in 1:length(H)) + Hλ = InfiniteMPOHamiltonian( + lattice, + i => scale!(id(M, lattice[i]), λs[i]) for i in 1:length(H) + ) return H + Hλ end function Base.:+(λs::AbstractVector{<:Number}, H::MPOHamiltonian) @@ -716,16 +726,19 @@ Base.:-(H::MPOHamiltonian, λs::AbstractVector{<:Number}) = H + (-λs) Base.:-(λs::AbstractVector{<:Number}, H::MPOHamiltonian) = λs + (-H) Base.:-(H1::MPOHamiltonian, H2::MPOHamiltonian) = H1 + (-H2) -function VectorInterface.scale!(H::MPOHamiltonian{O}, - λ::Number) where {O<:JordanMPOTensor} +function VectorInterface.scale!( + H::MPOHamiltonian{O}, λ::Number + ) where {O <: JordanMPOTensor} for i in 1:length(H) scale!(H[i].C, λ) scale!(H[i].D, λ) end return H end -function VectorInterface.scale!(Hdst::MPOHamiltonian{<:JordanMPOTensor}, - Hsrc::MPOHamiltonian{<:JordanMPOTensor}, λ::Number) +function VectorInterface.scale!( + Hdst::MPOHamiltonian{<:JordanMPOTensor}, + Hsrc::MPOHamiltonian{<:JordanMPOTensor}, λ::Number + ) N = check_length(Hdst, Hsrc) for i in 1:N scale!(Hdst[i].C, Hsrc[i].C, λ) @@ -746,38 +759,40 @@ function Base.:*(H::FiniteMPOHamiltonian, mps::FiniteMPS) N = check_length(H, mps) @assert N > 2 "MPS should have at least three sites, to be implemented otherwise" A = convert.(BlockTensorMap, [mps.AC[1]; mps.AR[2:end]]) - A′ = similar(A, - tensormaptype(spacetype(mps), numout(eltype(mps)), numin(eltype(mps)), - promote_type(scalartype(H), scalartype(mps)))) + A′ = similar( + A, + tensormaptype( + spacetype(mps), numout(eltype(mps)), numin(eltype(mps)), + promote_type(scalartype(H), scalartype(mps)) + ) + ) # left to middle U = ones(scalartype(H), left_virtualspace(H, 1)) @plansor a[-1 -2; -3 -4] := A[1][-1 2; -3] * H[1][1 -2; 2 -4] * conj(U[1]) - Q, R = leftorth!(a; alg=QR()) + Q, R = leftorth!(a; alg = QR()) A′[1] = convert(TensorMap, Q) for i in 2:(N ÷ 2) @plansor a[-1 -2; -3 -4] := R[-1; 1 2] * A[i][1 3; -3] * H[i][2 -2; 3 -4] - Q, R = leftorth!(a; alg=QR()) + Q, R = leftorth!(a; alg = QR()) A′[i] = convert(TensorMap, Q) end # right to middle U = ones(scalartype(H), right_virtualspace(H, N)) @plansor a[-1 -2; -3 -4] := A[end][-1 2; -3] * H[end][-2 -4; 2 1] * U[1] - L, Q = rightorth!(a; alg=LQ()) + L, Q = rightorth!(a; alg = LQ()) A′[end] = transpose(convert(TensorMap, Q), ((1, 3), (2,))) for i in (N - 1):-1:(N ÷ 2 + 2) @plansor a[-1 -2; -3 -4] := A[i][-1 3; 1] * H[i][-2 -4; 3 2] * L[1 2; -3] - L, Q = rightorth!(a; alg=LQ()) + L, Q = rightorth!(a; alg = LQ()) A′[i] = transpose(convert(TensorMap, Q), ((1, 3), (2,))) end # connect pieces - @plansor a[-1 -2; -3] := R[-1; 1 2] * - A[N ÷ 2 + 1][1 3; 4] * - H[N ÷ 2 + 1][2 -2; 3 5] * - L[4 5; -3] + @plansor a[-1 -2; -3] := R[-1; 1 2] * A[N ÷ 2 + 1][1 3; 4] * H[N ÷ 2 + 1][2 -2; 3 5] * + L[4 5; -3] A′[N ÷ 2 + 1] = convert(TensorMap, a) return FiniteMPS(A′) @@ -799,29 +814,37 @@ function TensorKit.dot(H₁::FiniteMPOHamiltonian, H₂::FiniteMPOHamiltonian) @plansor ρ_left[-1; -2] := conj(H₁[1][1 2; 3 -1]) * H₂[1][1 2; 3 -2] for i in 2:Nhalf @plansor ρ_left[-1; -2] := ρ_left[1; 2] * conj(H₁[i][1 3; 4 -1]) * - H₂[i][2 3; 4 -2] + H₂[i][2 3; 4 -2] end # right half @plansor ρ_right[-1; -2] := conj(H₁[N][-2 1; 2 3]) * H₂[N][-1 1; 2 3] for i in (N - 1):-1:(Nhalf + 1) @plansor ρ_right[-1; -2] := ρ_right[1; 2] * conj(H₁[i][-2 4; 3 2]) * - H₂[i][-1 4; 3 1] + H₂[i][-1 4; 3 1] end return @plansor ρ_left[1; 2] * ρ_right[2; 1] end -function TensorKit.dot(bra::FiniteMPS, H::FiniteMPOHamiltonian, ket::FiniteMPS=bra, - envs=environments(bra, H, ket)) +function TensorKit.dot( + bra::FiniteMPS, H::FiniteMPOHamiltonian, ket::FiniteMPS = bra, + envs = environments(bra, H, ket) + ) @assert ket === bra "TBA" # find where environments had already been computed - N = something(findfirst(i -> bra.ARs[i] !== envs.rdependencies[i], 1:length(bra)), - length(bra) ÷ 2) - return contract_mpo_expval(ket.AC[N], leftenv(envs, N, bra), H[N], - rightenv(envs, N, bra), bra.AC[N]) -end - -function Base.isapprox(H₁::FiniteMPOHamiltonian, H₂::FiniteMPOHamiltonian; - atol::Real=0, rtol::Real=atol > 0 ? 0 : √eps(real(scalartype(H₁)))) + N = something( + findfirst(i -> bra.ARs[i] !== envs.rdependencies[i], 1:length(bra)), + length(bra) ÷ 2 + ) + return contract_mpo_expval( + ket.AC[N], leftenv(envs, N, bra), H[N], + rightenv(envs, N, bra), bra.AC[N] + ) +end + +function Base.isapprox( + H₁::FiniteMPOHamiltonian, H₂::FiniteMPOHamiltonian; + atol::Real = 0, rtol::Real = atol > 0 ? 0 : √eps(real(scalartype(H₁))) + ) check_length(H₁, H₂) # computing ||H₁ - H₂|| without constructing H₁ - H₂ diff --git a/src/operators/multipliedoperator.jl b/src/operators/multipliedoperator.jl index 9b1848196..1cb38d912 100644 --- a/src/operators/multipliedoperator.jl +++ b/src/operators/multipliedoperator.jl @@ -3,7 +3,7 @@ - An operator op (MPO, Hamiltonian, ...) - An object f that gets multiplied with the operator (Number, function, ...) """ -struct MultipliedOperator{O,F} +struct MultipliedOperator{O, F} op::O f::F end @@ -13,7 +13,7 @@ end - An operator op (MPO, Hamiltonian, ...) - An function f that gives the time-dependence according to op(t) = f(t)*op """ -const TimedOperator{O} = MultipliedOperator{O,<:Function} +const TimedOperator{O} = MultipliedOperator{O, <:Function} TimedOperator(x) = MultipliedOperator(x, t -> One()) """ @@ -21,7 +21,7 @@ TimedOperator(x) = MultipliedOperator(x, t -> One()) - An operator (MPO, Hamiltonian, ...) - A number f that gets multiplied with the operator """ -const UntimedOperator{O} = MultipliedOperator{O,<:Number} +const UntimedOperator{O} = MultipliedOperator{O, <:Number} UntimedOperator(x) = MultipliedOperator(x, One()) # Holy traits @@ -52,6 +52,6 @@ Base.:*(op::UntimedOperator, g::Function) = MultipliedOperator(op.op, t -> g(t) function environments(st, x::MultipliedOperator, args...; kwargs...) return environments(st, x.op, args...; kwargs...) end -function recalculate!(envs, below, x::MultipliedOperator, above=below; kwargs...) +function recalculate!(envs, below, x::MultipliedOperator, above = below; kwargs...) return recalculate!(envs, below, x.op, above; kwargs...) end diff --git a/src/operators/show.jl b/src/operators/show.jl index b3b74dff5..020ad895f 100644 --- a/src/operators/show.jl +++ b/src/operators/show.jl @@ -9,7 +9,7 @@ end Base.show(io::IO, mpo::AbstractMPO) = show(convert(IOContext, io), mpo) function Base.show(io::IOContext, mpo::AbstractMPO) - charset = (; top="┬", bot="┴", mid="┼", ver="│", dash="──") + charset = (; top = "┬", bot = "┴", mid = "┼", ver = "│", dash = "──") limit = get(io, :limit, false)::Bool half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) L = length(mpo) @@ -23,14 +23,20 @@ function Base.show(io::IOContext, mpo::AbstractMPO) for site in reverse(1:L) if site < half_screen_rows || site > L - half_screen_rows if site == L && isfinite - println(io, charset.top, " $mpoletter[$site]: ", - repeat(" ", npad - floor(Int, log10(site))), mpo[site]) + println( + io, charset.top, " $mpoletter[$site]: ", + repeat(" ", npad - floor(Int, log10(site))), mpo[site] + ) elseif (site == 1) && isfinite - println(io, charset.bot, " $mpoletter[$site]: ", - repeat(" ", npad - floor(Int, log10(site))), mpo[site]) + println( + io, charset.bot, " $mpoletter[$site]: ", + repeat(" ", npad - floor(Int, log10(site))), mpo[site] + ) else - println(io, charset.mid, " $mpoletter[$site]: ", - repeat(" ", npad - floor(Int, log10(site))), mpo[site]) + println( + io, charset.mid, " $mpoletter[$site]: ", + repeat(" ", npad - floor(Int, log10(site))), mpo[site] + ) end elseif site == half_screen_rows println(io, " ", "⋮") @@ -56,7 +62,7 @@ This visualization is useful for quickly inspecting the structure and sparsity p If called without an `io` argument, output is printed to `stdout`. """ -function braille(io::IO, H::Union{SparseMPO,MPOHamiltonian}) +function braille(io::IO, H::Union{SparseMPO, MPOHamiltonian}) dash = "🭻" stride = 2 #amount of dashes between braille L = length(H) @@ -75,8 +81,9 @@ function braille(io::IO, H::Union{SparseMPO,MPOHamiltonian}) line *= ((i == 1 && !isfinite(H)) ? ("... " * dash) : " ") line *= (i > 1 && !isfinite(H)) ? " " : "" for (j, braille) in enumerate(brailles) - line *= (checkbounds(Bool, braille, i) ? braille[i] : - repeat(" ", length(braille[1]))) + line *= ( + checkbounds(Bool, braille, i) ? braille[i] : repeat(" ", length(braille[1])) + ) if j < L line *= repeat(((i == 1) ? dash : " "), stride) end @@ -87,4 +94,4 @@ function braille(io::IO, H::Union{SparseMPO,MPOHamiltonian}) return nothing end -braille(H::Union{SparseMPO,MPOHamiltonian}) = braille(stdout, H) +braille(H::Union{SparseMPO, MPOHamiltonian}) = braille(stdout, H) diff --git a/src/states/abstractmps.jl b/src/states/abstractmps.jl index 88d57404e..87500ba28 100644 --- a/src/states/abstractmps.jl +++ b/src/states/abstractmps.jl @@ -8,10 +8,10 @@ Tensor types Tensor type for representing local MPO tensors, with the index convention `W ⊗ S ← N ⊗ E`, where `N`, `E`, `S` and `W` denote the north, east, south and west virtual spaces respectively. """ -const MPOTensor{S} = AbstractTensorMap{T,S,2,2} where {T} -const MPSBondTensor{S} = AbstractTensorMap{T,S,1,1} where {T} -const GenericMPSTensor{S,N} = AbstractTensorMap{T,S,N,1} where {T} # some functions are also defined for "general mps tensors" (used in peps code) -const MPSTensor{S} = GenericMPSTensor{S,2} # the usual mps tensors on which we work +const MPOTensor{S} = AbstractTensorMap{T, S, 2, 2} where {T} +const MPSBondTensor{S} = AbstractTensorMap{T, S, 1, 1} where {T} +const GenericMPSTensor{S, N} = AbstractTensorMap{T, S, N, 1} where {T} # some functions are also defined for "general mps tensors" (used in peps code) +const MPSTensor{S} = GenericMPSTensor{S, 2} # the usual mps tensors on which we work """ MPSTensor([f, eltype], d::Int, left_D::Int, [right_D]::Int]) @@ -32,12 +32,14 @@ Construct an `MPSTensor` with given physical and virtual spaces. - `left_D::Int`: left virtual dimension - `right_D::Int`: right virtual dimension """ -function MPSTensor(::UndefInitializer, eltype, P::Union{S,CompositeSpace{S}}, Vₗ::S, - Vᵣ::S=Vₗ) where {S<:ElementarySpace} +function MPSTensor( + ::UndefInitializer, eltype, P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ + ) where {S <: ElementarySpace} return TensorMap{eltype}(undef, Vₗ ⊗ P ← Vᵣ) end -function MPSTensor(f, eltype, P::Union{S,CompositeSpace{S}}, Vₗ::S, - Vᵣ::S=Vₗ) where {S<:ElementarySpace} +function MPSTensor( + f, eltype, P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ + ) where {S <: ElementarySpace} A = MPSTensor(undef, eltype, P, Vₗ, Vᵣ) if f === rand return rand!(A) @@ -50,8 +52,9 @@ function MPSTensor(f, eltype, P::Union{S,CompositeSpace{S}}, Vₗ::S, end end # TODO: reinstate function initializers? -function MPSTensor(P::Union{S,CompositeSpace{S}}, Vₗ::S, - Vᵣ::S=Vₗ) where {S<:ElementarySpace} +function MPSTensor( + P::Union{S, CompositeSpace{S}}, Vₗ::S, Vᵣ::S = Vₗ + ) where {S <: ElementarySpace} return MPSTensor(rand, Defaults.eltype, P, Vₗ, Vᵣ) end @@ -67,15 +70,15 @@ Construct an `MPSTensor` with given physical and virtual dimensions. - `Dₗ::Int`: left virtual dimension - `Dᵣ::Int`: right virtual dimension """ -MPSTensor(f, eltype, d::Int, Dₗ::Int, Dᵣ::Int=Dₗ) = MPSTensor(f, eltype, ℂ^d, ℂ^Dₗ, ℂ^Dᵣ) -MPSTensor(d::Int, Dₗ::Int; Dᵣ::Int=Dₗ) = MPSTensor(ℂ^d, ℂ^Dₗ, ℂ^Dᵣ) +MPSTensor(f, eltype, d::Int, Dₗ::Int, Dᵣ::Int = Dₗ) = MPSTensor(f, eltype, ℂ^d, ℂ^Dₗ, ℂ^Dᵣ) +MPSTensor(d::Int, Dₗ::Int; Dᵣ::Int = Dₗ) = MPSTensor(ℂ^d, ℂ^Dₗ, ℂ^Dᵣ) """ MPSTensor(A::AbstractArray) Convert an array to an `MPSTensor`. """ -function MPSTensor(A::AbstractArray{T}) where {T<:Number} +function MPSTensor(A::AbstractArray{T}) where {T <: Number} @assert ndims(A) > 2 "MPSTensor should have at least 3 dims, but has $ndims(A)" sz = size(A) t = TensorMap(undef, T, foldl(⊗, ComplexSpace.(sz[1:(end - 1)])) ← ℂ^sz[end]) @@ -90,7 +93,7 @@ Determine whether the given tensor is full rank, i.e. whether both the map from virtual space and the physical space to the right virtual space, and the map from the right virtual space and the physical space to the left virtual space are injective. """ -function isfullrank(A::GenericMPSTensor; side=:both) +function isfullrank(A::GenericMPSTensor; side = :both) Vₗ = _firstspace(A) Vᵣ = _lastspace(A) P = ⊗(space.(Ref(A), 2:(numind(A) - 1))...) @@ -110,12 +113,12 @@ end Make the set of MPS tensors full rank by performing a series of orthogonalizations. """ -function makefullrank!(A::PeriodicVector{<:GenericMPSTensor}; alg=QRpos()) +function makefullrank!(A::PeriodicVector{<:GenericMPSTensor}; alg = QRpos()) while true i = findfirst(!isfullrank, A) isnothing(i) && break - if !isfullrank(A[i]; side=:left) - L, Q = rightorth!(_transpose_tail(A[i]); alg=alg') + if !isfullrank(A[i]; side = :left) + L, Q = rightorth!(_transpose_tail(A[i]); alg = alg') A[i] = _transpose_front(Q) A[i - 1] = A[i - 1] * L else @@ -207,7 +210,7 @@ function physicalspace end physicalspace(A::MPSTensor) = space(A, 2) physicalspace(A::GenericMPSTensor) = prod(x -> space(A, x), 2:(numind(A) - 1)) physicalspace(O::MPOTensor) = space(O, 2) -physicalspace(O::AbstractBlockTensorMap{<:Any,<:Any,2,2}) = only(space(O, 2)) +physicalspace(O::AbstractBlockTensorMap{<:Any, <:Any, 2, 2}) = only(space(O, 2)) """ eachsite(state::AbstractMPS) diff --git a/src/states/finitemps.jl b/src/states/finitemps.jl index b0eef3bcb..aad998e06 100644 --- a/src/states/finitemps.jl +++ b/src/states/finitemps.jl @@ -57,21 +57,22 @@ total charge can be constructed by passing a non-trivially charged vector space - `left=oneunit(S)`: left-most virtual space - `right=oneunit(S)`: right-most virtual space """ -struct FiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS - ALs::Vector{Union{Missing,A}} - ARs::Vector{Union{Missing,A}} - ACs::Vector{Union{Missing,A}} - Cs::Vector{Union{Missing,B}} - function FiniteMPS{A,B}(ALs::Vector{Union{Missing,A}}, ARs::Vector{Union{Missing,A}}, - ACs::Vector{Union{Missing,A}}, - Cs::Vector{Union{Missing,B}}) where {A<:GenericMPSTensor, - B<:MPSBondTensor} - return new{A,B}(ALs, ARs, ACs, Cs) +struct FiniteMPS{A <: GenericMPSTensor, B <: MPSBondTensor} <: AbstractFiniteMPS + ALs::Vector{Union{Missing, A}} + ARs::Vector{Union{Missing, A}} + ACs::Vector{Union{Missing, A}} + Cs::Vector{Union{Missing, B}} + function FiniteMPS{A, B}( + ALs::Vector{Union{Missing, A}}, ARs::Vector{Union{Missing, A}}, + ACs::Vector{Union{Missing, A}}, Cs::Vector{Union{Missing, B}} + ) where {A <: GenericMPSTensor, B <: MPSBondTensor} + return new{A, B}(ALs, ARs, ACs, Cs) end - function FiniteMPS(ALs::Vector{MA}, ARs::Vector{MA}, - ACs::Vector{MA}, - Cs::Vector{MB}) where {MA<:Union{GenericMPSTensor,Missing}, - MB<:Union{MPSBondTensor,Missing}} + function FiniteMPS( + ALs::Vector{MA}, ARs::Vector{MA}, + ACs::Vector{MA}, + Cs::Vector{MB} + ) where {MA <: Union{GenericMPSTensor, Missing}, MB <: Union{MPSBondTensor, Missing}} A = _not_missing_type(MA) B = _not_missing_type(MB) length(ACs) == length(Cs) - 1 == length(ALs) == length(ARs) || @@ -80,8 +81,8 @@ struct FiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS throw(ArgumentError("at least one AC/C should not be missing")) S = spacetype(A) - left_virt_spaces = Vector{Union{Missing,S}}(missing, length(Cs)) - right_virt_spaces = Vector{Union{Missing,S}}(missing, length(Cs)) + left_virt_spaces = Vector{Union{Missing, S}}(missing, length(Cs)) + right_virt_spaces = Vector{Union{Missing, S}}(missing, length(Cs)) for (i, tup) in enumerate(zip(ALs, ARs, ACs)) non_missing = filter(!ismissing, tup) @@ -90,8 +91,10 @@ struct FiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS if !ismissing(al) !ismissing(left_virt_spaces[i]) && - (left_virt_spaces[i] == _firstspace(al) || - throw(SpaceMismatch("Virtual space of AL on site $(i) doesn't match"))) + ( + left_virt_spaces[i] == _firstspace(al) || + throw(SpaceMismatch("Virtual space of AL on site $(i) doesn't match")) + ) left_virt_spaces[i + 1] = _lastspace(al)' left_virt_spaces[i] = _firstspace(al) @@ -99,8 +102,10 @@ struct FiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS if !ismissing(ar) !ismissing(right_virt_spaces[i]) && - (right_virt_spaces[i] == _firstspace(ar) || - throw(SpaceMismatch("Virtual space of AR on site $(i) doesn't match"))) + ( + right_virt_spaces[i] == _firstspace(ar) || + throw(SpaceMismatch("Virtual space of AR on site $(i) doesn't match")) + ) right_virt_spaces[i + 1] = _lastspace(ar)' right_virt_spaces[i] = _firstspace(ar) @@ -108,11 +113,15 @@ struct FiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS if !ismissing(ac) !ismissing(left_virt_spaces[i]) && - (left_virt_spaces[i] == _firstspace(ac) || - throw(SpaceMismatch("Left virtual space of AC on site $(i) doesn't match"))) + ( + left_virt_spaces[i] == _firstspace(ac) || + throw(SpaceMismatch("Left virtual space of AC on site $(i) doesn't match")) + ) !ismissing(right_virt_spaces[i + 1]) && - (right_virt_spaces[i + 1] == _lastspace(ac)' || - throw(SpaceMismatch("Right virtual space of AC on site $(i) doesn't match"))) + ( + right_virt_spaces[i + 1] == _lastspace(ac)' || + throw(SpaceMismatch("Right virtual space of AC on site $(i) doesn't match")) + ) right_virt_spaces[i + 1] = _lastspace(ac)' left_virt_spaces[i] = _firstspace(ac) @@ -121,12 +130,16 @@ struct FiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS for (i, c) in enumerate(Cs) ismissing(c) && continue - !ismissing(left_virt_spaces[i]) && (left_virt_spaces[i] == _firstspace(c) || - throw(SpaceMismatch("Left virtual space of C on site $(i-1) doesn't match"))) - !ismissing(right_virt_spaces[i]) && (right_virt_spaces[i] == _lastspace(c)' || - throw(SpaceMismatch("Right virtual space of C on site $(i-1) doesn't match"))) + !ismissing(left_virt_spaces[i]) && ( + left_virt_spaces[i] == _firstspace(c) || + throw(SpaceMismatch("Left virtual space of C on site $(i - 1) doesn't match")) + ) + !ismissing(right_virt_spaces[i]) && ( + right_virt_spaces[i] == _lastspace(c)' || + throw(SpaceMismatch("Right virtual space of C on site $(i - 1) doesn't match")) + ) end - return new{A,B}(ALs, ARs, ACs, Cs) + return new{A, B}(ALs, ARs, ACs, Cs) end end @@ -134,7 +147,7 @@ _not_missing_type(::Type{Missing}) = throw(ArgumentError("Only missing type pres function _not_missing_type(::Type{T}) where {T} if T isa Union return (!(T.a === Missing) && !(T.b === Missing)) ? T : - !(T.a === Missing) ? _not_missing_type(T.a) : _not_missing_type(T.b) + !(T.a === Missing) ? _not_missing_type(T.a) : _not_missing_type(T.b) else return T end @@ -193,27 +206,27 @@ end Constructors ===========================================================================================# -function FiniteMPS(As::Vector{<:GenericMPSTensor}; normalize=false, overwrite=false) - # TODO: copying the input vector is probably not necessary, as we are constructing new +function FiniteMPS(As::Vector{<:GenericMPSTensor}; normalize = false, overwrite = false) + # TODO: copying the input vector is probably not necessary, as we are constructing new # vectors anyways, maybe deprecate `overwrite`. As = overwrite ? As : copy(As) N = length(As) for i in 1:(N - 1) - As[i], C = leftorth(As[i]; alg=QRpos()) + As[i], C = leftorth(As[i]; alg = QRpos()) normalize && normalize!(C) As[i + 1] = _transpose_front(C * _transpose_tail(As[i + 1])) end - As[end], C = leftorth(As[end]; alg=QRpos()) + As[end], C = leftorth(As[end]; alg = QRpos()) normalize && normalize!(C) A = eltype(As) B = typeof(C) - Cs = Vector{Union{Missing,B}}(missing, N + 1) - ALs = Vector{Union{Missing,A}}(missing, N) - ARs = Vector{Union{Missing,A}}(missing, N) - ACs = Vector{Union{Missing,A}}(missing, N) + Cs = Vector{Union{Missing, B}}(missing, N + 1) + ALs = Vector{Union{Missing, A}}(missing, N) + ARs = Vector{Union{Missing, A}}(missing, N) + ACs = Vector{Union{Missing, A}}(missing, N) ALs .= As Cs[end] = C @@ -221,9 +234,10 @@ function FiniteMPS(As::Vector{<:GenericMPSTensor}; normalize=false, overwrite=fa return FiniteMPS(ALs, ARs, ACs, Cs) end -function FiniteMPS(f, elt, Pspaces::Vector{<:Union{S,CompositeSpace{S}}}, - maxVspaces::Vector{S}; normalize=true, left::S=oneunit(S), - right::S=oneunit(S)) where {S<:ElementarySpace} +function FiniteMPS( + f, elt, Pspaces::Vector{<:Union{S, CompositeSpace{S}}}, maxVspaces::Vector{S}; + normalize = true, left::S = oneunit(S), right::S = oneunit(S) + ) where {S <: ElementarySpace} N = length(Pspaces) length(maxVspaces) == N - 1 || throw(DimensionMismatch("length of physical spaces ($N) and virtual spaces $(length(maxVspaces)) should differ by 1")) @@ -246,16 +260,19 @@ function FiniteMPS(f, elt, Pspaces::Vector{<:Union{S,CompositeSpace{S}}}, # construct MPS tensors = MPSTensor.(f, elt, Pspaces, Vspaces[1:(end - 1)], Vspaces[2:end]) - return FiniteMPS(tensors; normalize, overwrite=true) + return FiniteMPS(tensors; normalize, overwrite = true) end -function FiniteMPS(f, elt, Pspaces::Vector{<:Union{S,CompositeSpace{S}}}, - maxVspace::S; kwargs...) where {S<:ElementarySpace} +function FiniteMPS( + f, elt, Pspaces::Vector{<:Union{S, CompositeSpace{S}}}, maxVspace::S; + kwargs... + ) where {S <: ElementarySpace} maxVspaces = fill(maxVspace, length(Pspaces) - 1) return FiniteMPS(f, elt, Pspaces, maxVspaces; kwargs...) end -function FiniteMPS(Pspaces::Vector{<:Union{S,CompositeSpace{S}}}, - maxVspaces::Union{S,Vector{S}}; - kwargs...) where {S<:ElementarySpace} +function FiniteMPS( + Pspaces::Vector{<:Union{S, CompositeSpace{S}}}, maxVspaces::Union{S, Vector{S}}; + kwargs... + ) where {S <: ElementarySpace} return FiniteMPS(rand, Defaults.eltype, Pspaces, maxVspaces; kwargs...) end @@ -277,9 +294,10 @@ end # TODO: make planar? function FiniteMPS(ψ::AbstractTensor) U = ones(scalartype(ψ), oneunit(spacetype(ψ))) - A = _transpose_front(U * - transpose(ψ * U', ((), reverse(ntuple(identity, numind(ψ) + 1))))) - return FiniteMPS(decompose_localmps(A); normalize=false, overwrite=true) + A = _transpose_front( + U * transpose(ψ * U', ((), reverse(ntuple(identity, numind(ψ) + 1)))) + ) + return FiniteMPS(decompose_localmps(A); normalize = false, overwrite = true) end #=========================================================================================== @@ -290,8 +308,8 @@ Base.size(ψ::FiniteMPS, args...) = size(ψ.ALs, args...) Base.length(ψ::FiniteMPS) = length(ψ.ALs) Base.eltype(ψtype::Type{<:FiniteMPS}) = site_type(ψtype) # this might not be true Base.copy(ψ::FiniteMPS) = FiniteMPS(copy(ψ.ALs), copy(ψ.ARs), copy(ψ.ACs), copy(ψ.Cs)) -function Base.similar(ψ::FiniteMPS{A,B}) where {A,B} - return FiniteMPS{A,B}(similar(ψ.ALs), similar(ψ.ARs), similar(ψ.ACs), similar(ψ.Cs)) +function Base.similar(ψ::FiniteMPS{A, B}) where {A, B} + return FiniteMPS{A, B}(similar(ψ.ALs), similar(ψ.ARs), similar(ψ.ACs), similar(ψ.Cs)) end Base.eachindex(ψ::FiniteMPS) = eachindex(ψ.AL) @@ -301,9 +319,7 @@ Base.checkbounds(::Type{Bool}, ψ::FiniteMPS, i::Integer) = 1 <= i <= length(ψ) Base.@propagate_inbounds function Base.getindex(ψ::FiniteMPS, i::Int) c = ψ.center - @boundscheck begin - checkbounds(ψ, i) - end + @boundscheck checkbounds(ψ, i) if ishalfodd(c) c -= 1 / 2 @@ -330,10 +346,12 @@ function Base.complex(mps::FiniteMPS) ACs = _complex_if_not_missing.(mps.ACs) TA = Base.promote_op(complex, site_type(mps)) TB = Base.promote_op(complex, bond_type(mps)) - return FiniteMPS(collect(Union{Missing,TA}, ALs), - collect(Union{Missing,TA}, ARs), - collect(Union{Missing,TA}, ACs), - collect(Union{Missing,TB}, Cs)) + return FiniteMPS( + collect(Union{Missing, TA}, ALs), + collect(Union{Missing, TA}, ARs), + collect(Union{Missing, TA}, ACs), + collect(Union{Missing, TB}, Cs) + ) end @inline function Base.getindex(ψ::FiniteMPS, I::AbstractUnitRange) @@ -341,7 +359,7 @@ end end function Base.convert(::Type{TensorMap}, ψ::FiniteMPS) - T = foldl(ψ.AR[2:end]; init=first(ψ.AC)) do x, y + T = foldl(ψ.AR[2:end]; init = first(ψ.AC)) do x, y return _transpose_front(x * _transpose_tail(y)) end @@ -350,33 +368,34 @@ function Base.convert(::Type{TensorMap}, ψ::FiniteMPS) space(T, numind(T)) == oneunit(spacetype(T))' || throw(ArgumentError("utility leg not trivial")) U = ones(scalartype(ψ), oneunit(spacetype(ψ))) - UTU = transpose(U' * _transpose_tail(T * U), - (reverse(ntuple(identity, numind(T) - 2)), ())) + UTU = transpose( + U' * _transpose_tail(T * U), (reverse(ntuple(identity, numind(T) - 2)), ()) + ) return UTU end site_type(::Type{<:FiniteMPS{A}}) where {A} = A -bond_type(::Type{<:FiniteMPS{<:Any,B}}) where {B} = B -function TensorKit.storagetype(::Union{MPS,Type{MPS}}) where {A,MPS<:FiniteMPS{A}} +bond_type(::Type{<:FiniteMPS{<:Any, B}}) where {B} = B +function TensorKit.storagetype(::Union{MPS, Type{MPS}}) where {A, MPS <: FiniteMPS{A}} return storagetype(A) end function left_virtualspace(ψ::FiniteMPS, n::Integer) checkbounds(ψ, n) return !ismissing(ψ.ALs[n]) ? left_virtualspace(ψ.ALs[n]) : - !ismissing(ψ.ARs[n]) ? left_virtualspace(ψ.ARs[n]) : - dual(_lastspace(ψ.C[n - 1])) + !ismissing(ψ.ARs[n]) ? left_virtualspace(ψ.ARs[n]) : + dual(_lastspace(ψ.C[n - 1])) end function right_virtualspace(ψ::FiniteMPS, n::Integer) checkbounds(ψ, n) return !ismissing(ψ.ARs[n]) ? right_virtualspace(ψ.ARs[n]) : - !ismissing(ψ.ALs[n]) ? right_virtualspace(ψ.ALs[n]) : - _firstspace(ψ.C[n]) + !ismissing(ψ.ALs[n]) ? right_virtualspace(ψ.ALs[n]) : + _firstspace(ψ.C[n]) end physicalspace(ψ::FiniteMPS) = physicalspace.(Ref(ψ), 1:length(ψ)) -function physicalspace(ψ::FiniteMPS{<:GenericMPSTensor{<:Any,N}}, n::Integer) where {N} +function physicalspace(ψ::FiniteMPS{<:GenericMPSTensor{<:Any, N}}, n::Integer) where {N} N == 1 && return ProductSpace{spacetype(ψ)}() return physicalspace(coalesce(ψ.ALs[n], ψ.ARs[n], ψ.ACs[n])) end @@ -394,8 +413,9 @@ end Compute the maximal virtual spaces of a given finite MPS or its physical spaces. """ -function max_virtualspaces(Ps::Vector{<:Union{S,CompositeSpace{S}}}; left=oneunit(S), - right=oneunit(S)) where {S<:ElementarySpace} +function max_virtualspaces( + Ps::Vector{<:Union{S, CompositeSpace{S}}}; left = oneunit(S), right = oneunit(S) + ) where {S <: ElementarySpace} Vs = similar(Ps, length(Ps) + 1) Vs[1] = left Vs[end] = right @@ -408,8 +428,10 @@ function max_virtualspaces(Ps::Vector{<:Union{S,CompositeSpace{S}}}; left=oneuni return Vs end function max_virtualspaces(ψ::FiniteMPS) - return max_virtualspaces(physicalspace(ψ); left=left_virtualspace(ψ, 1), - right=right_virtualspace(ψ, length(ψ))) + return max_virtualspaces( + physicalspace(ψ); + left = left_virtualspace(ψ, 1), right = right_virtualspace(ψ, length(ψ)) + ) end """ @@ -429,7 +451,7 @@ function Base.show(io::IO, ::MIME"text/plain", ψ::FiniteMPS) end Base.show(io::IO, ψ::FiniteMPS) = show(convert(IOContext, io), ψ) function Base.show(io::IOContext, ψ::FiniteMPS) - charset = (; start="┌", mid="├", stop="└", ver="│", dash="──") + charset = (; start = "┌", mid = "├", stop = "└", ver = "│", dash = "──") limit = get(io, :limit, false)::Bool half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) if !haskey(io, :compact) @@ -443,31 +465,39 @@ function Base.show(io::IOContext, ψ::FiniteMPS) if site < half_screen_rows || site > L - half_screen_rows if site > c # ARs if isinteger(site) - println(io, Int(site) == L ? charset.start : charset.mid, charset.dash, - " AR[$(Int(site))]: ", ψ.ARs[Int(site)]) + println( + io, Int(site) == L ? charset.start : charset.mid, charset.dash, + " AR[$(Int(site))]: ", ψ.ARs[Int(site)] + ) end elseif site == c # AC or C if isinteger(c) # center is an AC - println(io, if site == L - charset.start - elseif site == 1 - charset.stop - else - charset.mid - end, charset.dash, " AC[$(Int(site))]: ", ψ.ACs[Int(site)]) + println( + io, if site == L + charset.start + elseif site == 1 + charset.stop + else + charset.mid + end, charset.dash, " AC[$(Int(site))]: ", ψ.ACs[Int(site)] + ) else # center is a bond-tensor - println(io, if site == HalfInt(L + 1 / 2) - charset.start - elseif site == HalfInt(1 / 2) - charset.stop - else - charset.ver - end, " C[$(Int(site-1/2))]: ", ψ.Cs[Int(site + 1 / 2)]) + println( + io, if site == HalfInt(L + 1 / 2) + charset.start + elseif site == HalfInt(1 / 2) + charset.stop + else + charset.ver + end, " C[$(Int(site - 1 / 2))]: ", ψ.Cs[Int(site + 1 / 2)] + ) end else if isinteger(site) - println(io, site == 1 ? charset.stop : charset.mid, charset.dash, - " AL[$(Int(site))]: ", ψ.ALs[Int(site)]) + println( + io, site == 1 ? charset.stop : charset.mid, charset.dash, + " AL[$(Int(site))]: ", ψ.ALs[Int(site)] + ) end end elseif site == half_screen_rows @@ -487,7 +517,7 @@ No support yet for converting the scalar type, also no in-place operations Base.:*(ψ::FiniteMPS, a::Number) = rmul!(copy(ψ), a) Base.:*(a::Number, ψ::FiniteMPS) = lmul!(a, copy(ψ)) -function Base.:+(ψ₁::MPS, ψ₂::MPS) where {MPS<:FiniteMPS} +function Base.:+(ψ₁::MPS, ψ₂::MPS) where {MPS <: FiniteMPS} length(ψ₁) == length(ψ₂) || throw(DimensionMismatch("Cannot add states of length $(length(ψ₁)) and $(length(ψ₂))")) @assert length(ψ₁) > 1 "not implemented for length < 2" @@ -501,8 +531,9 @@ function Base.:+(ψ₁::MPS, ψ₂::MPS) where {MPS<:FiniteMPS} halfN = div(length(ψ), 2) # left half - F₁ = isometry(storagetype(ψ), (_lastspace(ψ₁.AL[1]) ⊕ _lastspace(ψ₂.AL[1]))', - _lastspace(ψ₁.AL[1])') + F₁ = isometry( + storagetype(ψ), (_lastspace(ψ₁.AL[1]) ⊕ _lastspace(ψ₂.AL[1]))', _lastspace(ψ₁.AL[1])' + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == _lastspace(ψ₂.AL[1]) @@ -513,8 +544,9 @@ function Base.:+(ψ₁::MPS, ψ₂::MPS) where {MPS<:FiniteMPS} AL₁ = _transpose_front(F₁ * _transpose_tail(ψ₁.AL[i])) AL₂ = _transpose_front(F₂ * _transpose_tail(ψ₂.AL[i])) - F₁ = isometry(storagetype(ψ), (_lastspace(AL₁) ⊕ _lastspace(ψ₂.AL[i]))', - _lastspace(AL₁)') + F₁ = isometry( + storagetype(ψ), (_lastspace(AL₁) ⊕ _lastspace(ψ₂.AL[i]))', _lastspace(AL₁)' + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == _lastspace(ψ₂.AL[i]) @@ -526,8 +558,9 @@ function Base.:+(ψ₁::MPS, ψ₂::MPS) where {MPS<:FiniteMPS} C₂ = F₂ * ψ₂.C[halfN] # right half - F₁ = isometry(storagetype(ψ), _firstspace(ψ₁.AR[end]) ⊕ _firstspace(ψ₂.AR[end]), - _firstspace(ψ₁.AR[end])) + F₁ = isometry( + storagetype(ψ), _firstspace(ψ₁.AR[end]) ⊕ _firstspace(ψ₂.AR[end]), _firstspace(ψ₁.AR[end]) + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == _firstspace(ψ₂.AR[end])' @@ -539,8 +572,9 @@ function Base.:+(ψ₁::MPS, ψ₂::MPS) where {MPS<:FiniteMPS} AR₁ = _transpose_tail(ψ₁.AR[i] * F₁') AR₂ = _transpose_tail(ψ₂.AR[i] * F₂') - F₁ = isometry(storagetype(ψ), _firstspace(ψ₁.AR[i]) ⊕ _firstspace(AR₂), - _firstspace(ψ₁.AR[i])) + F₁ = isometry( + storagetype(ψ), _firstspace(ψ₁.AR[i]) ⊕ _firstspace(AR₂), _firstspace(ψ₁.AR[i]) + ) F₂ = leftnull(F₁) @assert _lastspace(F₂) == _firstspace(AR₂)' diff --git a/src/states/infinitemps.jl b/src/states/infinitemps.jl index af3820384..fe192d80e 100644 --- a/src/states/infinitemps.jl +++ b/src/states/infinitemps.jl @@ -43,16 +43,15 @@ tensors `As`, or a list of left-gauged tensors `ALs`. - `tol`: gauge fixing tolerance - `maxiter`: gauge fixing maximum iterations """ -struct InfiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractMPS +struct InfiniteMPS{A <: GenericMPSTensor, B <: MPSBondTensor} <: AbstractMPS AL::PeriodicVector{A} AR::PeriodicVector{A} C::PeriodicVector{B} AC::PeriodicVector{A} - function InfiniteMPS{A,B}(AL::PeriodicVector{A}, - AR::PeriodicVector{A}, - C::PeriodicVector{B}, - AC::PeriodicVector{A}=AL .* C) where {A<:GenericMPSTensor, - B<:MPSBondTensor} + function InfiniteMPS{A, B}( + AL::PeriodicVector{A}, AR::PeriodicVector{A}, + C::PeriodicVector{B}, AC::PeriodicVector{A} = AL .* C + ) where {A <: GenericMPSTensor, B <: MPSBondTensor} # verify lengths are compatible L = length(AL) L == length(AR) == length(C) == length(AC) || @@ -61,13 +60,12 @@ struct InfiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractMPS spacetype(A) == spacetype(B) || throw(SpaceMismatch("incompatible space types of AL and C")) - return new{A,B}(AL, AR, C, AC) + return new{A, B}(AL, AR, C, AC) end - function InfiniteMPS(AL::PeriodicVector{A}, - AR::PeriodicVector{A}, - C::PeriodicVector{B}, - AC::PeriodicVector{A}=AL .* C) where {A<:GenericMPSTensor, - B<:MPSBondTensor} + function InfiniteMPS( + AL::PeriodicVector{A}, AR::PeriodicVector{A}, + C::PeriodicVector{B}, AC::PeriodicVector{A} = AL .* C + ) where {A <: GenericMPSTensor, B <: MPSBondTensor} # verify lengths are compatible L = length(AL) L == length(AR) == length(C) == length(AC) || @@ -83,8 +81,10 @@ struct InfiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractMPS # verify that the physical spaces are compatible phys_ind = 2:(N - 1) - all(space.(Ref(AL[i]), phys_ind) .== space.(Ref(AR[i]), phys_ind) .== - space.(Ref(AC[i]), phys_ind)) || + all( + space.(Ref(AL[i]), phys_ind) .== space.(Ref(AR[i]), phys_ind) .== + space.(Ref(AC[i]), phys_ind) + ) || throw(SpaceMismatch("incompatible physical spaces at site $i")) # verify that the virtual spaces are compatible @@ -99,7 +99,7 @@ struct InfiniteMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractMPS dim(space(AL[i])) > 0 && dim(space(C[i])) > 0 || @warn "no fusion channels available at site $i" end - return new{A,B}(AL, AR, C, AC) + return new{A, B}(AL, AR, C, AC) end end @@ -107,32 +107,43 @@ end Constructors ===========================================================================================# -function InfiniteMPS(AL::AbstractVector{A}, AR::AbstractVector{A}, C::AbstractVector{B}, - AC::AbstractVector{A}=AL .* C) where {A<:GenericMPSTensor, - B<:MPSBondTensor} - return InfiniteMPS(convert(PeriodicVector{A}, AL), convert(PeriodicVector{A}, AR), - convert(PeriodicVector{B}, C), convert(PeriodicVector{A}, AC)) +function InfiniteMPS( + AL::AbstractVector{A}, AR::AbstractVector{A}, C::AbstractVector{B}, + AC::AbstractVector{A} = AL .* C + ) where {A <: GenericMPSTensor, B <: MPSBondTensor} + return InfiniteMPS( + convert(PeriodicVector{A}, AL), convert(PeriodicVector{A}, AR), + convert(PeriodicVector{B}, C), convert(PeriodicVector{A}, AC) + ) end -function InfiniteMPS(pspaces::AbstractVector{S}, Dspaces::AbstractVector{S}; - kwargs...) where {S<:IndexSpace} +function InfiniteMPS( + pspaces::AbstractVector{S}, Dspaces::AbstractVector{S}; + kwargs... + ) where {S <: IndexSpace} return InfiniteMPS(MPSTensor.(pspaces, circshift(Dspaces, 1), Dspaces); kwargs...) end -function InfiniteMPS(f, elt::Type{<:Number}, pspaces::AbstractVector{S}, - Dspaces::AbstractVector{S}; kwargs...) where {S<:IndexSpace} - return InfiniteMPS(MPSTensor.(f, elt, pspaces, circshift(Dspaces, 1), Dspaces); - kwargs...) +function InfiniteMPS( + f, elt::Type{<:Number}, pspaces::AbstractVector{S}, Dspaces::AbstractVector{S}; + kwargs... + ) where {S <: IndexSpace} + return InfiniteMPS( + MPSTensor.(f, elt, pspaces, circshift(Dspaces, 1), Dspaces); + kwargs... + ) end -InfiniteMPS(d::S, D::S) where {S<:Union{Int,<:IndexSpace}} = InfiniteMPS([d], [D]) -function InfiniteMPS(f, elt::Type{<:Number}, d::S, - D::S) where {S<:Union{Int,<:IndexSpace}} +InfiniteMPS(d::S, D::S) where {S <: Union{Int, <:IndexSpace}} = InfiniteMPS([d], [D]) +function InfiniteMPS( + f, elt::Type{<:Number}, d::S, D::S + ) where {S <: Union{Int, <:IndexSpace}} return InfiniteMPS(f, elt, [d], [D]) end function InfiniteMPS(ds::AbstractVector{Int}, Ds::AbstractVector{Int}) return InfiniteMPS(ComplexSpace.(ds), ComplexSpace.(Ds)) end -function InfiniteMPS(f, elt::Type{<:Number}, ds::AbstractVector{Int}, - Ds::AbstractVector{Int}, kwargs...) +function InfiniteMPS( + f, elt::Type{<:Number}, ds::AbstractVector{Int}, Ds::AbstractVector{Int}, kwargs... + ) return InfiniteMPS(f, elt, ComplexSpace.(ds), ComplexSpace.(Ds); kwargs...) end @@ -164,7 +175,7 @@ function InfiniteMPS(A::AbstractVector{<:GenericMPSTensor}; kwargs...) AL = similar.(AR) AC = similar.(AR) C = similar(AR, typeof(C₀)) - ψ = InfiniteMPS{eltype(AL),eltype(C)}(AL, AR, C, AC) + ψ = InfiniteMPS{eltype(AL), eltype(C)}(AL, AR, C, AC) # gaugefix the MPS gaugefix!(ψ, A_copy, C₀; kwargs...) @@ -183,10 +194,10 @@ function InfiniteMPS(AL::AbstractVector{<:GenericMPSTensor}, C₀::MPSBondTensor AC = similar.(AL) AR = similar.(AL) C = similar(AR, typeof(C₀)) - ψ = InfiniteMPS{eltype(AL),eltype(C)}(AL, AR, C, AC) + ψ = InfiniteMPS{eltype(AL), eltype(C)}(AL, AR, C, AC) # gaugefix the MPS - gaugefix!(ψ, AL, C₀; order=:R, kwargs...) + gaugefix!(ψ, AL, C₀; order = :R, kwargs...) mul!.(ψ.AC, ψ.AL, ψ.C) return ψ @@ -199,10 +210,10 @@ function InfiniteMPS(C₀::MPSBondTensor, AR::AbstractVector{<:GenericMPSTensor} AC = similar.(AR) AL = similar.(AR) C = similar(AR, typeof(C₀)) - ψ = InfiniteMPS{eltype(AL),eltype(C)}(AL, AR, C, AC) + ψ = InfiniteMPS{eltype(AL), eltype(C)}(AL, AR, C, AC) # gaugefix the MPS - gaugefix!(ψ, AR, C₀; order=:L, kwargs...) + gaugefix!(ψ, AR, C₀; order = :L, kwargs...) mul!.(ψ.AC, ψ.AL, ψ.C) return ψ @@ -212,7 +223,7 @@ end Utility ===========================================================================================# -function AC2(ψ::InfiniteMPS, i::Integer; kind=:ACAR) +function AC2(ψ::InfiniteMPS, i::Integer; kind = :ACAR) if kind == :ACAR return ψ.AC[i] * _transpose_tail(ψ.AR[i + 1]) elseif kind == :ALAC @@ -243,12 +254,13 @@ end function Base.repeat(ψ::InfiniteMPS, i::Int) return InfiniteMPS(repeat(ψ.AL, i), repeat(ψ.AR, i), repeat(ψ.C, i), repeat(ψ.AC, i)) end -function Base.similar(ψ::InfiniteMPS{A,B}) where {A,B} - return InfiniteMPS{A,B}(similar(ψ.AL), similar(ψ.AR), similar(ψ.C), similar(ψ.AC)) +function Base.similar(ψ::InfiniteMPS{A, B}) where {A, B} + return InfiniteMPS{A, B}(similar(ψ.AL), similar(ψ.AR), similar(ψ.C), similar(ψ.AC)) end function Base.circshift(ψ::InfiniteMPS, n) - return InfiniteMPS(circshift(ψ.AL, n), circshift(ψ.AR, n), circshift(ψ.C, n), - circshift(ψ.AC, n)) + return InfiniteMPS( + circshift(ψ.AL, n), circshift(ψ.AR, n), circshift(ψ.C, n), circshift(ψ.AC, n) + ) end Base.eachindex(ψ::InfiniteMPS) = eachindex(ψ.AL) @@ -257,7 +269,7 @@ Base.eachindex(l::IndexStyle, ψ::InfiniteMPS) = eachindex(l, ψ.AL) Base.checkbounds(::Type{Bool}, ψ::InfiniteMPS, i::Integer) = true site_type(::Type{<:InfiniteMPS{A}}) where {A} = A -bond_type(::Type{<:InfiniteMPS{<:Any,B}}) where {B} = B +bond_type(::Type{<:InfiniteMPS{<:Any, B}}) where {B} = B left_virtualspace(ψ::InfiniteMPS) = left_virtualspace.(ψ.AL) left_virtualspace(ψ::InfiniteMPS, n::Integer) = left_virtualspace(ψ.AL[n]) @@ -280,11 +292,12 @@ function TensorKit.normalize!(ψ::InfiniteMPS) return ψ end -function TensorKit.dot(ψ₁::InfiniteMPS, ψ₂::InfiniteMPS; krylovdim=30) +function TensorKit.dot(ψ₁::InfiniteMPS, ψ₂::InfiniteMPS; krylovdim = 30) init = similar(ψ₁.AL[1], _firstspace(ψ₂.AL[1]) ← _firstspace(ψ₁.AL[1])) randomize!(init) - val, = fixedpoint(TransferMatrix(ψ₂.AL, ψ₁.AL), init, :LM, - Arnoldi(; krylovdim=krylovdim)) + val, = fixedpoint( + TransferMatrix(ψ₂.AL, ψ₁.AL), init, :LM, Arnoldi(; krylovdim = krylovdim) + ) return val end function Base.isapprox(ψ₁::InfiniteMPS, ψ₂::InfiniteMPS; kwargs...) @@ -299,7 +312,7 @@ function Base.show(io::IO, ::MIME"text/plain", ψ::InfiniteMPS) end Base.show(io::IO, ψ::InfiniteMPS) = show(convert(IOContext, io), ψ) function Base.show(io::IOContext, ψ::InfiniteMPS) - charset = (; mid="├", ver="│", dash="──") + charset = (; mid = "├", ver = "│", dash = "──") limit = get(io, :limit, false)::Bool half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int) if !haskey(io, :compact) @@ -330,28 +343,28 @@ Fixedpoints Left dominant eigenvector of the `AR`-`AR` transfermatrix. """ -l_RR(ψ::InfiniteMPS, loc::Int=1) = adjoint(ψ.C[loc - 1]) * ψ.C[loc - 1] +l_RR(ψ::InfiniteMPS, loc::Int = 1) = adjoint(ψ.C[loc - 1]) * ψ.C[loc - 1] """ l_RL(ψ, location) Left dominant eigenvector of the `AR`-`AL` transfermatrix. """ -l_RL(ψ::InfiniteMPS, loc::Int=1) = ψ.C[loc - 1] +l_RL(ψ::InfiniteMPS, loc::Int = 1) = ψ.C[loc - 1] """ l_LR(ψ, location) Left dominant eigenvector of the `AL`-`AR` transfermatrix. """ -l_LR(ψ::InfiniteMPS, loc::Int=1) = ψ.C[loc - 1]' +l_LR(ψ::InfiniteMPS, loc::Int = 1) = ψ.C[loc - 1]' """ l_LL(ψ, location) Left dominant eigenvector of the `AL`-`AL` transfermatrix. """ -function l_LL(ψ::InfiniteMPS{A}, loc::Int=1) where {A} +function l_LL(ψ::InfiniteMPS{A}, loc::Int = 1) where {A} return isomorphism(storagetype(A), left_virtualspace(ψ, loc), left_virtualspace(ψ, loc)) end @@ -360,9 +373,10 @@ end Right dominant eigenvector of the `AR`-`AR` transfermatrix. """ -function r_RR(ψ::InfiniteMPS{A}, loc::Int=length(ψ)) where {A} - return isomorphism(storagetype(A), right_virtualspace(ψ, loc), - right_virtualspace(ψ, loc)) +function r_RR(ψ::InfiniteMPS{A}, loc::Int = length(ψ)) where {A} + return isomorphism( + storagetype(A), right_virtualspace(ψ, loc), right_virtualspace(ψ, loc) + ) end """ @@ -370,18 +384,18 @@ end Right dominant eigenvector of the `AR`-`AL` transfermatrix. """ -r_RL(ψ::InfiniteMPS, loc::Int=length(ψ)) = ψ.C[loc]' +r_RL(ψ::InfiniteMPS, loc::Int = length(ψ)) = ψ.C[loc]' """ r_LR(ψ, location) Right dominant eigenvector of the `AL`-`AR` transfermatrix. """ -r_LR(ψ::InfiniteMPS, loc::Int=length(ψ)) = ψ.C[loc] +r_LR(ψ::InfiniteMPS, loc::Int = length(ψ)) = ψ.C[loc] """ r_LL(ψ, location) Right dominant eigenvector of the `AL`-`AL` transfermatrix. """ -r_LL(ψ::InfiniteMPS, loc::Int=length(ψ)) = ψ.C[loc] * adjoint(ψ.C[loc]) +r_LL(ψ::InfiniteMPS, loc::Int = length(ψ)) = ψ.C[loc] * adjoint(ψ.C[loc]) diff --git a/src/states/multilinemps.jl b/src/states/multilinemps.jl index 6211b144d..8ce092843 100644 --- a/src/states/multilinemps.jl +++ b/src/states/multilinemps.jl @@ -21,21 +21,24 @@ See also: [`Multiline`](@ref) function MultilineMPS end MultilineMPS(mpss::AbstractVector{<:InfiniteMPS}) = Multiline(mpss) -function MultilineMPS(pspaces::AbstractMatrix{S}, Dspaces::AbstractMatrix{S}; - kwargs...) where {S<:VectorSpace} +function MultilineMPS( + pspaces::AbstractMatrix{S}, Dspaces::AbstractMatrix{S}; kwargs... + ) where {S <: VectorSpace} data = map(eachrow(pspaces), eachrow(Dspaces)) do p, D return InfiniteMPS(p, D; kwargs...) end return MultilineMPS(data) end -function MultilineMPS(As::AbstractMatrix{T}; kwargs...) where {T<:GenericMPSTensor} +function MultilineMPS(As::AbstractMatrix{T}; kwargs...) where {T <: GenericMPSTensor} data = map(eachrow(As)) do Arow return InfiniteMPS(Arow; kwargs...) end return MultilineMPS(data) end -function MultilineMPS(ALs::AbstractMatrix{<:GenericMPSTensor}, - C₀::AbstractVector{<:MPSBondTensor}; kwargs...) +function MultilineMPS( + ALs::AbstractMatrix{<:GenericMPSTensor}, C₀::AbstractVector{<:MPSBondTensor}; + kwargs... + ) data = map(eachrow(ALs), C₀) do ALrow, C₀row return InfiniteMPS(ALrow, C₀row; kwargs...) end @@ -71,11 +74,11 @@ function Base.propertynames(::MultilineMPS) end for f in (:l_RR, :l_RL, :l_LL, :l_LR) - @eval $f(t::MultilineMPS, i, j=1) = $f(t[i], j) + @eval $f(t::MultilineMPS, i, j = 1) = $f(t[i], j) end for f in (:r_RR, :r_RL, :r_LR, :r_LL) - @eval $f(t::MultilineMPS, i, j=size(t, 2)) = $f(t[i], j) + @eval $f(t::MultilineMPS, i, j = size(t, 2)) = $f(t[i], j) end site_type(::Type{Multiline{S}}) where {S} = site_type(S) @@ -96,8 +99,7 @@ TensorKit.normalize!(a::MultilineMPS) = (normalize!.(parent(a)); return a) Base.convert(::Type{MultilineMPS}, st::InfiniteMPS) = Multiline([st]) Base.convert(::Type{InfiniteMPS}, st::MultilineMPS) = only(st) Base.eltype(t::MultilineMPS) = eltype(t[1]) -Base.copy!(ψ::MultilineMPS, ϕ::MultilineMPS) = (copy!.(parent(ψ), parent(ϕ)); - ψ) +Base.copy!(ψ::MultilineMPS, ϕ::MultilineMPS) = (copy!.(parent(ψ), parent(ϕ)); ψ) left_virtualspace(t::MultilineMPS, i::Int, j::Int) = left_virtualspace(t[i], j) right_virtualspace(t::MultilineMPS, i::Int, j::Int) = right_virtualspace(t[i], j) diff --git a/src/states/ortho.jl b/src/states/ortho.jl index 5521950ba..572a7b8e3 100644 --- a/src/states/ortho.jl +++ b/src/states/ortho.jl @@ -1,7 +1,7 @@ # Algorithms # ---------- -const _GAUGE_ALG_EIGSOLVE = Defaults.alg_eigsolve(; ishermitian=false, tol_factor=1) +const _GAUGE_ALG_EIGSOLVE = Defaults.alg_eigsolve(; ishermitian = false, tol_factor = 1) """ $(TYPEDEF) @@ -71,10 +71,12 @@ struct MixedCanonical <: Algorithm order::Symbol end -function MixedCanonical(; tol::Real=Defaults.tolgauge, maxiter::Int=Defaults.maxiter, - verbosity::Int=VERBOSE_WARN, alg_orth=QRpos(), - alg_eigsolve=_GAUGE_ALG_EIGSOLVE, - eig_miniter::Int=10, order::Symbol=:LR) +function MixedCanonical(; + tol::Real = Defaults.tolgauge, maxiter::Int = Defaults.maxiter, + verbosity::Int = VERBOSE_WARN, alg_orth = QRpos(), + alg_eigsolve = _GAUGE_ALG_EIGSOLVE, + eig_miniter::Int = 10, order::Symbol = :LR + ) if alg_orth isa QR || alg_orth isa QRpos alg_leftorth = alg_orth alg_rightorth = alg_orth' @@ -85,10 +87,12 @@ function MixedCanonical(; tol::Real=Defaults.tolgauge, maxiter::Int=Defaults.max throw(ArgumentError("Invalid orthogonalization algorithm: $(typeof(alg_orth))")) end - left = LeftCanonical(; tol, maxiter, verbosity, alg_orth=alg_leftorth, - alg_eigsolve, eig_miniter) - right = RightCanonical(; tol, maxiter=maxiter, verbosity, alg_orth=alg_rightorth, - alg_eigsolve, eig_miniter) + left = LeftCanonical(; + tol, maxiter, verbosity, alg_orth = alg_leftorth, alg_eigsolve, eig_miniter + ) + right = RightCanonical(; + tol, maxiter = maxiter, verbosity, alg_orth = alg_rightorth, alg_eigsolve, eig_miniter + ) return MixedCanonical(left, right, order) end @@ -104,7 +108,7 @@ Bring an `InfiniteMPS` into a uniform gauge, using the specified algorithm. """ gaugefix! -function gaugefix!(ψ::InfiniteMPS, A, C₀=ψ.C[end]; order=:LR, kwargs...) +function gaugefix!(ψ::InfiniteMPS, A, C₀ = ψ.C[end]; order = :LR, kwargs...) alg = if order === :LR || order === :RL MixedCanonical(; order, kwargs...) elseif order === :L @@ -153,33 +157,33 @@ tensors. This minimizes `∥AC_i - AL_i * C_i∥` or `∥AC_i - C_{i-1} * AR_i """ regauge! -function regauge!(AC::GenericMPSTensor, C::MPSBondTensor; alg=QRpos()) +function regauge!(AC::GenericMPSTensor, C::MPSBondTensor; alg = QRpos()) Q_AC, _ = leftorth!(AC; alg) Q_C, _ = leftorth!(C; alg) return mul!(AC, Q_AC, Q_C') end -function regauge!(AC::Vector{<:GenericMPSTensor}, C::Vector{<:MPSBondTensor}; alg=QRpos()) +function regauge!(AC::Vector{<:GenericMPSTensor}, C::Vector{<:MPSBondTensor}; alg = QRpos()) for i in 1:length(AC) regauge!(AC[i], C[i]; alg) end return AC end -function regauge!(CL::MPSBondTensor, AC::GenericMPSTensor; alg=LQpos()) +function regauge!(CL::MPSBondTensor, AC::GenericMPSTensor; alg = LQpos()) AC_tail = _transpose_tail(AC) _, Q_AC = rightorth!(AC_tail; alg) _, Q_C = rightorth!(CL; alg) AR_tail = mul!(AC_tail, Q_C', Q_AC) return repartition!(AC, AR_tail) end -function regauge!(CL::Vector{<:MPSBondTensor}, AC::Vector{<:GenericMPSTensor}; alg=LQpos()) +function regauge!(CL::Vector{<:MPSBondTensor}, AC::Vector{<:GenericMPSTensor}; alg = LQpos()) for i in length(CL):-1:1 regauge!(CL[i], AC[i]; alg) end return CL end # fix ambiguity + error -regauge!(::MPSBondTensor, ::MPSBondTensor; alg=QRpos()) = error("method ambiguity") -function regauge!(::Vector{<:MPSBondTensor}, ::Vector{<:MPSBondTensor}; alg=QRpos()) +regauge!(::MPSBondTensor, ::MPSBondTensor; alg = QRpos()) = error("method ambiguity") +function regauge!(::Vector{<:MPSBondTensor}, ::Vector{<:MPSBondTensor}; alg = QRpos()) return error("method ambiguity") end @@ -193,7 +197,7 @@ function uniform_leftorth!((AL, C), A, C₀, alg::LeftCanonical) log = IterLog("LC") A_tail = _transpose_tail.(A) # pre-transpose A CA_tail = similar.(A_tail) # pre-allocate workspace - state = (; AL, C, A, A_tail, CA_tail, iter=0, ϵ=Inf) + state = (; AL, C, A, A_tail, CA_tail, iter = 0, ϵ = Inf) it = IterativeSolver(alg, state) loginit!(log, it.ϵ) @@ -212,7 +216,7 @@ function uniform_leftorth!((AL, C), A, C₀, alg::LeftCanonical) end end -function Base.iterate(it::IterativeSolver{LeftCanonical}, state=it.state) +function Base.iterate(it::IterativeSolver{LeftCanonical}, state = it.state) C₀ = gauge_eigsolve_step!(it, state) C₁ = gauge_orth_step!(it, state) ϵ = oftype(state.ϵ, norm(C₀ - C₁)) @@ -228,7 +232,7 @@ function gauge_eigsolve_step!(it::IterativeSolver{LeftCanonical}, state) if iter ≥ it.eig_miniter alg_eigsolve = updatetol(it.alg_eigsolve, 1, ϵ^2) _, vec = fixedpoint(flip(TransferMatrix(A, AL)), C[end], :LM, alg_eigsolve) - _, C[end] = leftorth!(vec; alg=it.alg_orth) + _, C[end] = leftorth!(vec; alg = it.alg_orth) end return C[end] end @@ -238,7 +242,7 @@ function gauge_orth_step!(it::IterativeSolver{LeftCanonical}, state) for i in 1:length(AL) mul!(CA_tail[i], C[i - 1], A_tail[i]) repartition!(AL[i], CA_tail[i]) - AL[i], C[i] = leftorth!(AL[i]; alg=it.alg_orth) + AL[i], C[i] = leftorth!(AL[i]; alg = it.alg_orth) end normalize!(C[end]) return C[end] @@ -250,7 +254,7 @@ function uniform_rightorth!((AR, C), A, C₀, alg::RightCanonical) # initialize algorithm and temporary variables log = IterLog("RC") AC_tail = _similar_tail.(A) # pre-allocate workspace - state = (; AR, C, A, AC_tail, iter=0, ϵ=Inf) + state = (; AR, C, A, AC_tail, iter = 0, ϵ = Inf) it = IterativeSolver(alg, state) loginit!(log, it.ϵ) @@ -269,7 +273,7 @@ function uniform_rightorth!((AR, C), A, C₀, alg::RightCanonical) end end -function Base.iterate(it::IterativeSolver{RightCanonical}, state=it.state) +function Base.iterate(it::IterativeSolver{RightCanonical}, state = it.state) C₀ = gauge_eigsolve_step!(it, state) C₁ = gauge_orth_step!(it, state) ϵ = oftype(state.ϵ, norm(C₀ - C₁)) @@ -285,7 +289,7 @@ function gauge_eigsolve_step!(it::IterativeSolver{RightCanonical}, state) if iter ≥ it.eig_miniter alg_eigsolve = updatetol(it.alg_eigsolve, 1, ϵ^2) _, vec = fixedpoint(TransferMatrix(A, AR), C[end], :LM, alg_eigsolve) - C[end], _ = rightorth!(vec; alg=it.alg_orth) + C[end], _ = rightorth!(vec; alg = it.alg_orth) end return C[end] end @@ -295,7 +299,7 @@ function gauge_orth_step!(it::IterativeSolver{RightCanonical}, state) for i in length(AR):-1:1 AC = mul!(AR[i], A[i], C[i]) # use AR as temporary storage for A * C tmp = repartition!(AC_tail[i], AC) - C[i - 1], tmp = rightorth!(tmp; alg=it.alg_orth) + C[i - 1], tmp = rightorth!(tmp; alg = it.alg_orth) repartition!(AR[i], tmp) # TODO: avoid doing this every iteration end normalize!(C[end]) diff --git a/src/states/orthoview.jl b/src/states/orthoview.jl index 0864a936f..c3abe3325 100644 --- a/src/states/orthoview.jl +++ b/src/states/orthoview.jl @@ -1,14 +1,14 @@ -struct ALView{S,E,N} <: AbstractArray{E,N} +struct ALView{S, E, N} <: AbstractArray{E, N} parent::S - ALView(parent::S) where {S} = new{S,site_type(S),length(size(parent))}(parent) + ALView(parent::S) where {S} = new{S, site_type(S), length(size(parent))}(parent) end -function Base.getindex(v::ALView{<:FiniteMPS,E}, i::Int)::E where {E} +function Base.getindex(v::ALView{<:FiniteMPS, E}, i::Int)::E where {E} ismissing(v.parent.ALs[i]) && v.parent.C[i] # by getting C[i], we are garantueeing that AL[i] exists return v.parent.ALs[i] end -function Base.getindex(v::ALView{<:WindowMPS,E}, i::Int)::E where {E} +function Base.getindex(v::ALView{<:WindowMPS, E}, i::Int)::E where {E} i <= length(v.parent) || throw(ArgumentError("out of bounds")) i < 1 && return v.parent.left_gs.AL[i] return ALView(v.parent.window)[i] @@ -19,18 +19,18 @@ function Base.setindex!(v::ALView{<:Multiline}, vec, i::Int, j::Int) return setindex!(v.parent[i].AL, vec, j) end -struct ARView{S,E,N} <: AbstractArray{E,N} +struct ARView{S, E, N} <: AbstractArray{E, N} parent::S - ARView(parent::S) where {S} = new{S,site_type(S),length(size(parent))}(parent) + ARView(parent::S) where {S} = new{S, site_type(S), length(size(parent))}(parent) end -function Base.getindex(v::ARView{<:FiniteMPS,E}, i::Int)::E where {E} +function Base.getindex(v::ARView{<:FiniteMPS, E}, i::Int)::E where {E} # by getting C[i-1], we are garantueeing that AR[i] exists ismissing(v.parent.ARs[i]) && v.parent.C[i - 1] return v.parent.ARs[i] end -function Base.getindex(v::ARView{<:WindowMPS,E}, i::Int)::E where {E} +function Base.getindex(v::ARView{<:WindowMPS, E}, i::Int)::E where {E} i >= 1 || throw(ArgumentError("out of bounds")) i > length(v.parent) && return v.parent.right_gs.AR[i] return ARView(v.parent.window)[i] @@ -41,12 +41,12 @@ function Base.setindex!(v::ARView{<:Multiline}, vec, i::Int, j::Int) return setindex!(v.parent[i].AR, vec, j) end -struct CView{S,E,N} <: AbstractArray{E,N} +struct CView{S, E, N} <: AbstractArray{E, N} parent::S - CView(parent::S) where {S} = new{S,bond_type(S),length(size(parent))}(parent) + CView(parent::S) where {S} = new{S, bond_type(S), length(size(parent))}(parent) end -function Base.getindex(v::CView{<:FiniteMPS,E}, i::Int)::E where {E} +function Base.getindex(v::CView{<:FiniteMPS, E}, i::Int)::E where {E} ismissing(v.parent.Cs[i + 1]) || return v.parent.Cs[i + 1] if i == 0 || !ismissing(v.parent.ALs[i]) # center is too far right @@ -60,7 +60,7 @@ function Base.getindex(v::CView{<:FiniteMPS,E}, i::Int)::E where {E} end for j in Iterators.reverse((i + 1):center) - v.parent.Cs[j], tmp = rightorth(_transpose_tail(v.parent.ACs[j]); alg=LQpos()) + v.parent.Cs[j], tmp = rightorth(_transpose_tail(v.parent.ACs[j]); alg = LQpos()) v.parent.ARs[j] = _transpose_front(tmp) if j != i + 1 # last AC not needed v.parent.ACs[j - 1] = _mul_tail(v.parent.ALs[j - 1], v.parent.Cs[j]) @@ -76,7 +76,7 @@ function Base.getindex(v::CView{<:FiniteMPS,E}, i::Int)::E where {E} end for j in center:i - v.parent.ALs[j], v.parent.Cs[j + 1] = leftorth(v.parent.ACs[j]; alg=QRpos()) + v.parent.ALs[j], v.parent.Cs[j + 1] = leftorth(v.parent.ACs[j]; alg = QRpos()) if j != i # last AC not needed v.parent.ACs[j + 1] = _mul_front(v.parent.Cs[j + 1], v.parent.ARs[j + 1]) end @@ -89,11 +89,10 @@ end function Base.setindex!(v::CView{<:FiniteMPS}, vec, i::Int) if ismissing(v.parent.Cs[i + 1]) if !ismissing(v.parent.ALs[i]) - (v.parent.Cs[i + 1], temp) = rightorth(_transpose_tail(v.parent.AC[i + 1]); - alg=LQpos()) + v.parent.Cs[i + 1], temp = rightorth(_transpose_tail(v.parent.AC[i + 1]); alg = LQpos()) v.parent.ARs[i + 1] = _transpose_front(temp) else - (v.parent.ALs[i], v.parent.Cs[i + 1]) = leftorth(v.parent.AC[i]; alg=QRpos()) + v.parent.ALs[i], v.parent.Cs[i + 1] = leftorth(v.parent.AC[i]; alg = QRpos()) end end @@ -114,12 +113,12 @@ function Base.setindex!(v::CView{<:Multiline}, vec, i::Int, j::Int) return setindex!(v.parent[i].C, vec, j) end; -struct ACView{S,E,N} <: AbstractArray{E,N} +struct ACView{S, E, N} <: AbstractArray{E, N} parent::S - ACView(parent::S) where {S} = new{S,site_type(S),length(size(parent))}(parent) + ACView(parent::S) where {S} = new{S, site_type(S), length(size(parent))}(parent) end -function Base.getindex(v::ACView{<:FiniteMPS,E}, i::Int)::E where {E} +function Base.getindex(v::ACView{<:FiniteMPS, E}, i::Int)::E where {E} ismissing(v.parent.ACs[i]) || return v.parent.ACs[i] if !ismissing(v.parent.ARs[i]) # center is too far left @@ -146,8 +145,9 @@ function Base.setindex!(v::ACView{<:FiniteMPS}, vec::GenericMPSTensor, i::Int) return setindex!(v.parent.ACs, vec, i) end -function Base.setindex!(v::ACView{<:FiniteMPS}, - vec::Tuple{<:GenericMPSTensor,<:GenericMPSTensor}, i::Int) +function Base.setindex!( + v::ACView{<:FiniteMPS}, vec::Tuple{<:GenericMPSTensor, <:GenericMPSTensor}, i::Int + ) if ismissing(v.parent.ACs[i]) i < length(v) && v.parent.AR[i + 1] i > 1 && v.parent.AL[i - 1] @@ -159,7 +159,7 @@ function Base.setindex!(v::ACView{<:FiniteMPS}, v.parent.ARs[1:i] .= missing a, b = vec - if isa(a, MPSBondTensor) #c/ar + return if isa(a, MPSBondTensor) #c/ar setindex!(v.parent.Cs, a, i) setindex!(v.parent.ARs, b, i) elseif isa(b, MPSBondTensor) #al/c @@ -170,7 +170,7 @@ function Base.setindex!(v::ACView{<:FiniteMPS}, end end -function Base.getindex(v::ACView{<:WindowMPS,E}, i::Int)::E where {E} +function Base.getindex(v::ACView{<:WindowMPS, E}, i::Int)::E where {E} (i >= 1 && i <= length(v.parent)) || throw(ArgumentError("out of bounds")) return ACView(v.parent.window)[i] end @@ -184,7 +184,7 @@ function Base.setindex!(v::ACView{<:Multiline}, vec, i::Int, j::Int) end #--- define the rest of the abstractarray interface -Base.size(psi::Union{ACView,ALView,ARView}) = size(psi.parent) +Base.size(psi::Union{ACView, ALView, ARView}) = size(psi.parent) #= CView is tricky. It starts at 0 for finitemps/WindowMPS, but for multiline Infinitemps objects, it should start at 1. @@ -202,9 +202,11 @@ end #the checkbounds for multiline objects needs to be changed, as the first index is periodic #however if it is a Multiline(Infinitemps), then the second index is also periodic! -function Base.checkbounds(::Type{Bool}, - psi::Union{ACView{<:Multiline},ALView{<:Multiline}, - ARView{<:Multiline},CView{<:Multiline}}, a, b) +function Base.checkbounds( + ::Type{Bool}, + psi::Union{ACView{<:Multiline}, ALView{<:Multiline}, ARView{<:Multiline}, CView{<:Multiline}}, + a, b + ) return if first(psi.parent.data) isa InfiniteMPS true else diff --git a/src/states/quasiparticle_state.jl b/src/states/quasiparticle_state.jl index ed1b81d9e..008ab05b8 100644 --- a/src/states/quasiparticle_state.jl +++ b/src/states/quasiparticle_state.jl @@ -4,7 +4,7 @@ I think it makes sense to see these things as an actual state instead of return This will allow us to plot energy density (finite qp) and measure observeables. =# -struct LeftGaugedQP{S,T1,T2,E<:Number} +struct LeftGaugedQP{S, T1, T2, E <: Number} # !(left_gs === right_gs) => domain wall excitation left_gs::S right_gs::S @@ -15,7 +15,7 @@ struct LeftGaugedQP{S,T1,T2,E<:Number} momentum::E end -struct RightGaugedQP{S,T1,T2,E<:Number} +struct RightGaugedQP{S, T1, T2, E <: Number} # !(left_gs === right_gs) => domain wall excitation left_gs::S right_gs::S @@ -26,22 +26,25 @@ struct RightGaugedQP{S,T1,T2,E<:Number} momentum::E end -function leftgaugedqptype(::Type{S}, ::Type{E}) where {S,E<:Number} +function leftgaugedqptype(::Type{S}, ::Type{E}) where {S, E <: Number} T1 = eltype(S) T2 = tensormaptype(spacetype(T1), 1, 2, storagetype(T1)) - return LeftGaugedQP{S,T1,T2,E} + return LeftGaugedQP{S, T1, T2, E} end #constructors -function LeftGaugedQP(datfun, left_gs, right_gs=left_gs; - sector=one(sectortype(left_gs)), momentum=0.0) +function LeftGaugedQP( + datfun, left_gs, right_gs = left_gs; + sector = one(sectortype(left_gs)), momentum = 0.0 + ) # find the left null spaces for the TNS excitation_space = Vect[typeof(sector)](sector => 1) VLs = convert(Vector, map(leftnull, left_gs.AL)) Xs = map(enumerate(VLs)) do (loc, vl) - x = similar(vl, - right_virtualspace(vl) ← - excitation_space ⊗ right_virtualspace(right_gs, loc)) + x = similar( + vl, + right_virtualspace(vl) ← excitation_space ⊗ right_virtualspace(right_gs, loc) + ) fill_data!(x, datfun) return x end @@ -51,8 +54,10 @@ function LeftGaugedQP(datfun, left_gs, right_gs=left_gs; @warn "momentum is ignored for finite quasiparticles" return LeftGaugedQP(left_gs, right_gs, VLs, Xs, momentum) end -function LeftGaugedQP(datfun, left_gs::MultilineMPS, right_gs::MultilineMPS=left_gs; - sector=one(sectortype(left_gs)), momentum=0.0) +function LeftGaugedQP( + datfun, left_gs::MultilineMPS, right_gs::MultilineMPS = left_gs; + sector = one(sectortype(left_gs)), momentum = 0.0 + ) # not sure why this is needed for type stability Tresult = leftgaugedqptype(eltype(parent(left_gs)), typeof(momentum)) qp_rows = Vector{Tresult}(undef, size(left_gs, 1)) @@ -62,15 +67,18 @@ function LeftGaugedQP(datfun, left_gs::MultilineMPS, right_gs::MultilineMPS=left return Multiline(qp_rows) end -function RightGaugedQP(datfun, left_gs, right_gs=left_gs; - sector=one(sectortype(left_gs)), momentum=0.0) +function RightGaugedQP( + datfun, left_gs, right_gs = left_gs; + sector = one(sectortype(left_gs)), momentum = 0.0 + ) # find the left null spaces for the TNS excitation_space = Vect[typeof(sector)](sector => 1) VRs = convert(Vector, map(rightnull! ∘ _transpose_tail, right_gs.AR)) Xs = map(enumerate(VRs)) do (i, vr) - x = similar(vr, - left_virtualspace(left_gs, i)' ← - excitation_space ⊗ _firstspace(vr)) + x = similar( + vr, + left_virtualspace(left_gs, i)' ← excitation_space ⊗ _firstspace(vr) + ) return fill_data!(x, datfun) end left_gs isa InfiniteMPS || @@ -80,16 +88,16 @@ function RightGaugedQP(datfun, left_gs, right_gs=left_gs; end #gauge dependent code -function Base.similar(v::LeftGaugedQP, ::Type{T}=scalartype(v)) where {T<:Number} +function Base.similar(v::LeftGaugedQP, ::Type{T} = scalartype(v)) where {T <: Number} return LeftGaugedQP(v.left_gs, v.right_gs, v.VLs, similar.(v.Xs, T), v.momentum) end -function Base.similar(v::RightGaugedQP, ::Type{T}=scalartype(v)) where {T<:Number} +function Base.similar(v::RightGaugedQP, ::Type{T} = scalartype(v)) where {T <: Number} return RightGaugedQP(v.left_gs, v.right_gs, similar.(v.Xs, T), v.VRs, v.momentum) end Base.getindex(v::LeftGaugedQP, i::Int) = v.VLs[mod1(i, end)] * v.Xs[mod1(i, end)] function Base.getindex(v::RightGaugedQP, i::Int) - @plansor t[-1 -2; -3 -4] := v.Xs[mod1(i, end)][-1; -3 1] * v.VRs[mod1(i, end)][1; -4 -2] + return @plansor t[-1 -2; -3 -4] := v.Xs[mod1(i, end)][-1; -3 1] * v.VRs[mod1(i, end)][1; -4 -2] end function Base.setindex!(v::LeftGaugedQP, B, i::Int) @@ -97,23 +105,25 @@ function Base.setindex!(v::LeftGaugedQP, B, i::Int) return v end function Base.setindex!(v::RightGaugedQP, B, i::Int) - @plansor v.Xs[mod1(i, end)][-1; -2 -3] := B[-1 1; -2 2] * - conj(v.VRs[mod1(i, end)][-3; 2 1]) + @plansor v.Xs[mod1(i, end)][-1; -2 -3] := B[-1 1; -2 2] * conj(v.VRs[mod1(i, end)][-3; 2 1]) return v end #conversion between gauges (partially implemented) -function Base.convert(::Type{RightGaugedQP}, - input::LeftGaugedQP{S}) where {S<:InfiniteMPS} - rg = RightGaugedQP(zero, input.left_gs, input.right_gs; - sector=first(sectors(auxiliaryspace(input))), - momentum=input.momentum) +function Base.convert( + ::Type{RightGaugedQP}, input::LeftGaugedQP{S} + ) where {S <: InfiniteMPS} + rg = RightGaugedQP( + zero, input.left_gs, input.right_gs; + sector = first(sectors(auxiliaryspace(input))), momentum = input.momentum + ) len = length(input) #construct environments - rBs = [@plansor t[-1; -2 -3] := input[len][-1 2; -2 3] * - conj(input.right_gs.AR[len][-3 2; 3]) * - exp(1im * input.momentum)] + rBs = [ + @plansor t[-1; -2 -3] := input[len][-1 2; -2 3] * + conj(input.right_gs.AR[len][-3 2; 3]) * exp(1im * input.momentum) + ] for i in (len - 1):-1:1 t = TransferMatrix(input.left_gs.AL[i], input.right_gs.AR[i]) * rBs[end] @plansor t[-1; -2 -3] += input[i][-1 2; -2 3] * conj(input.right_gs.AR[i][-3 2; 3]) @@ -127,36 +137,42 @@ function Base.convert(::Type{RightGaugedQP}, tm = regularize(tm, l_LR(input.right_gs), r_LR(input.right_gs)) end - rBE, convhist = linsolve(tm, rBs[1], rBs[1], GMRES(), 1, - -exp(1im * input.momentum * len)) + rBE, convhist = linsolve( + tm, rBs[1], rBs[1], GMRES(), 1, -exp(1im * input.momentum * len) + ) convhist.converged == 0 && @warn "failed to converge: normres = $(convhist.normres)" rBs[1] = rBE for i in len:-1:2 rBE = TransferMatrix(input.left_gs.AL[i], input.right_gs.AR[i]) * rBE * - exp(1im * input.momentum) + exp(1im * input.momentum) rBs[i] += rBE end #final contraction is now easy for i in 1:len @plansor T[-1 -2; -3 -4] := input.left_gs.AL[i][-1 -2; 1] * - rBs[mod1(i + 1, end)][1; -3 -4] + rBs[mod1(i + 1, end)][1; -3 -4] @plansor T[-1 -2; -3 -4] += input[i][-1 -2; -3 -4] rg[i] = T end return rg end -function Base.convert(::Type{LeftGaugedQP}, - input::RightGaugedQP{S}) where {S<:InfiniteMPS} - lg = LeftGaugedQP(zero, input.left_gs, input.right_gs; - sector=first(sectors(auxiliaryspace(input))), momentum=input.momentum) +function Base.convert( + ::Type{LeftGaugedQP}, input::RightGaugedQP{S} + ) where {S <: InfiniteMPS} + lg = LeftGaugedQP( + zero, input.left_gs, input.right_gs; + sector = first(sectors(auxiliaryspace(input))), momentum = input.momentum + ) len = length(input) - lBs = [@plansor t[-1; -2 -3] := input[1][1 2; -2 -3] * - conj(input.left_gs.AL[1][1 2; -1])] ./ - exp(1im * input.momentum) + lBs = [ + @plansor t[-1; -2 -3] := input[1][1 2; -2 -3] * + conj(input.left_gs.AL[1][1 2; -1]) + ] ./ + exp(1im * input.momentum) for i in 2:len t = lBs[end] * TransferMatrix(input.right_gs.AR[i], input.left_gs.AL[i]) @plansor t[-1; -2 -3] += input[i][1 2; -2 -3] * conj(input.left_gs.AL[i][1 2; -1]) @@ -168,20 +184,21 @@ function Base.convert(::Type{LeftGaugedQP}, tm = regularize(tm, l_RL(input.right_gs), r_RL(input.right_gs)) end - lBE, convhist = linsolve(flip(tm), lBs[end], lBs[end], GMRES(), 1, - -1 / exp(1im * input.momentum * len)) + lBE, convhist = linsolve( + flip(tm), lBs[end], lBs[end], GMRES(), 1, -1 / exp(1im * input.momentum * len) + ) convhist.converged == 0 && @warn "failed to converge: normres = $(convhist.normres)" lBs[end] = lBE for i in 1:(len - 1) lBE = lBE * TransferMatrix(input.right_gs.AR[i], input.left_gs.AL[i]) / - exp(1im * input.momentum) + exp(1im * input.momentum) lBs[i] += lBE end for i in 1:len @plansor T[-1 -2; -3 -4] := lBs[mod1(i - 1, len)][-1; -3 1] * - input.right_gs.AR[i][1 -2; -4] + input.right_gs.AR[i][1 -2; -4] @plansor T[-1 -2; -3 -4] += input[i][-1 -2; -3 -4] lg[i] = T end @@ -190,13 +207,13 @@ function Base.convert(::Type{LeftGaugedQP}, end # gauge independent code -const QP{S,T1,T2} = Union{LeftGaugedQP{S,T1,T2},RightGaugedQP{S,T1,T2}} -const FiniteQP{S<:FiniteMPS,T1,T2} = QP{S,T1,T2} -const InfiniteQP{S<:InfiniteMPS,T1,T2} = QP{S,T1,T2} -const MultilineQP{Q<:QP} = Multiline{Q} +const QP{S, T1, T2} = Union{LeftGaugedQP{S, T1, T2}, RightGaugedQP{S, T1, T2}} +const FiniteQP{S <: FiniteMPS, T1, T2} = QP{S, T1, T2} +const InfiniteQP{S <: InfiniteMPS, T1, T2} = QP{S, T1, T2} +const MultilineQP{Q <: QP} = Multiline{Q} -TensorKit.spacetype(::Union{QP{S},Type{<:QP{S}}}) where {S} = spacetype(S) -TensorKit.sectortype(::Union{QP{S},Type{<:QP{S}}}) where {S} = sectortype(S) +TensorKit.spacetype(::Union{QP{S}, Type{<:QP{S}}}) where {S} = spacetype(S) +TensorKit.sectortype(::Union{QP{S}, Type{<:QP{S}}}) where {S} = sectortype(S) left_virtualspace(state::QP, i::Int) = left_virtualspace(state.left_gs, i) right_virtualspace(state::QP, i::Int) = right_virtualspace(state.right_gs, i) @@ -204,7 +221,7 @@ auxiliaryspace(state::QP) = space(state.Xs[1], 2) Base.copy(a::QP) = copy!(similar(a), a) Base.copyto!(a::QP, b::QP) = copy!(a, b) -function Base.copy!(a::T, b::T) where {T<:QP} +function Base.copy!(a::T, b::T) where {T <: QP} for (i, j) in zip(a.Xs, b.Xs) copy!(i, j) end @@ -218,31 +235,31 @@ function Base.getproperty(v::QP, s::Symbol) end end -function Base.:-(v::T, w::T) where {T<:QP} +function Base.:-(v::T, w::T) where {T <: QP} t = similar(v) t.Xs[:] = (v.Xs - w.Xs)[:] return t end -function Base.:+(v::T, w::T) where {T<:QP} +function Base.:+(v::T, w::T) where {T <: QP} t = similar(v) t.Xs[:] = (v.Xs + w.Xs)[:] return t end -LinearAlgebra.dot(v::T, w::T) where {T<:QP} = sum(dot.(v.Xs, w.Xs)) +LinearAlgebra.dot(v::T, w::T) where {T <: QP} = sum(dot.(v.Xs, w.Xs)) LinearAlgebra.norm(v::QP) = norm(norm.(v.Xs)) LinearAlgebra.normalize!(w::QP) = rmul!(w, 1 / norm(w)) Base.length(v::QP) = length(v.Xs) -Base.eltype(::Type{<:QP{<:Any,<:Any,T}}) where {T} = T +Base.eltype(::Type{<:QP{<:Any, <:Any, T}}) where {T} = T -function LinearAlgebra.mul!(w::T, a::Number, v::T) where {T<:QP} +function LinearAlgebra.mul!(w::T, a::Number, v::T) where {T <: QP} @inbounds for (i, j) in zip(w.Xs, v.Xs) LinearAlgebra.mul!(i, a, j) end return w end -function LinearAlgebra.mul!(w::T, v::T, a::Number) where {T<:QP} +function LinearAlgebra.mul!(w::T, v::T, a::Number) where {T <: QP} @inbounds for (i, j) in zip(w.Xs, v.Xs) LinearAlgebra.mul!(i, j, a) end @@ -255,13 +272,13 @@ function LinearAlgebra.rmul!(v::QP, a::Number) return v end -function LinearAlgebra.axpy!(a::Number, v::T, w::T) where {T<:QP} +function LinearAlgebra.axpy!(a::Number, v::T, w::T) where {T <: QP} @inbounds for (i, j) in zip(w.Xs, v.Xs) LinearAlgebra.axpy!(a, j, i) end return w end -function LinearAlgebra.axpby!(a::Number, v::T, b::Number, w::T) where {T<:QP} +function LinearAlgebra.axpby!(a::Number, v::T, b::Number, w::T) where {T <: QP} @inbounds for (i, j) in zip(w.Xs, v.Xs) LinearAlgebra.axpby!(a, j, b, i) end @@ -273,7 +290,7 @@ Base.:*(a::Number, v::QP) = mul!(similar(v), a, v) Base.zero(v::QP) = v * 0; -function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S<:FiniteMPS} +function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S <: FiniteMPS} #very slow and clunky, but shouldn't be performance critical anyway elt = scalartype(v) @@ -293,19 +310,24 @@ function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S<:FiniteMPS} #step 0 : fuse the utility leg of B with the first leg of B orig_Bs = map(i -> v[i], 1:length(v)) Bs = map(orig_Bs) do t - frontmap = isomorphism(storagetype(t), fuse(utl * _firstspace(t)), - utl * _firstspace(t)) + frontmap = isomorphism( + storagetype(t), fuse(utl * _firstspace(t)), utl * _firstspace(t) + ) @plansor tt[-1 -2; -3] := t[1 -2; 2 -3] * frontmap[-1; 2 1] end function simplefuse(temp) - frontmap = isomorphism(storagetype(temp), fuse(space(temp, 1) * space(temp, 2)), - space(temp, 1) * space(temp, 2)) - backmap = isomorphism(storagetype(temp), space(temp, 5)' * space(temp, 4)', - fuse(space(temp, 5)' * space(temp, 4)')) - - @plansor tempp[-1 -2; -3] := frontmap[-1; 1 2] * temp[1 2 -2 3; 4] * - backmap[4 3; -3] + frontmap = isomorphism( + storagetype(temp), fuse(space(temp, 1) * space(temp, 2)), + space(temp, 1) * space(temp, 2) + ) + backmap = isomorphism( + storagetype(temp), space(temp, 5)' * space(temp, 4)', + fuse(space(temp, 5)' * space(temp, 4)') + ) + + return @plansor tempp[-1 -2; -3] := frontmap[-1; 1 2] * temp[1 2 -2 3; 4] * + backmap[4 3; -3] end #step 1 : pass utl through Ls @@ -322,10 +344,14 @@ function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S<:FiniteMPS} push!(superspaces, supremum(_lastspace(Ls[end])', _lastspace(Rs[end])')) for i in 1:(length(v) + 1) - Lf = isometry(storagetype(Ls[i <= length(v) ? i : i - 1]), superspaces[i], - i <= length(v) ? _firstspace(Ls[i]) : _lastspace(Ls[i - 1])') - Rf = isometry(storagetype(Rs[i <= length(v) ? i : i - 1]), superspaces[i], - i <= length(v) ? _firstspace(Rs[i]) : _lastspace(Rs[i - 1])') + Lf = isometry( + storagetype(Ls[i <= length(v) ? i : i - 1]), superspaces[i], + i <= length(v) ? _firstspace(Ls[i]) : _lastspace(Ls[i - 1])' + ) + Rf = isometry( + storagetype(Rs[i <= length(v) ? i : i - 1]), superspaces[i], + i <= length(v) ? _firstspace(Rs[i]) : _lastspace(Rs[i - 1])' + ) if i <= length(v) @plansor Ls[i][-1 -2; -3] := Lf[-1; 1] * Ls[i][1 -2; -3] @@ -367,7 +393,7 @@ function Base.convert(::Type{<:FiniteMPS}, v::QP{S}) where {S<:FiniteMPS} Bs[i] = simplefuse(temp) end - return FiniteMPS(Ls + Rs + Bs; normalize=false) + return FiniteMPS(Ls + Rs + Bs; normalize = false) end function Base.getproperty(exci::MultilineQP, s::Symbol) @@ -378,7 +404,7 @@ function Base.getproperty(exci::MultilineQP, s::Symbol) elseif s == :right_gs Multiline(map(x -> x.right_gs, exci.data)) elseif s == :trivial - return reduce(&, map(x -> x.trivial, exci.data); init=true) + return reduce(&, map(x -> x.trivial, exci.data); init = true) else return getfield(exci, s) end @@ -389,7 +415,7 @@ end VectorInterface.scalartype(T::Type{<:QP}) = scalartype(eltype(T)) -function VectorInterface.zerovector(ϕ::QP, ::Type{S}) where {S<:Number} +function VectorInterface.zerovector(ϕ::QP, ::Type{S}) where {S <: Number} ϕ = similar(ϕ, S) return zerovector!(ϕ) end diff --git a/src/states/windowmps.jl b/src/states/windowmps.jl index 147b1d295..56c38452a 100644 --- a/src/states/windowmps.jl +++ b/src/states/windowmps.jl @@ -35,18 +35,18 @@ left (and right) environment to construct the WindowMPS in one step. Construct a WindowMPS from an InfiniteMPS, by promoting a region of length `L` to a `FiniteMPS`. """ -struct WindowMPS{A<:GenericMPSTensor,B<:MPSBondTensor} <: AbstractFiniteMPS - left_gs::InfiniteMPS{A,B} - window::FiniteMPS{A,B} - right_gs::InfiniteMPS{A,B} - - function WindowMPS(ψₗ::InfiniteMPS{A,B}, ψₘ::FiniteMPS{A,B}, - ψᵣ::InfiniteMPS{A,B}=copy(ψₗ)) where {A<:GenericMPSTensor, - B<:MPSBondTensor} +struct WindowMPS{A <: GenericMPSTensor, B <: MPSBondTensor} <: AbstractFiniteMPS + left_gs::InfiniteMPS{A, B} + window::FiniteMPS{A, B} + right_gs::InfiniteMPS{A, B} + + function WindowMPS( + ψₗ::InfiniteMPS{A, B}, ψₘ::FiniteMPS{A, B}, ψᵣ::InfiniteMPS{A, B} = copy(ψₗ) + ) where {A <: GenericMPSTensor, B <: MPSBondTensor} left_virtualspace(ψₗ, 1) == left_virtualspace(ψₘ, 1) && right_virtualspace(ψₘ, length(ψₘ)) == right_virtualspace(ψᵣ, length(ψₘ)) || throw(SpaceMismatch("Mismatch between window and environment virtual spaces")) - return new{A,B}(ψₗ, ψₘ, ψᵣ) + return new{A, B}(ψₗ, ψₘ, ψᵣ) end end @@ -54,31 +54,41 @@ end Constructors ===========================================================================================# -function WindowMPS(ψₗ::InfiniteMPS, site_tensors::AbstractVector{<:GenericMPSTensor}, - ψᵣ::InfiniteMPS=ψₗ) +function WindowMPS( + ψₗ::InfiniteMPS, site_tensors::AbstractVector{<:GenericMPSTensor}, + ψᵣ::InfiniteMPS = ψₗ + ) return WindowMPS(ψₗ, FiniteMPS(site_tensors), ψᵣ) end -function WindowMPS(f, elt, physspaces::Vector{<:Union{S,CompositeSpace{S}}}, - maxvirtspace::S, ψₗ::InfiniteMPS, - ψᵣ::InfiniteMPS=ψₗ) where {S<:ElementarySpace} - ψₘ = FiniteMPS(f, elt, physspaces, maxvirtspace; left=left_virtualspace(ψₗ, 1), - right=right_virtualspace(ψᵣ, length(physspaces))) +function WindowMPS( + f, elt, physspaces::Vector{<:Union{S, CompositeSpace{S}}}, + maxvirtspace::S, ψₗ::InfiniteMPS, ψᵣ::InfiniteMPS = ψₗ + ) where {S <: ElementarySpace} + ψₘ = FiniteMPS( + f, elt, physspaces, maxvirtspace; left = left_virtualspace(ψₗ, 1), + right = right_virtualspace(ψᵣ, length(physspaces)) + ) return WindowMPS(ψₗ, ψₘ, ψᵣ) end -function WindowMPS(physspaces::Vector{<:Union{S,CompositeSpace{S}}}, maxvirtspace::S, - ψₗ::InfiniteMPS, ψᵣ::InfiniteMPS=ψₗ) where {S<:ElementarySpace} +function WindowMPS( + physspaces::Vector{<:Union{S, CompositeSpace{S}}}, maxvirtspace::S, + ψₗ::InfiniteMPS, ψᵣ::InfiniteMPS = ψₗ + ) where {S <: ElementarySpace} return WindowMPS(rand, Defaults.eltype, physspaces, maxvirtspace, ψₗ, ψᵣ) end -function WindowMPS(f, elt, physspaces::Vector{<:Union{S,CompositeSpace{S}}}, - virtspaces::Vector{S}, ψₗ::InfiniteMPS, - ψᵣ::InfiniteMPS=ψₗ) where {S<:ElementarySpace} +function WindowMPS( + f, elt, physspaces::Vector{<:Union{S, CompositeSpace{S}}}, + virtspaces::Vector{S}, ψₗ::InfiniteMPS, ψᵣ::InfiniteMPS = ψₗ + ) where {S <: ElementarySpace} ψₘ = FiniteMPS(f, elt, physspaces, virtspaces) return WindowMPS(ψₗ, ψₘ, ψᵣ) end -function WindowMPS(physspaces::Vector{<:Union{S,CompositeSpace{S}}}, virtspaces::Vector{S}, - ψₗ::InfiniteMPS, ψᵣ::InfiniteMPS=ψₗ) where {S<:ElementarySpace} +function WindowMPS( + physspaces::Vector{<:Union{S, CompositeSpace{S}}}, virtspaces::Vector{S}, + ψₗ::InfiniteMPS, ψᵣ::InfiniteMPS = ψₗ + ) where {S <: ElementarySpace} return WindowMPS(rand, Defaults.eltype, physspaces, virtspaces, ψₗ, ψᵣ) end @@ -96,11 +106,11 @@ function WindowMPS(N::Int, V::VectorSpace, args...; kwargs...) return WindowMPS(fill(V, N), args...; kwargs...) end -function WindowMPS(ψ::InfiniteMPS{A,B}, L::Int) where {A,B} - CLs = Vector{Union{Missing,B}}(missing, L + 1) - ALs = Vector{Union{Missing,A}}(missing, L) - ARs = Vector{Union{Missing,A}}(missing, L) - ACs = Vector{Union{Missing,A}}(missing, L) +function WindowMPS(ψ::InfiniteMPS{A, B}, L::Int) where {A, B} + CLs = Vector{Union{Missing, B}}(missing, L + 1) + ALs = Vector{Union{Missing, A}}(missing, L) + ARs = Vector{Union{Missing, A}}(missing, L) + ACs = Vector{Union{Missing, A}}(missing, L) ALs .= ψ.AL[1:L] ARs .= ψ.AR[1:L] @@ -133,17 +143,18 @@ Base.eltype(::Type{<:WindowMPS{A}}) where {A} = A Base.checkbounds(::Type{Bool}, ψ::WindowMPS, i::Integer) = true site_type(::Type{<:WindowMPS{A}}) where {A} = A -bond_type(::Type{<:WindowMPS{<:Any,B}}) where {B} = B +bond_type(::Type{<:WindowMPS{<:Any, B}}) where {B} = B TensorKit.space(ψ::WindowMPS, n::Integer) = space(ψ.AC[n], 2) for f in (:physicalspace, :left_virtualspace, :right_virtualspace) @eval $f(ψ::WindowMPS, n::Integer) = n < 1 ? $f(ψ.left_gs, n) : - n > length(ψ) ? $f(ψ.right_gs, n - length(ψ)) : - $f(ψ.window, n) + n > length(ψ) ? $f(ψ.right_gs, n - length(ψ)) : + $f(ψ.window, n) end function physicalspace(ψ::WindowMPS) - return WindowArray(physicalspace(ψ.left_gs), physicalspace(ψ.window), - physicalspace(ψ.right_gs)) + return WindowArray( + physicalspace(ψ.left_gs), physicalspace(ψ.window), physicalspace(ψ.right_gs) + ) end r_RR(ψ::WindowMPS) = r_RR(ψ.right_gs, length(ψ)) l_LL(ψ::WindowMPS) = l_LL(ψ.left_gs, 1) diff --git a/src/transfermatrix/transfer.jl b/src/transfermatrix/transfer.jl index c2d263f6f..15cc2237a 100644 --- a/src/transfermatrix/transfer.jl +++ b/src/transfermatrix/transfer.jl @@ -15,15 +15,18 @@ apply a transfer matrix to the left. └─Ā─ ``` """ -@generated function transfer_left(v::AbstractTensorMap{<:Any,S,1,N₁}, - A::GenericMPSTensor{S,N₂}, - Abar::GenericMPSTensor{S,N₂}) where {S,N₁,N₂} +@generated function transfer_left( + v::AbstractTensorMap{<:Any, S, 1, N₁}, + A::GenericMPSTensor{S, N₂}, + Abar::GenericMPSTensor{S, N₂} + ) where {S, N₁, N₂} t_out = tensorexpr(:v, -1, -(2:(N₁ + 1))) t_top = tensorexpr(:A, 2:(N₂ + 1), -(N₁ + 1)) t_bot = tensorexpr(:Abar, (1, (3:(N₂ + 1))...), -1) t_in = tensorexpr(:v, 1, (-(2:N₁)..., 2)) - return macroexpand(@__MODULE__, - :(return @plansor $t_out := $t_in * $t_top * conj($t_bot))) + return macroexpand( + @__MODULE__, :(return @plansor $t_out := $t_in * $t_top * conj($t_bot)) + ) end """ @@ -37,61 +40,64 @@ apply a transfer matrix to the right. ─Ā─┘ ``` """ -@generated function transfer_right(v::AbstractTensorMap{<:Any,S,1,N₁}, - A::GenericMPSTensor{S,N₂}, - Abar::GenericMPSTensor{S,N₂}) where {S,N₁,N₂} +@generated function transfer_right( + v::AbstractTensorMap{<:Any, S, 1, N₁}, + A::GenericMPSTensor{S, N₂}, + Abar::GenericMPSTensor{S, N₂} + ) where {S, N₁, N₂} t_out = tensorexpr(:v, -1, -(2:(N₁ + 1))) t_top = tensorexpr(:A, (-1, reverse(3:(N₂ + 1))...), 1) t_bot = tensorexpr(:Abar, (-(N₁ + 1), reverse(3:(N₂ + 1))...), 2) t_in = tensorexpr(:v, 1, (-(2:N₁)..., 2)) - return macroexpand(@__MODULE__, - :(return @plansor $t_out := $t_top * conj($t_bot) * $t_in)) + return macroexpand( + @__MODULE__, :(return @plansor $t_out := $t_top * conj($t_bot) * $t_in) + ) end # transfer, but the upper A is an excited tensor function transfer_left(v::MPSBondTensor, A::MPOTensor, Ab::MPSTensor) - @plansor t[-1; -2 -3] := v[1; 2] * A[2 3; -2 -3] * conj(Ab[1 3; -1]) + return @plansor t[-1; -2 -3] := v[1; 2] * A[2 3; -2 -3] * conj(Ab[1 3; -1]) end function transfer_right(v::MPSBondTensor, A::MPOTensor, Ab::MPSTensor) - @plansor t[-1; -2 -3] := A[-1 3; -2 1] * v[1; 2] * conj(Ab[-3 3; 2]) + return @plansor t[-1; -2 -3] := A[-1 3; -2 1] * v[1; 2] * conj(Ab[-3 3; 2]) end # transfer, but the upper A is an excited tensor and there is an mpo leg being passed through function transfer_left(v::MPSTensor, A::MPOTensor, Ab::MPSTensor) - @plansor t[-1 -2; -3 -4] := v[1 3; 4] * A[4 5; -3 -4] * τ[3 2; 5 -2] * conj(Ab[1 2; -1]) + return @plansor t[-1 -2; -3 -4] := v[1 3; 4] * A[4 5; -3 -4] * τ[3 2; 5 -2] * conj(Ab[1 2; -1]) end function transfer_right(v::MPSTensor, A::MPOTensor, Ab::MPSTensor) - @plansor t[-1 -2; -3 -4] := A[-1 4; -3 5] * τ[-2 3; 4 2] * conj(Ab[-4 3; 1]) * v[5 2; 1] + return @plansor t[-1 -2; -3 -4] := A[-1 4; -3 5] * τ[-2 3; 4 2] * conj(Ab[-4 3; 1]) * v[5 2; 1] end # the transfer operation of a density matrix with a utility leg in its codomain is ill defined - how should one braid the utility leg? # hence the checks - to make sure that this operation is uniquely defined function transfer_left(v::MPSTensor{S}, A::MPSTensor{S}, Ab::MPSTensor{S}) where {S} check_unambiguous_braiding(space(v, 2)) - @plansor v[-1 -2; -3] := v[1 2; 4] * A[4 5; -3] * τ[2 3; 5 -2] * conj(Ab[1 3; -1]) + return @plansor v[-1 -2; -3] := v[1 2; 4] * A[4 5; -3] * τ[2 3; 5 -2] * conj(Ab[1 3; -1]) end function transfer_right(v::MPSTensor{S}, A::MPSTensor{S}, Ab::MPSTensor{S}) where {S} check_unambiguous_braiding(space(v, 2)) - @plansor v[-1 -2; -3] := A[-1 2; 1] * τ[-2 4; 2 3] * conj(Ab[-3 4; 5]) * v[1 3; 5] + return @plansor v[-1 -2; -3] := A[-1 2; 1] * τ[-2 4; 2 3] * conj(Ab[-3 4; 5]) * v[1 3; 5] end # the transfer operation with a utility leg in both the domain and codomain is also ill defined - only due to the codomain utility space function transfer_left(v::MPOTensor{S}, A::MPSTensor{S}, Ab::MPSTensor{S}) where {S} check_unambiguous_braiding(space(v, 2)) - @plansor t[-1 -2; -3 -4] := v[1 2; -3 4] * A[4 5; -4] * τ[2 3; 5 -2] * conj(Ab[1 3; -1]) + return @plansor t[-1 -2; -3 -4] := v[1 2; -3 4] * A[4 5; -4] * τ[2 3; 5 -2] * conj(Ab[1 3; -1]) end function transfer_right(v::MPOTensor{S}, A::MPSTensor{S}, Ab::MPSTensor{S}) where {S} check_unambiguous_braiding(space(v, 2)) - @plansor t[-1 -2; -3 -4] := A[-1 2; 1] * τ[-2 4; 2 3] * conj(Ab[-4 4; 5]) * v[1 3; -3 5] + return @plansor t[-1 -2; -3 -4] := A[-1 2; 1] * τ[-2 4; 2 3] * conj(Ab[-4 4; 5]) * v[1 3; -3 5] end #transfer for 2 mpo tensors function transfer_left(v::MPSBondTensor, A::MPOTensor, B::MPOTensor) - @plansor t[-1; -2] := v[1; 2] * A[2 3; 4 -2] * conj(B[1 3; 4 -1]) + return @plansor t[-1; -2] := v[1; 2] * A[2 3; 4 -2] * conj(B[1 3; 4 -1]) end function transfer_right(v::MPSBondTensor, A::MPOTensor, B::MPOTensor) - @plansor t[-1; -2] := A[-1 3; 4 1] * conj(B[-2 3; 4 2]) * v[1; 2] + return @plansor t[-1; -2] := A[-1 3; 4 1] * conj(B[-2 3; 4 2]) * v[1; 2] end # ---------------------------------------------------- @@ -102,37 +108,39 @@ transfer_left(v, ::Nothing, A, B) = transfer_left(v, A, B); transfer_right(v, ::Nothing, A, B) = transfer_right(v, A, B); #mpo transfer function transfer_left(x::MPSTensor, O::MPOTensor, A::MPSTensor, Ab::MPSTensor) - @plansor y[-1 -2; -3] := x[1 2; 4] * A[4 5; -3] * O[2 3; 5 -2] * conj(Ab[1 3; -1]) + return @plansor y[-1 -2; -3] := x[1 2; 4] * A[4 5; -3] * O[2 3; 5 -2] * conj(Ab[1 3; -1]) end function transfer_right(v::MPSTensor, O::MPOTensor, A::MPSTensor, Ab::MPSTensor) - @plansor v[-1 -2; -3] := A[-1 2; 1] * O[-2 4; 2 3] * conj(Ab[-3 4; 5]) * v[1 3; 5] + return @plansor v[-1 -2; -3] := A[-1 2; 1] * O[-2 4; 2 3] * conj(Ab[-3 4; 5]) * v[1 3; 5] end #mpo transfer, but with A an excitation-tensor function transfer_left(v::MPSTensor, O::MPOTensor, A::MPOTensor, Ab::MPSTensor) - @plansor t[-1 -2; -3 -4] := v[4 2; 1] * A[1 3; -3 -4] * O[2 5; 3 -2] * conj(Ab[4 5; -1]) + return @plansor t[-1 -2; -3 -4] := v[4 2; 1] * A[1 3; -3 -4] * O[2 5; 3 -2] * conj(Ab[4 5; -1]) end function transfer_right(v::MPSTensor, O::MPOTensor, A::MPOTensor, Ab::MPSTensor) - @plansor t[-1 -2; -3 -4] := A[-1 4; -3 5] * O[-2 2; 4 3] * conj(Ab[-4 2; 1]) * v[5 3; 1] + return @plansor t[-1 -2; -3 -4] := A[-1 4; -3 5] * O[-2 2; 4 3] * conj(Ab[-4 2; 1]) * v[5 3; 1] end #mpo transfer, with an excitation leg function transfer_left(v::MPOTensor, O::MPOTensor, A::MPSTensor, Ab::MPSTensor) - @plansor v[-1 -2; -3 -4] := v[4 2; -3 1] * A[1 3; -4] * O[2 5; 3 -2] * conj(Ab[4 5; -1]) + return @plansor v[-1 -2; -3 -4] := v[4 2; -3 1] * A[1 3; -4] * O[2 5; 3 -2] * conj(Ab[4 5; -1]) end function transfer_right(v::MPOTensor, O::MPOTensor, A::MPSTensor, Ab::MPSTensor) - @plansor v[-1 -2; -3 -4] := A[-1 4; 5] * O[-2 2; 4 3] * - conj(Ab[-4 2; 1]) * v[5 3; -3 1] + return @plansor v[-1 -2; -3 -4] := A[-1 4; 5] * O[-2 2; 4 3] * + conj(Ab[-4 2; 1]) * v[5 3; -3 1] end # desity matrix transfer -function transfer_left(x::MPSTensor, O::MPOTensor, A::GenericMPSTensor{<:Any,3}, - Ab::GenericMPSTensor{<:Any,3}) - @plansor y[-1 -2; -3] ≔ x[1 2; 6] * A[6 7 8; -3] * O[2 3; 7 5] * τ[5 4; 8 -2] * - conj(Ab[1 3 4; -1]) -end -function transfer_right(x::MPSTensor, O::MPOTensor, A::GenericMPSTensor{<:Any,3}, - Ab::GenericMPSTensor{<:Any,3}) - @plansor y[-1 -2; -3] ≔ A[-1 4 2; 1] * O[-2 6; 4 5] * τ[5 7; 2 3] * - conj(Ab[-3 6 7; 8]) * x[1 3; 8] +function transfer_left( + x::MPSTensor, O::MPOTensor, A::GenericMPSTensor{<:Any, 3}, Ab::GenericMPSTensor{<:Any, 3} + ) + return @plansor y[-1 -2; -3] ≔ x[1 2; 6] * A[6 7 8; -3] * O[2 3; 7 5] * τ[5 4; 8 -2] * + conj(Ab[1 3 4; -1]) +end +function transfer_right( + x::MPSTensor, O::MPOTensor, A::GenericMPSTensor{<:Any, 3}, Ab::GenericMPSTensor{<:Any, 3} + ) + return @plansor y[-1 -2; -3] ≔ A[-1 4 2; 1] * O[-2 6; 4 5] * τ[5 7; 2 3] * + conj(Ab[-3 6 7; 8]) * x[1 3; 8] end diff --git a/src/transfermatrix/transfermatrix.jl b/src/transfermatrix/transfermatrix.jl index 5117c1c7d..88030e91d 100644 --- a/src/transfermatrix/transfermatrix.jl +++ b/src/transfermatrix/transfermatrix.jl @@ -1,8 +1,8 @@ abstract type AbstractTransferMatrix end; # single site transfer -struct SingleTransferMatrix{A<:AbstractTensorMap,B,C<:AbstractTensorMap} <: - AbstractTransferMatrix +struct SingleTransferMatrix{A <: AbstractTensorMap, B, C <: AbstractTensorMap} <: + AbstractTransferMatrix above::A middle::B below::C @@ -10,23 +10,23 @@ struct SingleTransferMatrix{A<:AbstractTensorMap,B,C<:AbstractTensorMap} <: end #the product of transfer matrices is its own type -struct ProductTransferMatrix{T<:AbstractTransferMatrix} <: AbstractTransferMatrix +struct ProductTransferMatrix{T <: AbstractTransferMatrix} <: AbstractTransferMatrix tms::Vector{T} # I don't want to use tuples, as an infinite mps transfer matrix will then be non-inferable end ProductTransferMatrix(v::AbstractVector) = ProductTransferMatrix(convert(Vector, v)); # a subset of possible operations, but certainly not all of them -function Base.:*(prod::ProductTransferMatrix{T}, tm::T) where {T<:AbstractTransferMatrix} +function Base.:*(prod::ProductTransferMatrix{T}, tm::T) where {T <: AbstractTransferMatrix} return ProductTransferMatrix(vcat(prod.tms, tm)) end; -function Base.:*(tm::T, prod::ProductTransferMatrix{T}) where {T<:AbstractTransferMatrix} +function Base.:*(tm::T, prod::ProductTransferMatrix{T}) where {T <: AbstractTransferMatrix} return ProductTransferMatrix(vcat(prod.tms, tm)) end; -Base.:*(tm1::T, tm2::T) where {T<:SingleTransferMatrix} = ProductTransferMatrix([tm1, tm2]) +Base.:*(tm1::T, tm2::T) where {T <: SingleTransferMatrix} = ProductTransferMatrix([tm1, tm2]) # regularized transfer matrices; where we project out after every full application -struct RegTransferMatrix{T<:AbstractTransferMatrix,L,R} <: AbstractTransferMatrix +struct RegTransferMatrix{T <: AbstractTransferMatrix, L, R} <: AbstractTransferMatrix tm::T lvec::L rvec::R @@ -44,7 +44,7 @@ Base.:*(tm::AbstractTransferMatrix, vec) = tm(vec); Base.:*(vec, tm::AbstractTransferMatrix) = flip(tm)(vec); # TransferMatrix acting as a function -(d::ProductTransferMatrix)(vec) = foldr((a, b) -> a(b), d.tms; init=vec); +(d::ProductTransferMatrix)(vec) = foldr((a, b) -> a(b), d.tms; init = vec); function (d::SingleTransferMatrix)(vec) return if d.isflipped transfer_left(vec, d.middle, d.above, d.below) @@ -57,10 +57,10 @@ end; # constructors TransferMatrix(a) = TransferMatrix(a, nothing, a); TransferMatrix(a, b) = TransferMatrix(a, nothing, b); -function TransferMatrix(a::AbstractTensorMap, b, c::AbstractTensorMap, isflipped=false) +function TransferMatrix(a::AbstractTensorMap, b, c::AbstractTensorMap, isflipped = false) return SingleTransferMatrix(a, b, c, isflipped) -end; -function TransferMatrix(a::AbstractVector, b, c::AbstractVector, isflipped=false) +end +function TransferMatrix(a::AbstractVector, b, c::AbstractVector, isflipped = false) tot = ProductTransferMatrix(convert(Vector, TransferMatrix.(a, b, c))) return isflipped ? flip(tot) : tot end @@ -68,23 +68,25 @@ end regularize(t::AbstractTransferMatrix, lvec, rvec) = RegTransferMatrix(t, lvec, rvec); function regularize!(v::MPSBondTensor, lvec::MPSBondTensor, rvec::MPSBondTensor) - @plansor v[-1; -2] -= lvec[1; 2] * v[2; 1] * rvec[-1; -2] + return @plansor v[-1; -2] -= lvec[1; 2] * v[2; 1] * rvec[-1; -2] end function regularize!(v::MPSTensor, lvec::MPSBondTensor, rvec::MPSBondTensor) - @plansor v[-1 -2; -3] -= lvec[1; 2] * v[2 -2; 1] * rvec[-1; -3] + return @plansor v[-1 -2; -3] -= lvec[1; 2] * v[2 -2; 1] * rvec[-1; -3] end -function regularize!(v::AbstractTensorMap{T,S,1,2} where {T,S}, lvec::MPSBondTensor, - rvec::MPSBondTensor) - @plansor v[-1; -2 -3] -= lvec[1; 2] * v[2; -2 1] * rvec[-1; -3] +function regularize!( + v::AbstractTensorMap{T, S, 1, 2} where {T, S}, lvec::MPSBondTensor, + rvec::MPSBondTensor + ) + return @plansor v[-1; -2 -3] -= lvec[1; 2] * v[2; -2 1] * rvec[-1; -3] end function regularize!(v::MPOTensor, lvec::MPSTensor, rvec::MPSTensor) - @plansor v[-1 -2; -3 -4] -= v[1 2; -3 3] * lvec[3 2; 1] * rvec[-1 -2; -4] + return @plansor v[-1 -2; -3 -4] -= v[1 2; -3 3] * lvec[3 2; 1] * rvec[-1 -2; -4] end function regularize!(v::MPOTensor, lvec::MPSBondTensor, rvec::MPSBondTensor) - @plansor v[-1 -2; -3 -4] -= τ[6 2; 3 4] * v[3 4; -3 5] * lvec[5; 2] * rvec[-1; 1] * - τ[-2 -4; 1 6] + return @plansor v[-1 -2; -3 -4] -= τ[6 2; 3 4] * v[3 4; -3 5] * lvec[5; 2] * rvec[-1; 1] * + τ[-2 -4; 1 6] end diff --git a/src/utility/defaults.jl b/src/utility/defaults.jl index 71b97a97d..b99f2e63f 100644 --- a/src/utility/defaults.jl +++ b/src/utility/defaults.jl @@ -18,56 +18,62 @@ const VERBOSE_ALL = 4 const eltype = ComplexF64 const maxiter = 200 -const tolgauge = 1e-13 -const tol = 1e-10 +const tolgauge = 1.0e-13 +const tol = 1.0e-10 const verbosity = VERBOSE_ITER const dynamic_tols = true -const tol_min = 1e-14 -const tol_max = 1e-4 -const eigs_tolfactor = 1e-3 -const gauge_tolfactor = 1e-6 -const envs_tolfactor = 1e-4 +const tol_min = 1.0e-14 +const tol_max = 1.0e-4 +const eigs_tolfactor = 1.0e-3 +const gauge_tolfactor = 1.0e-6 +const envs_tolfactor = 1.0e-4 const krylovdim = 30 _finalize(iter, state, opp, envs) = (state, envs) const linearsolver = GMRES(; tol, maxiter) -const eigsolver = Arnoldi(; tol, maxiter, eager=true) +const eigsolver = Arnoldi(; tol, maxiter, eager = true) # Default algorithms # ------------------ -function alg_gauge(; tol=tolgauge, maxiter=maxiter, verbosity=VERBOSE_WARN, - dynamic_tols=dynamic_tols, tol_min=tol_min, tol_max=tol_max, - tol_factor=gauge_tolfactor) +function alg_gauge(; + tol = tolgauge, maxiter = maxiter, verbosity = VERBOSE_WARN, + dynamic_tols = dynamic_tols, tol_min = tol_min, tol_max = tol_max, + tol_factor = gauge_tolfactor + ) alg = (; tol, maxiter, verbosity) return dynamic_tols ? DynamicTol(alg, tol_min, tol_max, tol_factor) : alg end -function alg_eigsolve(; ishermitian=true, tol=tol, maxiter=maxiter, verbosity=0, - eager=true, - krylovdim=krylovdim, - dynamic_tols=dynamic_tols, tol_min=tol_min, tol_max=tol_max, - tol_factor=eigs_tolfactor) +function alg_eigsolve(; + ishermitian = true, tol = tol, maxiter = maxiter, verbosity = 0, + eager = true, krylovdim = krylovdim, dynamic_tols = dynamic_tols, tol_min = tol_min, + tol_max = tol_max, tol_factor = eigs_tolfactor + ) alg = ishermitian ? Lanczos(; tol, maxiter, eager, krylovdim, verbosity) : - Arnoldi(; tol, maxiter, eager, krylovdim, verbosity) + Arnoldi(; tol, maxiter, eager, krylovdim, verbosity) return dynamic_tols ? DynamicTol(alg, tol_min, tol_max, tol_factor) : alg end alg_svd() = TensorKit.SDD() # TODO: make verbosity and maxiter actually do something -function alg_environments(; tol=tol, maxiter=maxiter, verbosity=0, - dynamic_tols=dynamic_tols, tol_min=tol_min, tol_max=tol_max, - tol_factor=envs_tolfactor) +function alg_environments(; + tol = tol, maxiter = maxiter, verbosity = 0, + dynamic_tols = dynamic_tols, tol_min = tol_min, tol_max = tol_max, + tol_factor = envs_tolfactor + ) alg = (; tol, maxiter, verbosity) return dynamic_tols ? DynamicTol(alg, tol_min, tol_max, tol_factor) : alg end -function alg_expsolve(; tol=tol, maxiter=maxiter, verbosity=0, - ishermitian=true, krylovdim=krylovdim) +function alg_expsolve(; + tol = tol, maxiter = maxiter, verbosity = 0, ishermitian = true, + krylovdim = krylovdim + ) return ishermitian ? Lanczos(; tol, maxiter, krylovdim, verbosity) : - Arnoldi(; tol, maxiter, krylovdim, verbosity) + Arnoldi(; tol, maxiter, krylovdim, verbosity) end """ @@ -86,7 +92,7 @@ Set the `OhMyThreads` multi-threading scheduler parameters. The function either accepts a `scheduler` as an `OhMyThreads.Scheduler` or as a symbol where the corresponding parameters are specificed as keyword arguments. For a detailed description of all schedulers and their keyword arguments consult the [`OhMyThreads` documentation](https://juliafolds2.github.io/OhMyThreads.jl/stable/refs/api/#Schedulers). """ -function set_scheduler!(sc=OhMyThreads.Implementation.NotGiven(); kwargs...) +function set_scheduler!(sc = OhMyThreads.Implementation.NotGiven(); kwargs...) if isempty(kwargs) && sc isa OhMyThreads.Implementation.NotGiven # default value: Serial if single-threaded, Dynamic otherwise scheduler[] = Threads.nthreads() == 1 ? SerialScheduler() : DynamicScheduler() diff --git a/src/utility/dynamictols.jl b/src/utility/dynamictols.jl index fedb06bf9..dc2a15781 100644 --- a/src/utility/dynamictols.jl +++ b/src/utility/dynamictols.jl @@ -31,20 +31,25 @@ See also [`updatetol`](@ref). struct DynamicTol{A} <: Algorithm "parent algorithm" alg::A + "minimal value of the dynamic tolerance" tol_min::Float64 + "maximal value of the dynamic tolerance" tol_max::Float64 + "tolerance factor for updating relative to current algorithm error" tol_factor::Float64 - function DynamicTol(alg::A, tol_min::Real, tol_max::Real, - tol_factor::Real) where {A} + + function DynamicTol( + alg::A, tol_min::Real, tol_max::Real, tol_factor::Real + ) where {A} 0 <= tol_min <= tol_max || throw(ArgumentError("tol_min must be between 0 and tol_max")) return new{A}(alg, tol_min, tol_max, tol_factor) end end -function DynamicTol(alg; tol_min=1e-6, tol_max=1e-2, tol_factor=0.1) +function DynamicTol(alg; tol_min = 1.0e-6, tol_max = 1.0e-2, tol_factor = 0.1) return DynamicTol(alg, tol_min, tol_max, tol_factor) end diff --git a/src/utility/iterativesolvers.jl b/src/utility/iterativesolvers.jl index dfbcca216..e74218544 100644 --- a/src/utility/iterativesolvers.jl +++ b/src/utility/iterativesolvers.jl @@ -1,12 +1,12 @@ # This file contains the definition of the IterativeSolver type and the solve! function. # Attempts to remove as much of the boilerplate code as possible from the iterative solvers. -mutable struct IterativeSolver{A,B} +mutable struct IterativeSolver{A, B} alg::A state::B end -function Base.getproperty(it::IterativeSolver{A,B}, name::Symbol) where {A,B} +function Base.getproperty(it::IterativeSolver{A, B}, name::Symbol) where {A, B} name === :alg || name === :state && return getfield(it, name) alg = getfield(it, :alg) diff --git a/src/utility/linearcombination.jl b/src/utility/linearcombination.jl index 9fb3d375e..e2e8ee4c7 100644 --- a/src/utility/linearcombination.jl +++ b/src/utility/linearcombination.jl @@ -1,4 +1,4 @@ -struct LinearCombination{O<:Tuple,C<:Tuple} +struct LinearCombination{O <: Tuple, C <: Tuple} opps::O coeffs::C end diff --git a/src/utility/logging.jl b/src/utility/logging.jl index 6556b6a0d..d4b190beb 100644 --- a/src/utility/logging.jl +++ b/src/utility/logging.jl @@ -13,7 +13,7 @@ mutable struct IterLog name::AbstractString iter::Int error::Float64 - objective::Union{Nothing,Number} + objective::Union{Nothing, Number} t_init::Float64 t_prev::Float64 @@ -21,7 +21,7 @@ mutable struct IterLog state::LogState end -function IterLog(name="") +function IterLog(name = "") t = Base.time() return IterLog(name, 0, NaN, nothing, t, t, t, INIT) end @@ -29,11 +29,12 @@ end # Input # ----- -isapproxreal(x::Number) = isreal(x) || isapprox(imag(x), 0; atol=eps(abs(x))^(3 / 4)) +isapproxreal(x::Number) = isreal(x) || isapprox(imag(x), 0; atol = eps(abs(x))^(3 / 4)) warnapproxreal(x::Number) = isapproxreal(x) || @warn "Objective has imaginary part: $x" -function loginit!(log::IterLog, error::Float64, - objective::Union{Nothing,Number}=nothing) +function loginit!( + log::IterLog, error::Float64, objective::Union{Nothing, Number} = nothing + ) log.iter = 0 log.error = error log.objective = objective @@ -44,8 +45,9 @@ function loginit!(log::IterLog, error::Float64, return log end -function logiter!(log::IterLog, iter::Int, error::Float64, - objective::Union{Nothing,Number}=nothing) +function logiter!( + log::IterLog, iter::Int, error::Float64, objective::Union{Nothing, Number} = nothing + ) log.iter = iter log.error = error log.objective = objective @@ -57,8 +59,9 @@ function logiter!(log::IterLog, iter::Int, error::Float64, return log end -function logfinish!(log::IterLog, iter::Int, error::Float64, - objective::Union{Nothing,Number}=nothing) +function logfinish!( + log::IterLog, iter::Int, error::Float64, objective::Union{Nothing, Number} = nothing + ) log.iter = iter log.error = error log.objective = objective @@ -70,8 +73,9 @@ function logfinish!(log::IterLog, iter::Int, error::Float64, return log end -function logcancel!(log::IterLog, iter::Int, error::Float64, - objective::Union{Nothing,Number}=nothing) +function logcancel!( + log::IterLog, iter::Int, error::Float64, objective::Union{Nothing, Number} = nothing + ) log.iter = iter log.error = error log.objective = objective @@ -88,8 +92,8 @@ end function format_time(t::Float64) return t < 60 ? @sprintf("%0.2f sec", t) : - t < 2600 ? @sprintf("%0.2f min", t / 60) : - @sprintf("%0.2f hr", t / 3600) + t < 2600 ? @sprintf("%0.2f min", t / 60) : + @sprintf("%0.2f hr", t / 3600) end function format_objective(t::Number) diff --git a/src/utility/multiline.jl b/src/utility/multiline.jl index 9219e0f8f..549766ee9 100644 --- a/src/utility/multiline.jl +++ b/src/utility/multiline.jl @@ -10,7 +10,7 @@ multiple lines of `InfiniteMPS` (`MultilineMPS`) or MPO (`Multiline{<:AbstractMP See also: [`MultilineMPS`](@ref) and [`MultilineMPO`](@ref) """ struct Multiline{T} - data::PeriodicArray{T,1} + data::PeriodicArray{T, 1} function Multiline{T}(data::AbstractVector{T}) where {T} @assert allequal(length.(data)) "All lines must have the same length" return new{T}(data) @@ -26,7 +26,7 @@ Base.size(m::Multiline, i::Int) = getindex(size(m), i) Base.length(m::Multiline) = prod(size(m)) function Base.axes(m::Multiline, i::Int) return i == 1 ? axes(parent(m), 1) : - i == 2 ? axes(parent(m)[1], 1) : throw(ArgumentError("Invalid index $i")) + i == 2 ? axes(parent(m)[1], 1) : throw(ArgumentError("Invalid index $i")) end Base.eachindex(m::Multiline) = CartesianIndices(size(m)) @@ -41,7 +41,7 @@ Base.iterate(m::Multiline, args...) = iterate(parent(m), args...) # Utility functions # ----------------- Base.circshift(A::Multiline, n::Int) = Multiline(circshift(parent(A), n)) -function Base.circshift(A::Multiline, shifts::Tuple{Int,Int}) +function Base.circshift(A::Multiline, shifts::Tuple{Int, Int}) data′ = circshift.(parent(A), shifts[2]) return Multiline(circshift!(data′, shifts[1])) end @@ -58,7 +58,7 @@ end # --------------- VectorInterface.scalartype(::Type{Multiline{T}}) where {T} = scalartype(T) -function VectorInterface.zerovector(x::Multiline, ::Type{S}) where {S<:Number} +function VectorInterface.zerovector(x::Multiline, ::Type{S}) where {S <: Number} return Multiline(zerovector.(parent(x), S)) end VectorInterface.zerovector!(x::Multiline) = (zerovector!.(parent(x)); x) diff --git a/src/utility/periodicarray.jl b/src/utility/periodicarray.jl index 3cdb912b7..14ce75b65 100644 --- a/src/utility/periodicarray.jl +++ b/src/utility/periodicarray.jl @@ -26,16 +26,16 @@ A[-1, 1], A[1, 1], A[4, 5] See also [`PeriodicVector`](@ref), [`PeriodicMatrix`](@ref) """ -struct PeriodicArray{T,N} <: AbstractArray{T,N} - data::Array{T,N} +struct PeriodicArray{T, N} <: AbstractArray{T, N} + data::Array{T, N} end -PeriodicArray(data::AbstractArray{T,N}) where {T,N} = PeriodicArray{T,N}(data) -PeriodicArray{T}(data::AbstractArray{T,N}) where {T,N} = PeriodicArray{T,N}(data) +PeriodicArray(data::AbstractArray{T, N}) where {T, N} = PeriodicArray{T, N}(data) +PeriodicArray{T}(data::AbstractArray{T, N}) where {T, N} = PeriodicArray{T, N}(data) function PeriodicArray{T}(initializer, args...) where {T} return PeriodicArray(Array{T}(initializer, args...)) end -function PeriodicArray{T,N}(initializer, args...) where {T,N} - return PeriodicArray(Array{T,N}(initializer, args...)) +function PeriodicArray{T, N}(initializer, args...) where {T, N} + return PeriodicArray(Array{T, N}(initializer, args...)) end """ @@ -44,7 +44,7 @@ end One-dimensional dense array with elements of type `T` and periodic boundary conditions. Alias for [`PeriodicArray{T,1}`](@ref). """ -const PeriodicVector{T} = PeriodicArray{T,1} +const PeriodicVector{T} = PeriodicArray{T, 1} PeriodicVector(data::AbstractVector{T}) where {T} = PeriodicVector{T}(data) """ @@ -53,7 +53,7 @@ PeriodicVector(data::AbstractVector{T}) where {T} = PeriodicVector{T}(data) Two-dimensional dense array with elements of type `T` and periodic boundary conditions. Alias for [`PeriodicArray{T,2}`](@ref). """ -const PeriodicMatrix{T} = PeriodicArray{T,2} +const PeriodicMatrix{T} = PeriodicArray{T, 2} PeriodicMatrix(data::AbstractMatrix{T}) where {T} = PeriodicMatrix{T}(data) Base.parent(A::PeriodicArray) = A.data @@ -62,11 +62,11 @@ Base.parent(A::PeriodicArray) = A.data # ----------------------- Base.size(A::PeriodicArray) = size(parent(A)) -function Base.getindex(A::PeriodicArray{T,N}, I::Vararg{Int,N}) where {T,N} - @inbounds getindex(parent(A), map(mod1, I, size(A))...) +function Base.getindex(A::PeriodicArray{T, N}, I::Vararg{Int, N}) where {T, N} + return @inbounds getindex(parent(A), map(mod1, I, size(A))...) end -function Base.setindex!(A::PeriodicArray{T,N}, v, I::Vararg{Int,N}) where {T,N} - @inbounds setindex!(parent(A), v, map(mod1, I, size(A))...) +function Base.setindex!(A::PeriodicArray{T, N}, v, I::Vararg{Int, N}) where {T, N} + return @inbounds setindex!(parent(A), v, map(mod1, I, size(A))...) end Base.checkbounds(A::PeriodicArray, I...) = true @@ -85,17 +85,18 @@ end # Broadcasting # ------------ -Base.BroadcastStyle(::Type{T}) where {T<:PeriodicArray} = Broadcast.ArrayStyle{T}() +Base.BroadcastStyle(::Type{T}) where {T <: PeriodicArray} = Broadcast.ArrayStyle{T}() -function Base.similar(bc::Broadcast.Broadcasted{<:Broadcast.ArrayStyle{<:PeriodicArray}}, - ::Type{T}) where {T} +function Base.similar( + bc::Broadcast.Broadcasted{<:Broadcast.ArrayStyle{<:PeriodicArray}}, ::Type{T} + ) where {T} return PeriodicArray(similar(Array{T}, axes(bc))) end # Conversion # ---------- -Base.convert(::Type{T}, A::AbstractArray) where {T<:PeriodicArray} = T(A) -Base.convert(::Type{T}, A::PeriodicArray) where {T<:AbstractArray} = convert(T, parent(A)) +Base.convert(::Type{T}, A::AbstractArray) where {T <: PeriodicArray} = T(A) +Base.convert(::Type{T}, A::PeriodicArray) where {T <: AbstractArray} = convert(T, parent(A)) # fix ambiguities -Base.convert(::Type{T}, A::PeriodicArray) where {T<:PeriodicArray} = A -Base.convert(::Type{T}, A::PeriodicArray) where {T<:Array} = parent(A) +Base.convert(::Type{T}, A::PeriodicArray) where {T <: PeriodicArray} = A +Base.convert(::Type{T}, A::PeriodicArray) where {T <: Array} = parent(A) diff --git a/src/utility/plotting.jl b/src/utility/plotting.jl index f8be50082..9265311fa 100644 --- a/src/utility/plotting.jl +++ b/src/utility/plotting.jl @@ -25,8 +25,10 @@ Plot the [entanglement spectrum](@ref entanglement_spectrum) of a given MPS `sta function entanglementplot end @userplot EntanglementPlot -@recipe function f(h::EntanglementPlot; site=0, expand_symmetry=false, sortby=maximum, - sector_margin=1 // 10, sector_formatter=string) +@recipe function f( + h::EntanglementPlot; site = 0, expand_symmetry = false, sortby = maximum, + sector_margin = 1 // 10, sector_formatter = string + ) mps = h.args[1] (site <= length(mps) && !(isa(mps, FiniteMPS) && site == 0)) || throw(ArgumentError("Invalid site $site for the given mps.")) @@ -37,7 +39,7 @@ function entanglementplot end for (c, b) in spectra if expand_symmetry # Duplicate entries according to the quantum dimension. b′ = repeat(b, dim(c)) - sort!(b′; rev=true) + sort!(b′; rev = true) push!(spectrum, b′) else push!(spectrum, b) @@ -46,7 +48,7 @@ function entanglementplot end end if length(spectrum) > 1 - order = sortperm(spectrum; by=sortby, rev=true) + order = sortperm(spectrum; by = sortby, rev = true) spectrum = spectrum[order] sectors = sectors[order] end @@ -61,7 +63,7 @@ function entanglementplot end if n_spectrum == 1 x = [i + 1 // 2] else - x = range(i + sector_margin, i + 1 - sector_margin; length=n_spectrum) + x = range(i + sector_margin, i + 1 - sector_margin; length = n_spectrum) end return x, partial_spectrum end @@ -79,7 +81,7 @@ function entanglementplot end xrotation --> 45 xlims --> (1, length(sectors) + 1) - ylims --> (-Inf, 1 + 1e-1) + ylims --> (-Inf, 1 + 1.0e-1) yscale --> :log10 label := nothing @@ -112,16 +114,20 @@ Plot the partial transfer matrix spectrum of two InfiniteMPS's. function transferplot end @userplot TransferPlot -@recipe function f(h::TransferPlot; sectors=nothing, transferkwargs=(;), thetaorigin=0, - sector_formatter=string) +@recipe function f( + h::TransferPlot; sectors = nothing, transferkwargs = (;), thetaorigin = 0, + sector_formatter = string + ) if sectors === nothing sectors = [one(sectortype(h.args[1]))] end for sector in sectors below = length(h.args) == 1 ? h.args[1] : h.args[2] - spectrum = transfer_spectrum(h.args[1]; below=below, sector=sector, - transferkwargs...) + spectrum = transfer_spectrum( + h.args[1]; below = below, sector = sector, + transferkwargs... + ) @series begin yguide --> "r" @@ -129,8 +135,8 @@ function transferplot end xguide --> "θ" xlims --> (thetaorigin, thetaorigin + 2pi) - xticks --> range(0, 2pi; length=7) - xformatter --> x -> "$(rationalize(x/π, tol=0.05))π" + xticks --> range(0, 2pi; length = 7) + xformatter --> x -> "$(rationalize(x / π, tol = 0.05))π" xwiden --> true seriestype := :scatter markershape --> :auto diff --git a/src/utility/utility.jl b/src/utility/utility.jl index ee7daab17..93181d667 100644 --- a/src/utility/utility.jl +++ b/src/utility/utility.jl @@ -21,28 +21,32 @@ _firstspace(t::AbstractTensorMap) = space(t, 1) _lastspace(t::AbstractTensorMap) = space(t, numind(t)) #given a hamiltonian with unit legs on the side, decompose it using svds to form a "localmpo" -function decompose_localmpo(inpmpo::AbstractTensorMap{T,PS,N,N}, - trunc=truncbelow(Defaults.tol)) where {T,PS,N} +function decompose_localmpo( + inpmpo::AbstractTensorMap{T, PS, N, N}, trunc = truncbelow(Defaults.tol) + ) where {T, PS, N} N == 2 && return [inpmpo] leftind = (N + 1, 1, 2) rightind = (ntuple(x -> x + N + 1, N - 1)..., reverse(ntuple(x -> x + 2, N - 2))...) - U, S, V = tsvd(transpose(inpmpo, (leftind, rightind)); trunc=trunc) + U, S, V = tsvd(transpose(inpmpo, (leftind, rightind)); trunc = trunc) A = transpose(U * S, ((2, 3), (1, 4))) - B = transpose(V, - ((1, reverse(ntuple(x -> x + N, N - 2))...), ntuple(x -> x + 1, N - 1))) + B = transpose( + V, + ((1, reverse(ntuple(x -> x + N, N - 2))...), ntuple(x -> x + 1, N - 1)) + ) return [A; decompose_localmpo(B)] end # given a state with util legs on the side, decompose using svds to form an array of mpstensors -function decompose_localmps(state::AbstractTensorMap{T,PS,N,1}, - trunc=truncbelow(Defaults.tol)) where {T,PS,N} +function decompose_localmps( + state::AbstractTensorMap{T, PS, N, 1}, trunc = truncbelow(Defaults.tol) + ) where {T, PS, N} N == 2 && return [state] leftind = (1, 2) rightind = reverse(ntuple(x -> x + 2, N - 1)) - U, S, V = tsvd(transpose(state, (leftind, rightind)); trunc=trunc) + U, S, V = tsvd(transpose(state, (leftind, rightind)); trunc = trunc) A = U * S B = _transpose_front(V) @@ -56,7 +60,7 @@ end Add trivial one-dimensional utility spaces with trivial sector to the left and right of a given tensor map, i.e. as the first space of the codomain and the last space of the domain. """ -function add_util_leg(tensor::AbstractTensorMap{T,S,N1,N2}) where {T,S,N1,N2} +function add_util_leg(tensor::AbstractTensorMap{T, S, N1, N2}) where {T, S, N1, N2} ou = oneunit(_firstspace(tensor)) util_front = isomorphism(storagetype(tensor), ou * codomain(tensor), codomain(tensor)) @@ -66,7 +70,7 @@ function add_util_leg(tensor::AbstractTensorMap{T,S,N1,N2}) where {T,S,N1,N2} end function union_split(a::AbstractArray) - T = reduce((a, b) -> Union{a,b}, typeof.(a)) + T = reduce((a, b) -> Union{a, b}, typeof.(a)) nA = similar(a, T) return copy!(nA, a) end @@ -118,8 +122,9 @@ of the form `name[ind_out...; ind_in]`. """ tensorexpr(name, inds) = Expr(:ref, name, _totuple(inds)...) function tensorexpr(name, indout, indin) - return Expr(:typed_vcat, name, Expr(:row, _totuple(indout)...), - Expr(:row, _totuple(indin)...)) + return Expr( + :typed_vcat, name, Expr(:row, _totuple(indout)...), Expr(:row, _totuple(indin)...) + ) end function check_length(a, b...) @@ -128,7 +133,7 @@ function check_length(a, b...) return L end -function fuser(::Type{T}, V1::S, V2::S) where {T,S<:IndexSpace} +function fuser(::Type{T}, V1::S, V2::S) where {T, S <: IndexSpace} return isomorphism(T, fuse(V1 ⊗ V2), V1 ⊗ V2) end @@ -147,5 +152,5 @@ function check_unambiguous_braiding(::Type{Bool}, V::VectorSpace) end function check_unambiguous_braiding(V::VectorSpace) return check_unambiguous_braiding(Bool, V) || - throw(ArgumentError("cannot unambiguously braid $V")) + throw(ArgumentError("cannot unambiguously braid $V")) end diff --git a/src/utility/windowarray.jl b/src/utility/windowarray.jl index 60f742ab9..3dcaa686a 100644 --- a/src/utility/windowarray.jl +++ b/src/utility/windowarray.jl @@ -14,8 +14,9 @@ struct WindowArray{T} <: AbstractVector{T} middle::Vector{T} right::PeriodicVector{T} end -function WindowArray(left::PeriodicVector{T}, middle::AbstractVector{T}, - right::PeriodicVector{T}) where {T} +function WindowArray( + left::PeriodicVector{T}, middle::AbstractVector{T}, right::PeriodicVector{T} + ) where {T} return WindowArray{T}(left, convert(Vector{T}, middle), right) end @@ -25,16 +26,16 @@ Base.size(window::WindowArray) = size(window.middle) Base.axes(window::WindowArray) = axes(window.middle) function Base.getindex(window::WindowArray, i::Int) - if i < 1 - return window.left[end + i] + return if i < 1 + window.left[end + i] elseif i > length(window.middle) - return window.right[i - length(window.middle)] + window.right[i - length(window.middle)] else - return window.middle[i] + window.middle[i] end end function Base.setindex!(window::WindowArray, value, i::Int) - if i < 1 + return if i < 1 window.left[end + i] = value elseif i > length(window.middle) window.right[i - length(window.middle)] = value @@ -46,17 +47,21 @@ end Base.checkbounds(::Type{Bool}, window::WindowArray, i::Int) = true function Base.similar(window::WindowArray, ::Type{S}, l::Int) where {S} - return WindowArray(similar(window.left, S), - similar(window.middle, S, l), - similar(window.right, S)) + return WindowArray( + similar(window.left, S), similar(window.middle, S, l), similar(window.right, S) + ) end function Base.LinearIndices(window::WindowArray) - return WindowArray(LinearIndices(window.left) .- length(window.left), - LinearIndices(window.middle), - LinearIndices(window.right) .+ length(window.middle)) + return WindowArray( + LinearIndices(window.left) .- length(window.left), + LinearIndices(window.middle), + LinearIndices(window.right) .+ length(window.middle) + ) end function Base.CartesianIndices(window::WindowArray) - return WindowArray(CartesianIndices(window.left), - CartesianIndices(window.middle), - CartesianIndices(window.right)) + return WindowArray( + CartesianIndices(window.left), + CartesianIndices(window.middle), + CartesianIndices(window.right) + ) end diff --git a/test/algorithms.jl b/test/algorithms.jl index 4a10af9d3..469506e39 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -5,1002 +5,1013 @@ println(" ") module TestAlgorithms -using ..TestSetup -using Test, TestExtras -using MPSKit -using MPSKit: fuse_mul_mpo -using TensorKit -using TensorKit: ℙ -using LinearAlgebra: eigvals - -verbosity_full = 5 -verbosity_conv = 1 - -@testset "FiniteMPS groundstate" verbose = true begin - tol = 1e-8 - g = 4.0 - D = 6 - L = 10 - - H = force_planar(transverse_field_ising(; g, L)) - - @testset "DMRG" begin - ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^D) - v₀ = variance(ψ₀, H) - - # test logging - ψ, envs, δ = find_groundstate(ψ₀, H, - DMRG(; verbosity=verbosity_full, maxiter=2)) - - ψ, envs, δ = find_groundstate(ψ, H, - DMRG(; verbosity=verbosity_conv, maxiter=10), - envs) - v = variance(ψ, H) - - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ && v < 1e-2 - end + using ..TestSetup + using Test, TestExtras + using MPSKit + using MPSKit: fuse_mul_mpo + using TensorKit + using TensorKit: ℙ + using LinearAlgebra: eigvals + + verbosity_full = 5 + verbosity_conv = 1 + + @testset "FiniteMPS groundstate" verbose = true begin + tol = 1.0e-8 + g = 4.0 + D = 6 + L = 10 - @testset "DMRG2" begin - ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℙ^2, ℙ^D) - v₀ = variance(ψ₀, H) - trscheme = truncdim(floor(Int, D * 1.5)) - # test logging - ψ, envs, δ = find_groundstate(ψ₀, H, - DMRG2(; verbosity=verbosity_full, maxiter=2, - trscheme)) - - ψ, envs, δ = find_groundstate(ψ, H, - DMRG2(; verbosity=verbosity_conv, maxiter=10, - trscheme), envs) - v = variance(ψ, H) - - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ && v < 1e-2 - end + H = force_planar(transverse_field_ising(; g, L)) + + @testset "DMRG" begin + ψ₀ = FiniteMPS(randn, ComplexF64, L, ℙ^2, ℙ^D) + v₀ = variance(ψ₀, H) + + # test logging + ψ, envs, δ = find_groundstate( + ψ₀, H, DMRG(; verbosity = verbosity_full, maxiter = 2) + ) + + ψ, envs, δ = find_groundstate( + ψ, H, DMRG(; verbosity = verbosity_conv, maxiter = 10), envs + ) + v = variance(ψ, H) + + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ && v < 1.0e-2 + end - @testset "GradientGrassmann" begin - ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℙ^2, ℙ^D) - v₀ = variance(ψ₀, H) + @testset "DMRG2" begin + ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℙ^2, ℙ^D) + v₀ = variance(ψ₀, H) + trscheme = truncdim(floor(Int, D * 1.5)) + # test logging + ψ, envs, δ = find_groundstate( + ψ₀, H, DMRG2(; verbosity = verbosity_full, maxiter = 2, trscheme) + ) + + ψ, envs, δ = find_groundstate( + ψ, H, DMRG2(; verbosity = verbosity_conv, maxiter = 10, trscheme), envs + ) + v = variance(ψ, H) + + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ && v < 1.0e-2 + end + + @testset "GradientGrassmann" begin + ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℙ^2, ℙ^D) + v₀ = variance(ψ₀, H) - # test logging - ψ, envs, δ = find_groundstate(ψ₀, H, - GradientGrassmann(; verbosity=verbosity_full, - maxiter=2)) + # test logging + ψ, envs, δ = find_groundstate( + ψ₀, H, GradientGrassmann(; verbosity = verbosity_full, maxiter = 2) + ) - ψ, envs, δ = find_groundstate(ψ, H, - GradientGrassmann(; verbosity=verbosity_conv, - maxiter=50), - envs) - v = variance(ψ, H) + ψ, envs, δ = find_groundstate( + ψ, H, GradientGrassmann(; verbosity = verbosity_conv, maxiter = 50), envs + ) + v = variance(ψ, H) - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ && v < 1e-2 + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ && v < 1.0e-2 + end end -end -@testset "InfiniteMPS groundstate" verbose = true begin - tol = 1e-8 - g = 4.0 - D = 6 + @testset "InfiniteMPS groundstate" verbose = true begin + tol = 1.0e-8 + g = 4.0 + D = 6 - H_ref = force_planar(transverse_field_ising(; g)) - ψ = InfiniteMPS(ℙ^2, ℙ^D) - v₀ = variance(ψ, H_ref) + H_ref = force_planar(transverse_field_ising(; g)) + ψ = InfiniteMPS(ℙ^2, ℙ^D) + v₀ = variance(ψ, H_ref) - @testset "VUMPS" for unit_cell_size in [1, 3] - ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) - H = repeat(H_ref, unit_cell_size) + @testset "VUMPS" for unit_cell_size in [1, 3] + ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) + H = repeat(H_ref, unit_cell_size) - # test logging - ψ, envs, δ = find_groundstate(ψ, H, - VUMPS(; tol, verbosity=verbosity_full, maxiter=2)) + # test logging + ψ, envs, δ = find_groundstate( + ψ, H, VUMPS(; tol, verbosity = verbosity_full, maxiter = 2) + ) - ψ, envs, δ = find_groundstate(ψ, H, VUMPS(; tol, verbosity=verbosity_conv)) - v = variance(ψ, H, envs) + ψ, envs, δ = find_groundstate(ψ, H, VUMPS(; tol, verbosity = verbosity_conv)) + v = variance(ψ, H, envs) - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ - @test v < 1e-2 - end + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ + @test v < 1.0e-2 + end - @testset "IDMRG" for unit_cell_size in [1, 3] - ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) - H = repeat(H_ref, unit_cell_size) + @testset "IDMRG" for unit_cell_size in [1, 3] + ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) + H = repeat(H_ref, unit_cell_size) - # test logging - ψ, envs, δ = find_groundstate(ψ, H, - IDMRG(; tol, verbosity=verbosity_full, maxiter=2)) + # test logging + ψ, envs, δ = find_groundstate( + ψ, H, IDMRG(; tol, verbosity = verbosity_full, maxiter = 2) + ) - ψ, envs, δ = find_groundstate(ψ, H, IDMRG(; tol, verbosity=verbosity_conv)) - v = variance(ψ, H, envs) + ψ, envs, δ = find_groundstate(ψ, H, IDMRG(; tol, verbosity = verbosity_conv)) + v = variance(ψ, H, envs) - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ - @test v < 1e-2 - end + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ + @test v < 1.0e-2 + end - @testset "IDMRG2" begin - ψ = repeat(InfiniteMPS(ℙ^2, ℙ^D), 2) - H = repeat(H_ref, 2) + @testset "IDMRG2" begin + ψ = repeat(InfiniteMPS(ℙ^2, ℙ^D), 2) + H = repeat(H_ref, 2) - trscheme = truncbelow(1e-8) + trscheme = truncbelow(1.0e-8) - # test logging - ψ, envs, δ = find_groundstate(ψ, H, - IDMRG2(; tol, verbosity=verbosity_full, maxiter=2, - trscheme)) + # test logging + ψ, envs, δ = find_groundstate( + ψ, H, IDMRG2(; tol, verbosity = verbosity_full, maxiter = 2, trscheme) + ) - ψ, envs, δ = find_groundstate(ψ, H, - IDMRG2(; tol, verbosity=verbosity_conv, trscheme)) - v = variance(ψ, H, envs) + ψ, envs, δ = find_groundstate( + ψ, H, IDMRG2(; tol, verbosity = verbosity_conv, trscheme) + ) + v = variance(ψ, H, envs) - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ - @test v < 1e-2 - end + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ + @test v < 1.0e-2 + end - @testset "GradientGrassmann" for unit_cell_size in [1, 3] - ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) - H = repeat(H_ref, unit_cell_size) + @testset "GradientGrassmann" for unit_cell_size in [1, 3] + ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) + H = repeat(H_ref, unit_cell_size) - # test logging - ψ, envs, δ = find_groundstate(ψ, H, - GradientGrassmann(; tol, verbosity=verbosity_full, - maxiter=2)) + # test logging + ψ, envs, δ = find_groundstate( + ψ, H, GradientGrassmann(; tol, verbosity = verbosity_full, maxiter = 2) + ) - ψ, envs, δ = find_groundstate(ψ, H, - GradientGrassmann(; tol, verbosity=verbosity_conv)) - v = variance(ψ, H, envs) + ψ, envs, δ = find_groundstate( + ψ, H, GradientGrassmann(; tol, verbosity = verbosity_conv) + ) + v = variance(ψ, H, envs) - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ - @test v < 1e-2 - end + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ + @test v < 1.0e-2 + end - @testset "Combination" for unit_cell_size in [1, 3] - ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) - H = repeat(H_ref, unit_cell_size) + @testset "Combination" for unit_cell_size in [1, 3] + ψ = unit_cell_size == 1 ? InfiniteMPS(ℙ^2, ℙ^D) : repeat(ψ, unit_cell_size) + H = repeat(H_ref, unit_cell_size) - alg = VUMPS(; tol=100 * tol, verbosity=verbosity_conv, maxiter=10) & - GradientGrassmann(; tol, verbosity=verbosity_conv, maxiter=50) - ψ, envs, δ = find_groundstate(ψ, H, alg) + alg = VUMPS(; tol = 100 * tol, verbosity = verbosity_conv, maxiter = 10) & + GradientGrassmann(; tol, verbosity = verbosity_conv, maxiter = 50) + ψ, envs, δ = find_groundstate(ψ, H, alg) - v = variance(ψ, H, envs) + v = variance(ψ, H, envs) - # test using low variance - @test sum(δ) ≈ 0 atol = 1e-3 - @test v < v₀ - @test v < 1e-2 + # test using low variance + @test sum(δ) ≈ 0 atol = 1.0e-3 + @test v < v₀ + @test v < 1.0e-2 + end end -end -@testset "LazySum FiniteMPS groundstate" verbose = true begin - tol = 1e-8 - D = 15 - atol = 1e-2 - L = 10 + @testset "LazySum FiniteMPS groundstate" verbose = true begin + tol = 1.0e-8 + D = 15 + atol = 1.0e-2 + L = 10 - # test using XXZ model, Δ > 1 is gapped - spin = 1 - local_operators = [S_xx(; spin), S_yy(; spin), 1.7 * S_zz(; spin)] - Pspace = space(local_operators[1], 1) - lattice = fill(Pspace, L) + # test using XXZ model, Δ > 1 is gapped + spin = 1 + local_operators = [S_xx(; spin), S_yy(; spin), 1.7 * S_zz(; spin)] + Pspace = space(local_operators[1], 1) + lattice = fill(Pspace, L) - mpo_hamiltonians = map(local_operators) do O - return FiniteMPOHamiltonian(lattice, (i, i + 1) => O for i in 1:(L - 1)) - end + mpo_hamiltonians = map(local_operators) do O + return FiniteMPOHamiltonian(lattice, (i, i + 1) => O for i in 1:(L - 1)) + end - H_lazy = LazySum(mpo_hamiltonians) - H = sum(H_lazy) + H_lazy = LazySum(mpo_hamiltonians) + H = sum(H_lazy) - ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℂ^3, ℂ^D) - ψ₀, = find_groundstate(ψ₀, H; tol, verbosity=1) + ψ₀ = FiniteMPS(randn, ComplexF64, 10, ℂ^3, ℂ^D) + ψ₀, = find_groundstate(ψ₀, H; tol, verbosity = 1) - @testset "DMRG" begin - # test logging passes - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, - DMRG(; tol, verbosity=verbosity_full, maxiter=1)) + @testset "DMRG" begin + # test logging passes + ψ, envs, δ = find_groundstate( + ψ₀, H_lazy, DMRG(; tol, verbosity = verbosity_full, maxiter = 1) + ) - # compare states - alg = DMRG(; tol, verbosity=verbosity_conv) - ψ, envs, δ = find_groundstate(ψ, H_lazy, alg) + # compare states + alg = DMRG(; tol, verbosity = verbosity_conv) + ψ, envs, δ = find_groundstate(ψ, H_lazy, alg) - @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol - end + @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol + end - @testset "DMRG2" begin - # test logging passes - trscheme = truncdim(floor(Int, D * 1.5)) - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, - DMRG2(; tol, verbosity=verbosity_full, maxiter=1, - trscheme)) + @testset "DMRG2" begin + # test logging passes + trscheme = truncdim(floor(Int, D * 1.5)) + ψ, envs, δ = find_groundstate( + ψ₀, H_lazy, DMRG2(; tol, verbosity = verbosity_full, maxiter = 1, trscheme) + ) - # compare states - alg = DMRG2(; tol, verbosity=verbosity_conv, trscheme) - ψ, = find_groundstate(ψ₀, H, alg) - ψ_lazy, envs, δ = find_groundstate(ψ₀, H_lazy, alg) + # compare states + alg = DMRG2(; tol, verbosity = verbosity_conv, trscheme) + ψ, = find_groundstate(ψ₀, H, alg) + ψ_lazy, envs, δ = find_groundstate(ψ₀, H_lazy, alg) - @test abs(dot(ψ₀, ψ_lazy)) ≈ 1 atol = atol - end + @test abs(dot(ψ₀, ψ_lazy)) ≈ 1 atol = atol + end - @testset "GradientGrassmann" begin - # test logging passes - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, - GradientGrassmann(; tol, verbosity=verbosity_full, - maxiter=2)) + @testset "GradientGrassmann" begin + # test logging passes + ψ, envs, δ = find_groundstate( + ψ₀, H_lazy, GradientGrassmann(; tol, verbosity = verbosity_full, maxiter = 2) + ) - # compare states - alg = GradientGrassmann(; tol, verbosity=verbosity_conv) - ψ, = find_groundstate(ψ₀, H, alg) - ψ_lazy, envs, δ = find_groundstate(ψ₀, H_lazy, alg) + # compare states + alg = GradientGrassmann(; tol, verbosity = verbosity_conv) + ψ, = find_groundstate(ψ₀, H, alg) + ψ_lazy, envs, δ = find_groundstate(ψ₀, H_lazy, alg) - @test abs(dot(ψ₀, ψ_lazy)) ≈ 1 atol = atol + @test abs(dot(ψ₀, ψ_lazy)) ≈ 1 atol = atol + end end -end -@testset "LazySum InfiniteMPS groundstate" verbose = true begin - tol = 1e-8 - D = 16 - atol = 1e-2 - - spin = 1 - local_operators = [S_xx(; spin), S_yy(; spin), 0.7 * S_zz(; spin)] - Pspace = space(local_operators[1], 1) - lattice = PeriodicVector([Pspace]) - mpo_hamiltonians = map(local_operators) do O - return InfiniteMPOHamiltonian(lattice, (1, 2) => O) - end + @testset "LazySum InfiniteMPS groundstate" verbose = true begin + tol = 1.0e-8 + D = 16 + atol = 1.0e-2 - H_lazy = LazySum(mpo_hamiltonians) - H = sum(H_lazy) + spin = 1 + local_operators = [S_xx(; spin), S_yy(; spin), 0.7 * S_zz(; spin)] + Pspace = space(local_operators[1], 1) + lattice = PeriodicVector([Pspace]) + mpo_hamiltonians = map(local_operators) do O + return InfiniteMPOHamiltonian(lattice, (1, 2) => O) + end - ψ₀ = InfiniteMPS(ℂ^3, ℂ^D) - ψ₀, = find_groundstate(ψ₀, H; tol, verbosity=1) + H_lazy = LazySum(mpo_hamiltonians) + H = sum(H_lazy) - @testset "VUMPS" begin - # test logging passes - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, - VUMPS(; tol, verbosity=verbosity_full, maxiter=2)) + ψ₀ = InfiniteMPS(ℂ^3, ℂ^D) + ψ₀, = find_groundstate(ψ₀, H; tol, verbosity = 1) - # compare states - alg = VUMPS(; tol, verbosity=verbosity_conv) - ψ, envs, δ = find_groundstate(ψ, H_lazy, alg) + @testset "VUMPS" begin + # test logging passes + ψ, envs, δ = find_groundstate( + ψ₀, H_lazy, VUMPS(; tol, verbosity = verbosity_full, maxiter = 2) + ) - @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol - end + # compare states + alg = VUMPS(; tol, verbosity = verbosity_conv) + ψ, envs, δ = find_groundstate(ψ, H_lazy, alg) - @testset "IDMRG" begin - # test logging passes - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, - IDMRG(; tol, verbosity=verbosity_full, maxiter=2)) + @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol + end - # compare states - alg = IDMRG(; tol, verbosity=verbosity_conv, maxiter=300) - ψ, envs, δ = find_groundstate(ψ, H_lazy, alg) + @testset "IDMRG" begin + # test logging passes + ψ, envs, δ = find_groundstate( + ψ₀, H_lazy, IDMRG(; tol, verbosity = verbosity_full, maxiter = 2) + ) - @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol - end + # compare states + alg = IDMRG(; tol, verbosity = verbosity_conv, maxiter = 300) + ψ, envs, δ = find_groundstate(ψ, H_lazy, alg) - @testset "IDMRG2" begin - ψ₀′ = repeat(ψ₀, 2) - H_lazy′ = repeat(H_lazy, 2) - H′ = repeat(H, 2) + @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol + end - trscheme = truncdim(floor(Int, D * 1.5)) - # test logging passes - ψ, envs, δ = find_groundstate(ψ₀′, H_lazy′, - IDMRG2(; tol, verbosity=verbosity_full, maxiter=2, - trscheme)) + @testset "IDMRG2" begin + ψ₀′ = repeat(ψ₀, 2) + H_lazy′ = repeat(H_lazy, 2) + H′ = repeat(H, 2) - # compare states - alg = IDMRG2(; tol, verbosity=verbosity_conv, trscheme) - ψ, envs, δ = find_groundstate(ψ, H_lazy′, alg) + trscheme = truncdim(floor(Int, D * 1.5)) + # test logging passes + ψ, envs, δ = find_groundstate( + ψ₀′, H_lazy′, IDMRG2(; tol, verbosity = verbosity_full, maxiter = 2, trscheme) + ) - @test abs(dot(ψ₀′, ψ)) ≈ 1 atol = atol - end + # compare states + alg = IDMRG2(; tol, verbosity = verbosity_conv, trscheme) + ψ, envs, δ = find_groundstate(ψ, H_lazy′, alg) - @testset "GradientGrassmann" begin - # test logging passes - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, - GradientGrassmann(; tol, verbosity=verbosity_full, - maxiter=2)) + @test abs(dot(ψ₀′, ψ)) ≈ 1 atol = atol + end - # compare states - alg = GradientGrassmann(; tol, verbosity=verbosity_conv) - ψ, envs, δ = find_groundstate(ψ₀, H_lazy, alg) + @testset "GradientGrassmann" begin + # test logging passes + ψ, envs, δ = find_groundstate( + ψ₀, H_lazy, GradientGrassmann(; tol, verbosity = verbosity_full, maxiter = 2) + ) - @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol - end -end + # compare states + alg = GradientGrassmann(; tol, verbosity = verbosity_conv) + ψ, envs, δ = find_groundstate(ψ₀, H_lazy, alg) -@testset "timestep" verbose = true begin - dt = 0.1 - algs = [TDVP(), TDVP2(; trscheme=truncdim(10))] - L = 10 - - H = force_planar(heisenberg_XXX(Trivial, Float64; spin=1 // 2, L)) - ψ = FiniteMPS(rand, Float64, L, ℙ^2, ℙ^4) - E = expectation_value(ψ, H) - ψ₀, = find_groundstate(ψ, H) - E₀ = expectation_value(ψ₀, H) - - @testset "Finite $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs - ψ1, envs = timestep(ψ₀, H, 0.0, dt, alg) - E1 = expectation_value(ψ1, H, envs) - @test E₀ ≈ E1 atol = 1e-2 - @test dot(ψ1, ψ₀) ≈ exp(im * dt * E₀) atol = 1e-4 + @test abs(dot(ψ₀, ψ)) ≈ 1 atol = atol + end end - Hlazy = LazySum([3 * H, 1.55 * H, -0.1 * H]) + @testset "timestep" verbose = true begin + dt = 0.1 + algs = [TDVP(), TDVP2(; trscheme = truncdim(10))] + L = 10 - @testset "Finite LazySum $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs - ψ, envs = timestep(ψ₀, Hlazy, 0.0, dt, alg) - E = expectation_value(ψ, Hlazy, envs) - @test (3 + 1.55 - 0.1) * E₀ ≈ E atol = 1e-2 - end + H = force_planar(heisenberg_XXX(Trivial, Float64; spin = 1 // 2, L)) + ψ = FiniteMPS(rand, Float64, L, ℙ^2, ℙ^4) + E = expectation_value(ψ, H) + ψ₀, = find_groundstate(ψ, H) + E₀ = expectation_value(ψ₀, H) + + @testset "Finite $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs + ψ1, envs = timestep(ψ₀, H, 0.0, dt, alg) + E1 = expectation_value(ψ1, H, envs) + @test E₀ ≈ E1 atol = 1.0e-2 + @test dot(ψ1, ψ₀) ≈ exp(im * dt * E₀) atol = 1.0e-4 + end - Ht = MultipliedOperator(H, t -> 4) + MultipliedOperator(H, 1.45) + Hlazy = LazySum([3 * H, 1.55 * H, -0.1 * H]) - @testset "Finite TimeDependent LazySum $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in - algs - ψ, envs = timestep(ψ₀, Ht(1.0), 0.0, dt, alg) - E = expectation_value(ψ, Ht(1.0), envs) + @testset "Finite LazySum $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs + ψ, envs = timestep(ψ₀, Hlazy, 0.0, dt, alg) + E = expectation_value(ψ, Hlazy, envs) + @test (3 + 1.55 - 0.1) * E₀ ≈ E atol = 1.0e-2 + end - ψt, envst = timestep(ψ₀, Ht, 1.0, dt, alg) - Et = expectation_value(ψt, Ht(1.0), envst) - @test E ≈ Et atol = 1e-8 - end + Ht = MultipliedOperator(H, t -> 4) + MultipliedOperator(H, 1.45) - Ht2 = MultipliedOperator(H, t -> t < 0 ? error("t < 0!") : 4) + - MultipliedOperator(H, 1.45) - @testset "Finite TimeDependent LazySum (fix negative t issue) $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in - algs - ψ, envs = timestep(ψ₀, Ht2, 0.0, dt, alg) - E = expectation_value(ψ, Ht2(0.0), envs) + @testset "Finite TimeDependent LazySum $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs + ψ, envs = timestep(ψ₀, Ht(1.0), 0.0, dt, alg) + E = expectation_value(ψ, Ht(1.0), envs) - ψt, envst = timestep(ψ₀, Ht2, 0.0, dt, alg) - Et = expectation_value(ψt, Ht2(0.0), envst) - @test E ≈ Et atol = 1e-8 - end - - H = repeat(force_planar(heisenberg_XXX(; spin=1)), 2) - ψ₀ = InfiniteMPS([ℙ^3, ℙ^3], [ℙ^50, ℙ^50]) - E₀ = expectation_value(ψ₀, H) + ψt, envst = timestep(ψ₀, Ht, 1.0, dt, alg) + Et = expectation_value(ψt, Ht(1.0), envst) + @test E ≈ Et atol = 1.0e-8 + end - @testset "Infinite TDVP" begin - ψ, envs = timestep(ψ₀, H, 0.0, dt, TDVP()) - E = expectation_value(ψ, H, envs) - @test E₀ ≈ E atol = 1e-2 - end + Ht2 = MultipliedOperator(H, t -> t < 0 ? error("t < 0!") : 4) + + MultipliedOperator(H, 1.45) + @testset "Finite TimeDependent LazySum (fix negative t issue) $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs + ψ, envs = timestep(ψ₀, Ht2, 0.0, dt, alg) + E = expectation_value(ψ, Ht2(0.0), envs) - Hlazy = LazySum([3 * deepcopy(H), 1.55 * deepcopy(H), -0.1 * deepcopy(H)]) + ψt, envst = timestep(ψ₀, Ht2, 0.0, dt, alg) + Et = expectation_value(ψt, Ht2(0.0), envst) + @test E ≈ Et atol = 1.0e-8 + end - @testset "Infinite LazySum TDVP" begin - ψ, envs = timestep(ψ₀, Hlazy, 0.0, dt, TDVP()) - E = expectation_value(ψ, Hlazy, envs) - @test (3 + 1.55 - 0.1) * E₀ ≈ E atol = 1e-2 - end + H = repeat(force_planar(heisenberg_XXX(; spin = 1)), 2) + ψ₀ = InfiniteMPS([ℙ^3, ℙ^3], [ℙ^50, ℙ^50]) + E₀ = expectation_value(ψ₀, H) - Ht = MultipliedOperator(H, t -> 4) + MultipliedOperator(H, 1.45) + @testset "Infinite TDVP" begin + ψ, envs = timestep(ψ₀, H, 0.0, dt, TDVP()) + E = expectation_value(ψ, H, envs) + @test E₀ ≈ E atol = 1.0e-2 + end - @testset "Infinite TimeDependent LazySum" begin - ψ, envs = timestep(ψ₀, Ht(1.0), 0.0, dt, TDVP()) - E = expectation_value(ψ, Ht(1.0), envs) + Hlazy = LazySum([3 * deepcopy(H), 1.55 * deepcopy(H), -0.1 * deepcopy(H)]) - ψt, envst = timestep(ψ₀, Ht, 1.0, dt, TDVP()) - Et = expectation_value(ψt, Ht(1.0), envst) - @test E ≈ Et atol = 1e-8 - end -end + @testset "Infinite LazySum TDVP" begin + ψ, envs = timestep(ψ₀, Hlazy, 0.0, dt, TDVP()) + E = expectation_value(ψ, Hlazy, envs) + @test (3 + 1.55 - 0.1) * E₀ ≈ E atol = 1.0e-2 + end -@testset "time_evolve" verbose = true begin - t_span = 0:0.1:0.1 - algs = [TDVP(), TDVP2(; trscheme=truncdim(10))] + Ht = MultipliedOperator(H, t -> 4) + MultipliedOperator(H, 1.45) - L = 10 - H = force_planar(heisenberg_XXX(; spin=1 // 2, L)) - ψ₀ = FiniteMPS(L, ℙ^2, ℙ^1) - E₀ = expectation_value(ψ₀, H) + @testset "Infinite TimeDependent LazySum" begin + ψ, envs = timestep(ψ₀, Ht(1.0), 0.0, dt, TDVP()) + E = expectation_value(ψ, Ht(1.0), envs) - @testset "Finite $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs - ψ, envs = time_evolve(ψ₀, H, t_span, alg) - E = expectation_value(ψ, H, envs) - @test E₀ ≈ E atol = 1e-2 + ψt, envst = timestep(ψ₀, Ht, 1.0, dt, TDVP()) + Et = expectation_value(ψt, Ht(1.0), envst) + @test E ≈ Et atol = 1.0e-8 + end end - H = repeat(force_planar(heisenberg_XXX(; spin=1)), 2) - ψ₀ = InfiniteMPS([ℙ^3, ℙ^3], [ℙ^50, ℙ^50]) - E₀ = expectation_value(ψ₀, H) + @testset "time_evolve" verbose = true begin + t_span = 0:0.1:0.1 + algs = [TDVP(), TDVP2(; trscheme = truncdim(10))] - @testset "Infinite TDVP" begin - ψ, envs = time_evolve(ψ₀, H, t_span, TDVP()) - E = expectation_value(ψ, H, envs) - @test E₀ ≈ E atol = 1e-2 + L = 10 + H = force_planar(heisenberg_XXX(; spin = 1 // 2, L)) + ψ₀ = FiniteMPS(L, ℙ^2, ℙ^1) + E₀ = expectation_value(ψ₀, H) + + @testset "Finite $(alg isa TDVP ? "TDVP" : "TDVP2")" for alg in algs + ψ, envs = time_evolve(ψ₀, H, t_span, alg) + E = expectation_value(ψ, H, envs) + @test E₀ ≈ E atol = 1.0e-2 + end + + H = repeat(force_planar(heisenberg_XXX(; spin = 1)), 2) + ψ₀ = InfiniteMPS([ℙ^3, ℙ^3], [ℙ^50, ℙ^50]) + E₀ = expectation_value(ψ₀, H) + + @testset "Infinite TDVP" begin + ψ, envs = time_evolve(ψ₀, H, t_span, TDVP()) + E = expectation_value(ψ, H, envs) + @test E₀ ≈ E atol = 1.0e-2 + end end -end -@testset "leading_boundary" verbose = true begin - tol = 1e-4 - verbosity = verbosity_conv - D = 10 - D1 = 13 - algs = [VUMPS(; tol, verbosity), VOMPS(; tol, verbosity), + @testset "leading_boundary" verbose = true begin + tol = 1.0e-4 + verbosity = verbosity_conv + D = 10 + D1 = 13 + algs = [ + VUMPS(; tol, verbosity), VOMPS(; tol, verbosity), GradientGrassmann(; tol, verbosity), IDMRG(; tol, verbosity), - IDMRG2(; tol, verbosity, trscheme=truncdim(D1))] - mpo = force_planar(classical_ising()) - - ψ₀ = InfiniteMPS([ℙ^2], [ℙ^D]) - @testset "Infinite $i" for (i, alg) in enumerate(algs) - if alg isa IDMRG2 - ψ2 = repeat(ψ₀, 2) - mpo2 = repeat(mpo, 2) - ψ, envs = leading_boundary(ψ2, mpo2, alg) - @test dim(space(ψ.AL[1, 1], 1)) == dim(space(ψ₀.AL[1, 1], 1)) + (D1 - D) - @test expectation_value(ψ, mpo2, envs) ≈ 2.5337^2 atol = 1e-3 - else - ψ, envs = leading_boundary(ψ₀, mpo, alg) - ψ, envs = changebonds(ψ, mpo, OptimalExpand(; trscheme=truncdim(D1 - D)), envs) - ψ, envs = leading_boundary(ψ, mpo, alg) - @test dim(space(ψ.AL[1, 1], 1)) == dim(space(ψ₀.AL[1, 1], 1)) + (D1 - D) - @test expectation_value(ψ, mpo, envs) ≈ 2.5337 atol = 1e-3 + IDMRG2(; tol, verbosity, trscheme = truncdim(D1)), + ] + mpo = force_planar(classical_ising()) + + ψ₀ = InfiniteMPS([ℙ^2], [ℙ^D]) + @testset "Infinite $i" for (i, alg) in enumerate(algs) + if alg isa IDMRG2 + ψ2 = repeat(ψ₀, 2) + mpo2 = repeat(mpo, 2) + ψ, envs = leading_boundary(ψ2, mpo2, alg) + @test dim(space(ψ.AL[1, 1], 1)) == dim(space(ψ₀.AL[1, 1], 1)) + (D1 - D) + @test expectation_value(ψ, mpo2, envs) ≈ 2.5337^2 atol = 1.0e-3 + else + ψ, envs = leading_boundary(ψ₀, mpo, alg) + ψ, envs = changebonds(ψ, mpo, OptimalExpand(; trscheme = truncdim(D1 - D)), envs) + ψ, envs = leading_boundary(ψ, mpo, alg) + @test dim(space(ψ.AL[1, 1], 1)) == dim(space(ψ₀.AL[1, 1], 1)) + (D1 - D) + @test expectation_value(ψ, mpo, envs) ≈ 2.5337 atol = 1.0e-3 + end end end -end -@testset "excitations" verbose = true begin - @testset "infinite (ham)" begin - H = repeat(force_planar(heisenberg_XXX()), 2) - ψ = InfiniteMPS([ℙ^3, ℙ^3], [ℙ^48, ℙ^48]) - ψ, envs, _ = find_groundstate(ψ, H; maxiter=400, verbosity=verbosity_conv, - tol=1e-10) - energies, ϕs = @inferred excitations(H, QuasiparticleAnsatz(), Float64(pi), ψ, - envs) - @test energies[1] ≈ 0.41047925 atol = 1e-4 - @test variance(ϕs[1], H) < 1e-8 - end - @testset "infinite (mpo)" begin - H = repeat(sixvertex(), 2) - ψ = InfiniteMPS([ℂ^2, ℂ^2], [ℂ^10, ℂ^10]) - ψ, envs, _ = leading_boundary(ψ, H, - VUMPS(; maxiter=400, verbosity=verbosity_conv, - tol=1e-10)) - energies, ϕs = @inferred excitations(H, QuasiparticleAnsatz(), - [0.0, Float64(pi / 2)], ψ, - envs; verbosity=0) - @test abs(energies[1]) > abs(energies[2]) # has a minimum at pi/2 - end + @testset "excitations" verbose = true begin + @testset "infinite (ham)" begin + H = repeat(force_planar(heisenberg_XXX()), 2) + ψ = InfiniteMPS([ℙ^3, ℙ^3], [ℙ^48, ℙ^48]) + ψ, envs, _ = find_groundstate( + ψ, H; maxiter = 400, verbosity = verbosity_conv, tol = 1.0e-10 + ) + energies, ϕs = @inferred excitations( + H, QuasiparticleAnsatz(), Float64(pi), ψ, envs + ) + @test energies[1] ≈ 0.41047925 atol = 1.0e-4 + @test variance(ϕs[1], H) < 1.0e-8 + end + @testset "infinite (mpo)" begin + H = repeat(sixvertex(), 2) + ψ = InfiniteMPS([ℂ^2, ℂ^2], [ℂ^10, ℂ^10]) + ψ, envs, _ = leading_boundary( + ψ, H, VUMPS(; maxiter = 400, verbosity = verbosity_conv, tol = 1.0e-10) + ) + energies, ϕs = @inferred excitations( + H, QuasiparticleAnsatz(), [0.0, Float64(pi / 2)], ψ, envs; verbosity = 0 + ) + @test abs(energies[1]) > abs(energies[2]) # has a minimum at pi/2 + end - @testset "finite" begin - verbosity = verbosity_conv - H_inf = force_planar(transverse_field_ising()) - ψ_inf = InfiniteMPS([ℙ^2], [ℙ^10]) - ψ_inf, envs, _ = find_groundstate(ψ_inf, H_inf; maxiter=400, verbosity, tol=1e-9) - energies, ϕs = @inferred excitations(H_inf, QuasiparticleAnsatz(), 0.0, ψ_inf, envs) - inf_en = energies[1] - - fin_en = map([20, 10]) do len - H = force_planar(transverse_field_ising(; L=len)) - ψ = FiniteMPS(rand, ComplexF64, len, ℙ^2, ℙ^10) - ψ, envs, = find_groundstate(ψ, H; verbosity) - - # find energy with quasiparticle ansatz - energies_QP, ϕs = @inferred excitations(H, QuasiparticleAnsatz(), ψ, envs) - @test variance(ϕs[1], H) < 1e-6 - - # find energy with normal dmrg - for gsalg in (DMRG(; verbosity, tol=1e-6), - DMRG2(; verbosity, tol=1e-6, trscheme=truncbelow(1e-4))) - energies_dm, _ = @inferred excitations(H, FiniteExcited(; gsalg), ψ) - @test energies_dm[1] ≈ energies_QP[1] + expectation_value(ψ, H, envs) atol = 1e-4 + @testset "finite" begin + verbosity = verbosity_conv + H_inf = force_planar(transverse_field_ising()) + ψ_inf = InfiniteMPS([ℙ^2], [ℙ^10]) + ψ_inf, envs, _ = find_groundstate(ψ_inf, H_inf; maxiter = 400, verbosity, tol = 1.0e-9) + energies, ϕs = @inferred excitations(H_inf, QuasiparticleAnsatz(), 0.0, ψ_inf, envs) + inf_en = energies[1] + + fin_en = map([20, 10]) do len + H = force_planar(transverse_field_ising(; L = len)) + ψ = FiniteMPS(rand, ComplexF64, len, ℙ^2, ℙ^10) + ψ, envs, = find_groundstate(ψ, H; verbosity) + + # find energy with quasiparticle ansatz + energies_QP, ϕs = @inferred excitations(H, QuasiparticleAnsatz(), ψ, envs) + @test variance(ϕs[1], H) < 1.0e-6 + + # find energy with normal dmrg + for gsalg in ( + DMRG(; verbosity, tol = 1.0e-6), + DMRG2(; verbosity, tol = 1.0e-6, trscheme = truncbelow(1.0e-4)), + ) + energies_dm, _ = @inferred excitations(H, FiniteExcited(; gsalg), ψ) + @test energies_dm[1] ≈ energies_QP[1] + expectation_value(ψ, H, envs) atol = 1.0e-4 + end + + # find energy with Chepiga ansatz + energies_ch, _ = @inferred excitations(H, ChepigaAnsatz(), ψ, envs) + @test energies_ch[1] ≈ energies_QP[1] + expectation_value(ψ, H, envs) atol = 1.0e-4 + energies_ch2, _ = @inferred excitations(H, ChepigaAnsatz2(), ψ, envs) + @test energies_ch2[1] ≈ energies_QP[1] + expectation_value(ψ, H, envs) atol = 1.0e-4 + return energies_QP[1] end - # find energy with Chepiga ansatz - energies_ch, _ = @inferred excitations(H, ChepigaAnsatz(), ψ, envs) - @test energies_ch[1] ≈ energies_QP[1] + expectation_value(ψ, H, envs) atol = 1e-4 - energies_ch2, _ = @inferred excitations(H, ChepigaAnsatz2(), ψ, envs) - @test energies_ch2[1] ≈ energies_QP[1] + expectation_value(ψ, H, envs) atol = 1e-4 - return energies_QP[1] + @test issorted(abs.(fin_en .- inf_en)) end - - @test issorted(abs.(fin_en .- inf_en)) end -end -@testset "changebonds $((pspace,Dspace))" verbose = true for (pspace, Dspace) in - [(ℙ^4, ℙ^3), - (Rep[SU₂](1 => 1), - Rep[SU₂](0 => 2, 1 => 2, - 2 => 1))] - @testset "mpo" begin - #random nn interaction - nn = rand(ComplexF64, pspace * pspace, pspace * pspace) - nn += nn' - H = InfiniteMPOHamiltonian(PeriodicVector(fill(pspace, 1)), (1, 2) => nn) - Δt = 0.1 - expH = make_time_mpo(H, Δt, WII()) - - O = MPSKit.DenseMPO(expH) - Op = periodic_boundary_conditions(O, 10) - Op′ = changebonds(Op, SvdCut(; trscheme=truncdim(5))) - - @test dim(space(Op′[5], 1)) < dim(space(Op[5], 1)) - end + @testset "changebonds $((pspace, Dspace))" verbose = true for (pspace, Dspace) in + [ + (ℙ^4, ℙ^3), + (Rep[SU₂](1 => 1), Rep[SU₂](0 => 2, 1 => 2, 2 => 1)), + ] + @testset "mpo" begin + #random nn interaction + nn = rand(ComplexF64, pspace * pspace, pspace * pspace) + nn += nn' + H = InfiniteMPOHamiltonian(PeriodicVector(fill(pspace, 1)), (1, 2) => nn) + Δt = 0.1 + expH = make_time_mpo(H, Δt, WII()) + + O = MPSKit.DenseMPO(expH) + Op = periodic_boundary_conditions(O, 10) + Op′ = changebonds(Op, SvdCut(; trscheme = truncdim(5))) + + @test dim(space(Op′[5], 1)) < dim(space(Op[5], 1)) + end + + @testset "infinite mps" begin + # random nn interaction + nn = rand(ComplexF64, pspace * pspace, pspace * pspace) + nn += nn' + H0 = InfiniteMPOHamiltonian(PeriodicVector(fill(pspace, 1)), (1, 2) => nn) + + # test rand_expand + for unit_cell_size in 2:3 + H = repeat(H0, unit_cell_size) + state = InfiniteMPS(fill(pspace, unit_cell_size), fill(Dspace, unit_cell_size)) + + state_re = changebonds( + state, RandExpand(; trscheme = truncdim(dim(Dspace) * dim(Dspace))) + ) + @test dot(state, state_re) ≈ 1 atol = 1.0e-8 + end + # test optimal_expand + for unit_cell_size in 2:3 + H = repeat(H0, unit_cell_size) + state = InfiniteMPS(fill(pspace, unit_cell_size), fill(Dspace, unit_cell_size)) + + state_oe, _ = changebonds( + state, H, OptimalExpand(; trscheme = truncdim(dim(Dspace) * dim(Dspace))) + ) + @test dot(state, state_oe) ≈ 1 atol = 1.0e-8 + end + # test VUMPSSvdCut + for unit_cell_size in [1, 2, 3, 4] + H = repeat(H0, unit_cell_size) + state = InfiniteMPS(fill(pspace, unit_cell_size), fill(Dspace, unit_cell_size)) - @testset "infinite mps" begin - # random nn interaction - nn = rand(ComplexF64, pspace * pspace, pspace * pspace) - nn += nn' - H0 = InfiniteMPOHamiltonian(PeriodicVector(fill(pspace, 1)), (1, 2) => nn) - - # test rand_expand - for unit_cell_size in 2:3 - H = repeat(H0, unit_cell_size) - state = InfiniteMPS(fill(pspace, unit_cell_size), fill(Dspace, unit_cell_size)) - - state_re = changebonds(state, - RandExpand(; - trscheme=truncdim(dim(Dspace) * dim(Dspace)))) - @test dot(state, state_re) ≈ 1 atol = 1e-8 - end - # test optimal_expand - for unit_cell_size in 2:3 - H = repeat(H0, unit_cell_size) - state = InfiniteMPS(fill(pspace, unit_cell_size), fill(Dspace, unit_cell_size)) - - state_oe, _ = changebonds(state, - H, - OptimalExpand(; - trscheme=truncdim(dim(Dspace) * - dim(Dspace)))) - @test dot(state, state_oe) ≈ 1 atol = 1e-8 - end - # test VUMPSSvdCut - for unit_cell_size in [1, 2, 3, 4] - H = repeat(H0, unit_cell_size) - state = InfiniteMPS(fill(pspace, unit_cell_size), fill(Dspace, unit_cell_size)) - - state_vs, _ = changebonds(state, H, - VUMPSSvdCut(; trscheme=notrunc())) - @test dim(left_virtualspace(state, 1)) < dim(left_virtualspace(state_vs, 1)) - - state_vs_tr = changebonds(state_vs, SvdCut(; trscheme=truncdim(dim(Dspace)))) - @test dim(right_virtualspace(state_vs_tr, 1)) < - dim(right_virtualspace(state_vs, 1)) + state_vs, _ = changebonds(state, H, VUMPSSvdCut(; trscheme = notrunc())) + @test dim(left_virtualspace(state, 1)) < dim(left_virtualspace(state_vs, 1)) + + state_vs_tr = changebonds(state_vs, SvdCut(; trscheme = truncdim(dim(Dspace)))) + @test dim(right_virtualspace(state_vs_tr, 1)) < dim(right_virtualspace(state_vs, 1)) + end end - end - @testset "finite mps" begin - #random nn interaction - L = 10 - nn = rand(ComplexF64, pspace * pspace, pspace * pspace) - nn += nn' - H = FiniteMPOHamiltonian(fill(pspace, L), (i, i + 1) => nn for i in 1:(L - 1)) + @testset "finite mps" begin + #random nn interaction + L = 10 + nn = rand(ComplexF64, pspace * pspace, pspace * pspace) + nn += nn' + H = FiniteMPOHamiltonian(fill(pspace, L), (i, i + 1) => nn for i in 1:(L - 1)) - state = FiniteMPS(L, pspace, Dspace) + state = FiniteMPS(L, pspace, Dspace) - state_re = changebonds(state, - RandExpand(; trscheme=truncdim(dim(Dspace) * dim(Dspace)))) - @test dot(state, state_re) ≈ 1 atol = 1e-8 + state_re = changebonds( + state, RandExpand(; trscheme = truncdim(dim(Dspace) * dim(Dspace))) + ) + @test dot(state, state_re) ≈ 1 atol = 1.0e-8 - state_oe, _ = changebonds(state, H, - OptimalExpand(; - trscheme=truncdim(dim(Dspace) * dim(Dspace)))) - @test dot(state, state_oe) ≈ 1 atol = 1e-8 + state_oe, _ = changebonds( + state, H, OptimalExpand(; trscheme = truncdim(dim(Dspace) * dim(Dspace))) + ) + @test dot(state, state_oe) ≈ 1 atol = 1.0e-8 - state_tr = changebonds(state_oe, SvdCut(; trscheme=truncdim(dim(Dspace)))) + state_tr = changebonds(state_oe, SvdCut(; trscheme = truncdim(dim(Dspace)))) - @test dim(left_virtualspace(state_tr, 5)) < dim(left_virtualspace(state_oe, 5)) - end + @test dim(left_virtualspace(state_tr, 5)) < dim(left_virtualspace(state_oe, 5)) + end - @testset "MultilineMPS" begin - o = rand(ComplexF64, pspace * pspace, pspace * pspace) - mpo = MultilineMPO(o) + @testset "MultilineMPS" begin + o = rand(ComplexF64, pspace * pspace, pspace * pspace) + mpo = MultilineMPO(o) - t = rand(ComplexF64, Dspace * pspace, Dspace) - state = MultilineMPS(fill(t, 1, 1)) + t = rand(ComplexF64, Dspace * pspace, Dspace) + state = MultilineMPS(fill(t, 1, 1)) - state_re = changebonds(state, - RandExpand(; trscheme=truncdim(dim(Dspace) * dim(Dspace)))) - @test dot(state, state_re) ≈ 1 atol = 1e-8 + state_re = changebonds( + state, RandExpand(; trscheme = truncdim(dim(Dspace) * dim(Dspace))) + ) + @test dot(state, state_re) ≈ 1 atol = 1.0e-8 - state_oe, _ = changebonds(state, mpo, - OptimalExpand(; - trscheme=truncdim(dim(Dspace) * - dim(Dspace)))) - @test dot(state, state_oe) ≈ 1 atol = 1e-8 + state_oe, _ = changebonds( + state, mpo, OptimalExpand(; trscheme = truncdim(dim(Dspace) * dim(Dspace))) + ) + @test dot(state, state_oe) ≈ 1 atol = 1.0e-8 - state_tr = changebonds(state_oe, SvdCut(; trscheme=truncdim(dim(Dspace)))) + state_tr = changebonds(state_oe, SvdCut(; trscheme = truncdim(dim(Dspace)))) - @test dim(left_virtualspace(state_tr, 1, 1)) < - dim(left_virtualspace(state_oe, 1, 1)) + @test dim(left_virtualspace(state_tr, 1, 1)) < dim(left_virtualspace(state_oe, 1, 1)) + end end -end -@testset "Dynamical DMRG" verbose = true begin - L = 10 - H = force_planar(-transverse_field_ising(; L, g=-4)) - gs, = find_groundstate(FiniteMPS(L, ℙ^2, ℙ^10), H; verbosity=verbosity_conv) - E₀ = expectation_value(gs, H) + @testset "Dynamical DMRG" verbose = true begin + L = 10 + H = force_planar(-transverse_field_ising(; L, g = -4)) + gs, = find_groundstate(FiniteMPS(L, ℙ^2, ℙ^10), H; verbosity = verbosity_conv) + E₀ = expectation_value(gs, H) - vals = (-0.5:0.2:0.5) .+ E₀ - eta = 0.3im + vals = (-0.5:0.2:0.5) .+ E₀ + eta = 0.3im - predicted = [1 / (v + eta - E₀) for v in vals] + predicted = [1 / (v + eta - E₀) for v in vals] - @testset "Flavour $f" for f in (Jeckelmann(), NaiveInvert()) - alg = DynamicalDMRG(; flavour=f, verbosity=0, tol=1e-8) - data = map(vals) do v - result, = propagator(gs, v + eta, H, alg) - return result + @testset "Flavour $f" for f in (Jeckelmann(), NaiveInvert()) + alg = DynamicalDMRG(; flavour = f, verbosity = 0, tol = 1.0e-8) + data = map(vals) do v + result, = propagator(gs, v + eta, H, alg) + return result + end + @test data ≈ predicted atol = 1.0e-8 end - @test data ≈ predicted atol = 1e-8 end -end -@testset "fidelity susceptibility" begin - X = TensorMap(ComplexF64[0 1; 1 0], ℂ^2 ← ℂ^2) - Z = TensorMap(ComplexF64[1 0; 0 -1], ℂ^2 ← ℂ^2) + @testset "fidelity susceptibility" begin + X = TensorMap(ComplexF64[0 1; 1 0], ℂ^2 ← ℂ^2) + Z = TensorMap(ComplexF64[1 0; 0 -1], ℂ^2 ← ℂ^2) - H_X = InfiniteMPOHamiltonian(X) - H_ZZ = InfiniteMPOHamiltonian(Z ⊗ Z) + H_X = InfiniteMPOHamiltonian(X) + H_ZZ = InfiniteMPOHamiltonian(Z ⊗ Z) - hamiltonian(λ) = H_ZZ + λ * H_X - analytical_susceptibility(λ) = abs(1 / (16 * λ^2 * (λ^2 - 1))) + hamiltonian(λ) = H_ZZ + λ * H_X + analytical_susceptibility(λ) = abs(1 / (16 * λ^2 * (λ^2 - 1))) - for λ in [1.05, 2.0, 4.0] - H = hamiltonian(λ) - ψ = InfiniteMPS([ℂ^2], [ℂ^16]) - ψ, envs, = find_groundstate(ψ, H, VUMPS(; maxiter=100, verbosity=0)) + for λ in [1.05, 2.0, 4.0] + H = hamiltonian(λ) + ψ = InfiniteMPS([ℂ^2], [ℂ^16]) + ψ, envs, = find_groundstate(ψ, H, VUMPS(; maxiter = 100, verbosity = 0)) - numerical_susceptibility = fidelity_susceptibility(ψ, H, [H_X], envs; maxiter=10) - @test numerical_susceptibility[1, 1] ≈ analytical_susceptibility(λ) atol = 1e-2 + numerical_susceptibility = fidelity_susceptibility(ψ, H, [H_X], envs; maxiter = 10) + @test numerical_susceptibility[1, 1] ≈ analytical_susceptibility(λ) atol = 1.0e-2 - # test if the finite fid sus approximates the analytical one with increasing system size - fin_en = map([20, 15, 10]) do L - Hfin = open_boundary_conditions(hamiltonian(λ), L) - H_Xfin = open_boundary_conditions(H_X, L) - ψ = FiniteMPS(rand, ComplexF64, L, ℂ^2, ℂ^16) - ψ, envs, = find_groundstate(ψ, Hfin, DMRG(; verbosity=0)) - numerical_susceptibility = fidelity_susceptibility(ψ, Hfin, [H_Xfin], envs; - maxiter=10) - return numerical_susceptibility[1, 1] / L + # test if the finite fid sus approximates the analytical one with increasing system size + fin_en = map([20, 15, 10]) do L + Hfin = open_boundary_conditions(hamiltonian(λ), L) + H_Xfin = open_boundary_conditions(H_X, L) + ψ = FiniteMPS(rand, ComplexF64, L, ℂ^2, ℂ^16) + ψ, envs, = find_groundstate(ψ, Hfin, DMRG(; verbosity = 0)) + numerical_susceptibility = fidelity_susceptibility( + ψ, Hfin, [H_Xfin], envs; maxiter = 10 + ) + return numerical_susceptibility[1, 1] / L + end + @test issorted(abs.(fin_en .- analytical_susceptibility(λ))) end - @test issorted(abs.(fin_en .- analytical_susceptibility(λ))) end -end -# stub tests -@testset "correlation length / entropy" begin - ψ = InfiniteMPS([ℙ^2], [ℙ^10]) - H = force_planar(transverse_field_ising()) - ψ, = find_groundstate(ψ, H, VUMPS(; verbosity=0)) - len_crit = correlation_length(ψ)[1] - entrop_crit = entropy(ψ) - - H = force_planar(transverse_field_ising(; g=4)) - ψ, = find_groundstate(ψ, H, VUMPS(; verbosity=0)) - len_gapped = correlation_length(ψ)[1] - entrop_gapped = entropy(ψ) - - @test len_crit > len_gapped - @test real(entrop_crit) > real(entrop_gapped) -end + # stub tests + @testset "correlation length / entropy" begin + ψ = InfiniteMPS([ℙ^2], [ℙ^10]) + H = force_planar(transverse_field_ising()) + ψ, = find_groundstate(ψ, H, VUMPS(; verbosity = 0)) + len_crit = correlation_length(ψ)[1] + entrop_crit = entropy(ψ) -@testset "expectation value / correlator" begin - g = 4.0 - ψ = InfiniteMPS(ℂ^2, ℂ^10) - H = transverse_field_ising(; g) - ψ, = find_groundstate(ψ, H, VUMPS(; verbosity=0)) - - @test expectation_value(ψ, H) ≈ - expectation_value(ψ, 1 => -g * S_x()) + expectation_value(ψ, (1, 2) => -S_zz()) - Z_mpo = MPSKit.add_util_leg(S_z()) - G = correlator(ψ, Z_mpo, Z_mpo, 1, 2:5) - G2 = correlator(ψ, S_zz(), 1, 3:2:5) - @test isapprox(G[2], G2[1], atol=1e-2) - @test isapprox(last(G), last(G2), atol=1e-2) - @test isapprox(G[1], expectation_value(ψ, (1, 2) => S_zz()), atol=1e-2) - @test isapprox(G[2], expectation_value(ψ, (1, 3) => S_zz()), atol=1e-2) -end + H = force_planar(transverse_field_ising(; g = 4)) + ψ, = find_groundstate(ψ, H, VUMPS(; verbosity = 0)) + len_gapped = correlation_length(ψ)[1] + entrop_gapped = entropy(ψ) -@testset "approximate" verbose = true begin - verbosity = verbosity_conv - @testset "mpo * infinite ≈ infinite" begin - ψ = InfiniteMPS([ℙ^2, ℙ^2], [ℙ^10, ℙ^10]) - ψ0 = InfiniteMPS([ℙ^2, ℙ^2], [ℙ^12, ℙ^12]) - - H = force_planar(repeat(transverse_field_ising(; g=4), 2)) - - dt = 1e-3 - sW1 = make_time_mpo(H, dt, TaylorCluster(; N=3, compression=true, extension=true)) - sW2 = make_time_mpo(H, dt, WII()) - W1 = MPSKit.DenseMPO(sW1) - W2 = MPSKit.DenseMPO(sW2) - - ψ1, _ = approximate(ψ0, (sW1, ψ), VOMPS(; verbosity)) - MPSKit.Defaults.set_scheduler!(:serial) - ψ2, _ = approximate(ψ0, (W2, ψ), VOMPS(; verbosity)) - MPSKit.Defaults.set_scheduler!() - - ψ3, _ = approximate(ψ0, (W1, ψ), IDMRG(; verbosity)) - ψ4, _ = approximate(ψ0, (sW2, ψ), IDMRG2(; trscheme=truncdim(12), verbosity)) - ψ5, _ = timestep(ψ, H, 0.0, dt, TDVP()) - ψ6 = changebonds(W1 * ψ, SvdCut(; trscheme=truncdim(12))) - - @test abs(dot(ψ1, ψ5)) ≈ 1.0 atol = dt - @test abs(dot(ψ3, ψ5)) ≈ 1.0 atol = dt - @test abs(dot(ψ6, ψ5)) ≈ 1.0 atol = dt - @test abs(dot(ψ2, ψ4)) ≈ 1.0 atol = dt - - nW1 = changebonds(W1, SvdCut(; trscheme=truncbelow(dt))) # this should be a trivial mpo now - @test dim(space(nW1[1], 1)) == 1 + @test len_crit > len_gapped + @test real(entrop_crit) > real(entrop_gapped) end - finite_algs = [DMRG(; verbosity), DMRG2(; verbosity, trscheme=truncdim(10))] - @testset "finitemps1 ≈ finitemps2" for alg in finite_algs - a = FiniteMPS(10, ℂ^2, ℂ^10) - b = FiniteMPS(10, ℂ^2, ℂ^20) - - before = abs(dot(a, b)) + @testset "expectation value / correlator" begin + g = 4.0 + ψ = InfiniteMPS(ℂ^2, ℂ^10) + H = transverse_field_ising(; g) + ψ, = find_groundstate(ψ, H, VUMPS(; verbosity = 0)) - a = first(approximate(a, b, alg)) + @test expectation_value(ψ, H) ≈ + expectation_value(ψ, 1 => -g * S_x()) + expectation_value(ψ, (1, 2) => -S_zz()) + Z_mpo = MPSKit.add_util_leg(S_z()) + G = correlator(ψ, Z_mpo, Z_mpo, 1, 2:5) + G2 = correlator(ψ, S_zz(), 1, 3:2:5) + @test isapprox(G[2], G2[1], atol = 1.0e-2) + @test isapprox(last(G), last(G2), atol = 1.0e-2) + @test isapprox(G[1], expectation_value(ψ, (1, 2) => S_zz()), atol = 1.0e-2) + @test isapprox(G[2], expectation_value(ψ, (1, 3) => S_zz()), atol = 1.0e-2) + end - after = abs(dot(a, b)) + @testset "approximate" verbose = true begin + verbosity = verbosity_conv + @testset "mpo * infinite ≈ infinite" begin + ψ = InfiniteMPS([ℙ^2, ℙ^2], [ℙ^10, ℙ^10]) + ψ0 = InfiniteMPS([ℙ^2, ℙ^2], [ℙ^12, ℙ^12]) + + H = force_planar(repeat(transverse_field_ising(; g = 4), 2)) + + dt = 1.0e-3 + sW1 = make_time_mpo(H, dt, TaylorCluster(; N = 3, compression = true, extension = true)) + sW2 = make_time_mpo(H, dt, WII()) + W1 = MPSKit.DenseMPO(sW1) + W2 = MPSKit.DenseMPO(sW2) + + ψ1, _ = approximate(ψ0, (sW1, ψ), VOMPS(; verbosity)) + MPSKit.Defaults.set_scheduler!(:serial) + ψ2, _ = approximate(ψ0, (W2, ψ), VOMPS(; verbosity)) + MPSKit.Defaults.set_scheduler!() + + ψ3, _ = approximate(ψ0, (W1, ψ), IDMRG(; verbosity)) + ψ4, _ = approximate(ψ0, (sW2, ψ), IDMRG2(; trscheme = truncdim(12), verbosity)) + ψ5, _ = timestep(ψ, H, 0.0, dt, TDVP()) + ψ6 = changebonds(W1 * ψ, SvdCut(; trscheme = truncdim(12))) + + @test abs(dot(ψ1, ψ5)) ≈ 1.0 atol = dt + @test abs(dot(ψ3, ψ5)) ≈ 1.0 atol = dt + @test abs(dot(ψ6, ψ5)) ≈ 1.0 atol = dt + @test abs(dot(ψ2, ψ4)) ≈ 1.0 atol = dt + + nW1 = changebonds(W1, SvdCut(; trscheme = truncbelow(dt))) # this should be a trivial mpo now + @test dim(space(nW1[1], 1)) == 1 + end - @test before < after - end + finite_algs = [DMRG(; verbosity), DMRG2(; verbosity, trscheme = truncdim(10))] + @testset "finitemps1 ≈ finitemps2" for alg in finite_algs + a = FiniteMPS(10, ℂ^2, ℂ^10) + b = FiniteMPS(10, ℂ^2, ℂ^20) - @testset "sparse_mpo * finitemps1 ≈ finitemps2" for alg in finite_algs - L = 10 - ψ₁ = FiniteMPS(L, ℂ^2, ℂ^30) - ψ₂ = FiniteMPS(L, ℂ^2, ℂ^25) + before = abs(dot(a, b)) - H = transverse_field_ising(; g=4.0, L) - τ = 1e-3 + a = first(approximate(a, b, alg)) - expH = make_time_mpo(H, τ, WI) - ψ₂, = approximate(ψ₂, (expH, ψ₁), alg) - normalize!(ψ₂) - ψ₂′, = timestep(ψ₁, H, 0.0, τ, TDVP()) - @test abs(dot(ψ₁, ψ₁)) ≈ abs(dot(ψ₂, ψ₂′)) atol = 0.001 - end + after = abs(dot(a, b)) - @testset "dense_mpo * finitemps1 ≈ finitemps2" for alg in finite_algs - L = 10 - ψ₁ = FiniteMPS(L, ℂ^2, ℂ^20) - ψ₂ = FiniteMPS(L, ℂ^2, ℂ^10) + @test before < after + end - O = finite_classical_ising(L) - ψ₂, = approximate(ψ₂, (O, ψ₁), alg) + @testset "sparse_mpo * finitemps1 ≈ finitemps2" for alg in finite_algs + L = 10 + ψ₁ = FiniteMPS(L, ℂ^2, ℂ^30) + ψ₂ = FiniteMPS(L, ℂ^2, ℂ^25) - @test norm(O * ψ₁ - ψ₂) ≈ 0 atol = 0.001 - end -end + H = transverse_field_ising(; g = 4.0, L) + τ = 1.0e-3 -@testset "periodic boundary conditions" begin - Hs = [transverse_field_ising(), heisenberg_XXX(), classical_ising(), sixvertex()] - for N in 2:6 - for H in Hs - TH = convert(TensorMap, periodic_boundary_conditions(H, N)) - @test TH ≈ - permute(TH, ((vcat(N, 1:(N - 1))...,), (vcat(2N, (N + 1):(2N - 1))...,))) + expH = make_time_mpo(H, τ, WI) + ψ₂, = approximate(ψ₂, (expH, ψ₁), alg) + normalize!(ψ₂) + ψ₂′, = timestep(ψ₁, H, 0.0, τ, TDVP()) + @test abs(dot(ψ₁, ψ₁)) ≈ abs(dot(ψ₂, ψ₂′)) atol = 0.001 end - end - # fermionic tests - for N in 3:5 - h = real(c_plusmin() + c_minplus()) - H = InfiniteMPOHamiltonian([space(h, 1)], (1, 2) => h) - H_periodic = periodic_boundary_conditions(H, N) - terms = [(i, i + 1) => h for i in 1:(N - 1)] - push!(terms, (1, N) => permute(h, ((2, 1), (4, 3)))) - H_periodic2 = FiniteMPOHamiltonian(physicalspace(H_periodic), terms) - @test H_periodic ≈ H_periodic2 - end -end + @testset "dense_mpo * finitemps1 ≈ finitemps2" for alg in finite_algs + L = 10 + ψ₁ = FiniteMPS(L, ℂ^2, ℂ^20) + ψ₂ = FiniteMPS(L, ℂ^2, ℂ^10) -@testset "TaylorCluster time evolution" begin - L = 4 - dt = 0.05 - dτ = -im * dt - - @testset "O(1) exact expression" begin - alg = TaylorCluster(; N=1, compression=true, extension=true) - for H in (transverse_field_ising(), heisenberg_XXX()) - # Infinite - mpo = make_time_mpo(H, dt, alg) - O = mpo[1] - I, A, B, C, D = H[1][1], H.A[1], H.B[1], H.C[1], H.D[1] - - @test size(O, 1) == size(D, 1)^2 + (size(D, 1) * size(B, 1)) - @test size(O, 4) == size(D, 4)^2 + (size(C, 4) * size(D, 4)) - - O_exact = similar(O) - O_exact[1, 1, 1, 1] = I + dτ * D + dτ^2 / 2 * fuse_mul_mpo(D, D) - O_exact[1, 1, 1, 2] = C + dτ * symm_mul_mpo(C, D) - O_exact[2, 1, 1, 1] = dτ * (B + dτ * symm_mul_mpo(B, D)) - O_exact[2, 1, 1, 2] = A + dτ * (symm_mul_mpo(A, D) + symm_mul_mpo(C, B)) - - @test all(isapprox.(parent(O), parent(O_exact))) - - # Finite - H_fin = open_boundary_conditions(H, L) - mpo_fin = make_time_mpo(H_fin, dt, alg) - mpo_fin2 = open_boundary_conditions(mpo, L) - for i in 1:L - @test all(isapprox.(parent(mpo_fin[i]), parent(mpo_fin2[i]))) - end + O = classical_ising(; L) + ψ₂, = approximate(ψ₂, (O, ψ₁), alg) + + @test norm(O * ψ₁ - ψ₂) ≈ 0 atol = 0.001 end end - @testset "O(2) exact expression" begin - alg = TaylorCluster(; N=2, compression=true, extension=true) - for H in (transverse_field_ising(), heisenberg_XXX()) - # Infinite - mpo = make_time_mpo(H, dt, alg) - O = mpo[1] - I, A, B, C, D = H[1][1], H.A[1], H.B[1], H.C[1], H.D[1] - - @test size(O, 1) == - size(D, 1)^3 + (size(D, 1)^2 * size(B, 1)) + (size(D, 1) * size(B, 1)^2) - @test size(O, 4) == - size(D, 4)^3 + (size(C, 4) * size(D, 4)^2) + (size(D, 4) * size(C, 4)^2) - - O_exact = similar(O) - O_exact[1, 1, 1, 1] = I + dτ * D + dτ^2 / 2 * fuse_mul_mpo(D, D) + - dτ^3 / 6 * fuse_mul_mpo(fuse_mul_mpo(D, D), D) - O_exact[1, 1, 1, 2] = C + dτ * symm_mul_mpo(C, D) + - dτ^2 / 2 * symm_mul_mpo(C, D, D) - O_exact[1, 1, 1, 3] = fuse_mul_mpo(C, C) + dτ * symm_mul_mpo(C, C, D) - O_exact[2, 1, 1, 1] = dτ * - (B + dτ * symm_mul_mpo(B, D) + - dτ^2 / 2 * symm_mul_mpo(B, D, D)) - O_exact[2, 1, 1, 2] = A + dτ * symm_mul_mpo(A, D) + - dτ^2 / 2 * symm_mul_mpo(A, D, D) + - dτ * (symm_mul_mpo(C, B) + dτ * symm_mul_mpo(C, B, D)) - O_exact[2, 1, 1, 3] = 2 * (symm_mul_mpo(A, C) + dτ * symm_mul_mpo(A, C, D)) + - dτ * symm_mul_mpo(C, C, B) - O_exact[3, 1, 1, 1] = dτ^2 / 2 * - (fuse_mul_mpo(B, B) + dτ * symm_mul_mpo(B, B, D)) - O_exact[3, 1, 1, 2] = dτ * (symm_mul_mpo(A, B) + dτ * symm_mul_mpo(A, B, D)) + - dτ^2 / 2 * symm_mul_mpo(B, B, C) - O_exact[3, 1, 1, 3] = fuse_mul_mpo(A, A) + dτ * symm_mul_mpo(A, A, D) + - 2 * dτ * symm_mul_mpo(A, C, B) - - @test all(isapprox.(parent(O), parent(O_exact))) - - # Finite - H_fin = open_boundary_conditions(H, L) - mpo_fin = make_time_mpo(H_fin, dt, alg) - mpo_fin2 = open_boundary_conditions(mpo, L) - for i in 1:L - @test all(isapprox.(parent(mpo_fin[i]), parent(mpo_fin2[i]))) + @testset "periodic boundary conditions" begin + Hs = [transverse_field_ising(), heisenberg_XXX(), classical_ising(), sixvertex()] + for N in 2:6 + for H in Hs + TH = convert(TensorMap, periodic_boundary_conditions(H, N)) + @test TH ≈ + permute(TH, ((vcat(N, 1:(N - 1))...,), (vcat(2N, (N + 1):(2N - 1))...,))) end end + + # fermionic tests + for N in 3:5 + h = real(c_plusmin() + c_minplus()) + H = InfiniteMPOHamiltonian([space(h, 1)], (1, 2) => h) + H_periodic = periodic_boundary_conditions(H, N) + terms = [(i, i + 1) => h for i in 1:(N - 1)] + push!(terms, (1, N) => permute(h, ((2, 1), (4, 3)))) + H_periodic2 = FiniteMPOHamiltonian(physicalspace(H_periodic), terms) + @test H_periodic ≈ H_periodic2 + end end - L = 4 - Hs = [transverse_field_ising(; L=L), heisenberg_XXX(; L=L)] + @testset "TaylorCluster time evolution" begin + L = 4 + dt = 0.05 + dτ = -im * dt + + @testset "O(1) exact expression" begin + alg = TaylorCluster(; N = 1, compression = true, extension = true) + for H in (transverse_field_ising(), heisenberg_XXX()) + # Infinite + mpo = make_time_mpo(H, dt, alg) + O = mpo[1] + I, A, B, C, D = H[1][1], H.A[1], H.B[1], H.C[1], H.D[1] + + @test size(O, 1) == size(D, 1)^2 + (size(D, 1) * size(B, 1)) + @test size(O, 4) == size(D, 4)^2 + (size(C, 4) * size(D, 4)) + + O_exact = similar(O) + O_exact[1, 1, 1, 1] = I + dτ * D + dτ^2 / 2 * fuse_mul_mpo(D, D) + O_exact[1, 1, 1, 2] = C + dτ * symm_mul_mpo(C, D) + O_exact[2, 1, 1, 1] = dτ * (B + dτ * symm_mul_mpo(B, D)) + O_exact[2, 1, 1, 2] = A + dτ * (symm_mul_mpo(A, D) + symm_mul_mpo(C, B)) + + @test all(isapprox.(parent(O), parent(O_exact))) + + # Finite + H_fin = open_boundary_conditions(H, L) + mpo_fin = make_time_mpo(H_fin, dt, alg) + mpo_fin2 = open_boundary_conditions(mpo, L) + for i in 1:L + @test all(isapprox.(parent(mpo_fin[i]), parent(mpo_fin2[i]))) + end + end + end - Ns = [1, 2, 3] - dts = [1e-2, 1e-3] - for H in Hs - ψ = FiniteMPS(L, physicalspace(H, 1), ℂ^16) - for N in Ns - εs = zeros(ComplexF64, 2) - for (i, dt) in enumerate(dts) - ψ₀, _ = find_groundstate(ψ, H, DMRG(; verbosity=0)) - E₀ = expectation_value(ψ₀, H) + @testset "O(2) exact expression" begin + alg = TaylorCluster(; N = 2, compression = true, extension = true) + for H in (transverse_field_ising(), heisenberg_XXX()) + # Infinite + mpo = make_time_mpo(H, dt, alg) + O = mpo[1] + I, A, B, C, D = H[1][1], H.A[1], H.B[1], H.C[1], H.D[1] + + @test size(O, 1) == + size(D, 1)^3 + (size(D, 1)^2 * size(B, 1)) + (size(D, 1) * size(B, 1)^2) + @test size(O, 4) == + size(D, 4)^3 + (size(C, 4) * size(D, 4)^2) + (size(D, 4) * size(C, 4)^2) + + O_exact = similar(O) + O_exact[1, 1, 1, 1] = I + dτ * D + dτ^2 / 2 * fuse_mul_mpo(D, D) + + dτ^3 / 6 * fuse_mul_mpo(fuse_mul_mpo(D, D), D) + O_exact[1, 1, 1, 2] = C + dτ * symm_mul_mpo(C, D) + + dτ^2 / 2 * symm_mul_mpo(C, D, D) + O_exact[1, 1, 1, 3] = fuse_mul_mpo(C, C) + dτ * symm_mul_mpo(C, C, D) + O_exact[2, 1, 1, 1] = dτ * + ( + B + dτ * symm_mul_mpo(B, D) + + dτ^2 / 2 * symm_mul_mpo(B, D, D) + ) + O_exact[2, 1, 1, 2] = A + dτ * symm_mul_mpo(A, D) + + dτ^2 / 2 * symm_mul_mpo(A, D, D) + + dτ * (symm_mul_mpo(C, B) + dτ * symm_mul_mpo(C, B, D)) + O_exact[2, 1, 1, 3] = 2 * (symm_mul_mpo(A, C) + dτ * symm_mul_mpo(A, C, D)) + + dτ * symm_mul_mpo(C, C, B) + O_exact[3, 1, 1, 1] = dτ^2 / 2 * + (fuse_mul_mpo(B, B) + dτ * symm_mul_mpo(B, B, D)) + O_exact[3, 1, 1, 2] = dτ * (symm_mul_mpo(A, B) + dτ * symm_mul_mpo(A, B, D)) + + dτ^2 / 2 * symm_mul_mpo(B, B, C) + O_exact[3, 1, 1, 3] = fuse_mul_mpo(A, A) + dτ * symm_mul_mpo(A, A, D) + + 2 * dτ * symm_mul_mpo(A, C, B) + + @test all(isapprox.(parent(O), parent(O_exact))) + + # Finite + H_fin = open_boundary_conditions(H, L) + mpo_fin = make_time_mpo(H_fin, dt, alg) + mpo_fin2 = open_boundary_conditions(mpo, L) + for i in 1:L + @test all(isapprox.(parent(mpo_fin[i]), parent(mpo_fin2[i]))) + end + end + end - O = make_time_mpo(H, dt, TaylorCluster(; N=N)) + L = 4 + Hs = [transverse_field_ising(; L = L), heisenberg_XXX(; L = L)] - ψ₁, _ = approximate(ψ₀, (O, ψ₀), DMRG(; verbosity=0)) - εs[i] = norm(dot(ψ₀, ψ₁) - exp(-im * E₀ * dt)) + Ns = [1, 2, 3] + dts = [1.0e-2, 1.0e-3] + for H in Hs + ψ = FiniteMPS(L, physicalspace(H, 1), ℂ^16) + for N in Ns + εs = zeros(ComplexF64, 2) + for (i, dt) in enumerate(dts) + ψ₀, _ = find_groundstate(ψ, H, DMRG(; verbosity = 0)) + E₀ = expectation_value(ψ₀, H) + + O = make_time_mpo(H, dt, TaylorCluster(; N = N)) + + ψ₁, _ = approximate(ψ₀, (O, ψ₀), DMRG(; verbosity = 0)) + εs[i] = norm(dot(ψ₀, ψ₁) - exp(-im * E₀ * dt)) + end + @test (log(εs[2]) - log(εs[1])) / (log(dts[2]) - log(dts[1])) ≈ N + 1 atol = 0.1 end - @test (log(εs[2]) - log(εs[1])) / (log(dts[2]) - log(dts[1])) ≈ N + 1 atol = 0.1 - end - for N in Ns - εs = zeros(ComplexF64, 2) - for (i, dt) in enumerate(dts) - ψ₀, _ = find_groundstate(ψ, H, DMRG(; verbosity=0)) - E₀ = expectation_value(ψ₀, H) + for N in Ns + εs = zeros(ComplexF64, 2) + for (i, dt) in enumerate(dts) + ψ₀, _ = find_groundstate(ψ, H, DMRG(; verbosity = 0)) + E₀ = expectation_value(ψ₀, H) - O = make_time_mpo(H, dt, TaylorCluster(; N=N, compression=true)) + O = make_time_mpo(H, dt, TaylorCluster(; N = N, compression = true)) - ψ₁, _ = approximate(ψ₀, (O, ψ₀), DMRG(; verbosity=0)) - εs[i] = norm(dot(ψ₀, ψ₁) - exp(-im * E₀ * dt)) + ψ₁, _ = approximate(ψ₀, (O, ψ₀), DMRG(; verbosity = 0)) + εs[i] = norm(dot(ψ₀, ψ₁) - exp(-im * E₀ * dt)) + end + @test (log(εs[2]) - log(εs[1])) / (log(dts[2]) - log(dts[1])) ≈ N + 1 atol = 0.1 end - @test (log(εs[2]) - log(εs[1])) / (log(dts[2]) - log(dts[1])) ≈ N + 1 atol = 0.1 end end -end - -@testset "Finite temperature methods" begin - L = 6 - H = transverse_field_ising(; L) - trscheme = truncdim(20) - verbosity = 1 - beta = 0.1 - - # exact diagonalization - H_dense = convert(TensorMap, H) - Z_dense_1 = tr(exp(-beta * H_dense))^(1 / L) - Z_dense_2 = tr(exp(-2beta * H_dense))^(1 / L) - - # taylor cluster - rho_taylor_1 = make_time_mpo(H, -im * beta, TaylorCluster(; N=2)) - Z_taylor_1 = tr(rho_taylor_1)^(1 / L) - @test Z_taylor_1 ≈ Z_dense_1 atol = 1e-2 - Z_taylor_2 = real(dot(rho_taylor_1, rho_taylor_1))^(1 / L) - @test Z_taylor_2 ≈ Z_dense_2 atol = 1e-2 - - # MPO multiplication - rho_mps = convert(FiniteMPS, rho_taylor_1) - rho_mps, = approximate(rho_mps, (rho_taylor_1, rho_mps), DMRG2(; trscheme, verbosity)) - Z_mpomul = tr(convert(FiniteMPO, rho_mps))^(1 / L) - @test Z_mpomul ≈ Z_dense_2 atol = 1e-2 - - # TDVP - rho_0 = MPSKit.infinite_temperature_density_matrix(H) - rho_0_mps = convert(FiniteMPS, rho_0) - rho_mps, = timestep(rho_0_mps, H, 0.0, im * beta, TDVP2(; trscheme)) - Z_tdvp = real(dot(rho_mps, rho_mps))^(1 / L) - @test Z_tdvp ≈ Z_dense_2 atol = 1e-2 -end -@testset "Sector conventions" begin - L = 4 - H = XY_model(U1Irrep; L) - - H_dense = convert(TensorMap, H) - vals_dense = eigvals(H_dense) - - tol = 1e-18 # tolerance required to separate degenerate eigenvalues - alg = MPSKit.Defaults.alg_eigsolve(; dynamic_tols=false, tol) - - maxVspaces = MPSKit.max_virtualspaces(physicalspace(H)) - gs, = find_groundstate(FiniteMPS(physicalspace(H), maxVspaces[2:(end - 1)]), H; - verbosity=0) - E₀ = expectation_value(gs, H) - @test E₀ ≈ first(vals_dense[one(U1Irrep)]) - - for (sector, vals) in vals_dense - # ED tests - num = length(vals) - E₀s, ψ₀s, info = exact_diagonalization(H; num, sector, alg) - @test E₀s[1:num] ≈ vals[1:num] - # this is a trick to make the mps full-rank again, which is not guaranteed by ED - ψ₀ = changebonds(first(ψ₀s), SvdCut(; trscheme=notrunc())) - Vspaces = left_virtualspace.(Ref(ψ₀), 1:L) - push!(Vspaces, right_virtualspace(ψ₀, L)) - @test all(splat(==), zip(Vspaces, MPSKit.max_virtualspaces(ψ₀))) - - # Quasiparticle tests - Es, Bs = excitations(H, QuasiparticleAnsatz(; tol), gs; sector, num=1) - Es = Es .+ E₀ - # first excited state is second eigenvalue if sector is trivial - @test Es[1] ≈ vals[isone(sector) ? 2 : 1] atol = 1e-8 - end + @testset "Finite temperature methods" begin + L = 6 + H = transverse_field_ising(; L) + trscheme = truncdim(20) + verbosity = 1 + beta = 0.1 + + # exact diagonalization + H_dense = convert(TensorMap, H) + Z_dense_1 = tr(exp(-beta * H_dense))^(1 / L) + Z_dense_2 = tr(exp(-2beta * H_dense))^(1 / L) + + # taylor cluster + rho_taylor_1 = make_time_mpo(H, -im * beta, TaylorCluster(; N = 2)) + Z_taylor_1 = tr(rho_taylor_1)^(1 / L) + @test Z_taylor_1 ≈ Z_dense_1 atol = 1.0e-2 + Z_taylor_2 = real(dot(rho_taylor_1, rho_taylor_1))^(1 / L) + @test Z_taylor_2 ≈ Z_dense_2 atol = 1.0e-2 + + # MPO multiplication + rho_mps = convert(FiniteMPS, rho_taylor_1) + rho_mps, = approximate(rho_mps, (rho_taylor_1, rho_mps), DMRG2(; trscheme, verbosity)) + Z_mpomul = tr(convert(FiniteMPO, rho_mps))^(1 / L) + @test Z_mpomul ≈ Z_dense_2 atol = 1.0e-2 + + # TDVP + rho_0 = MPSKit.infinite_temperature_density_matrix(H) + rho_0_mps = convert(FiniteMPS, rho_0) + rho_mps, = timestep(rho_0_mps, H, 0.0, im * beta, TDVP2(; trscheme)) + Z_tdvp = real(dot(rho_mps, rho_mps))^(1 / L) + @test Z_tdvp ≈ Z_dense_2 atol = 1.0e-2 + end + + @testset "Sector conventions" begin + L = 4 + H = XY_model(U1Irrep; L) + + H_dense = convert(TensorMap, H) + vals_dense = eigvals(H_dense) + + tol = 1.0e-18 # tolerance required to separate degenerate eigenvalues + alg = MPSKit.Defaults.alg_eigsolve(; dynamic_tols = false, tol) + + maxVspaces = MPSKit.max_virtualspaces(physicalspace(H)) + gs, = find_groundstate( + FiniteMPS(physicalspace(H), maxVspaces[2:(end - 1)]), H; verbosity = 0 + ) + E₀ = expectation_value(gs, H) + @test E₀ ≈ first(vals_dense[one(U1Irrep)]) + + for (sector, vals) in vals_dense + # ED tests + num = length(vals) + E₀s, ψ₀s, info = exact_diagonalization(H; num, sector, alg) + @test E₀s[1:num] ≈ vals[1:num] + # this is a trick to make the mps full-rank again, which is not guaranteed by ED + ψ₀ = changebonds(first(ψ₀s), SvdCut(; trscheme = notrunc())) + Vspaces = left_virtualspace.(Ref(ψ₀), 1:L) + push!(Vspaces, right_virtualspace(ψ₀, L)) + @test all(splat(==), zip(Vspaces, MPSKit.max_virtualspaces(ψ₀))) + + # Quasiparticle tests + Es, Bs = excitations(H, QuasiparticleAnsatz(; tol), gs; sector, num = 1) + Es = Es .+ E₀ + # first excited state is second eigenvalue if sector is trivial + @test Es[1] ≈ vals[isone(sector) ? 2 : 1] atol = 1.0e-8 + end - # shifted charges tests - # targeting states with Sz = 1 => vals_shift_dense[0] == vals_dense[1] - # so effectively shifting the charges by -1 - H_shift = MPSKit.add_physical_charge(H, U1Irrep.([1, 0, 0, 0])) - H_shift_dense = convert(TensorMap, H_shift) - vals_shift_dense = eigvals(H_shift_dense) - for (sector, vals) in vals_dense - sector′ = only(sector ⊗ U1Irrep(-1)) - @test vals ≈ vals_shift_dense[sector′] + # shifted charges tests + # targeting states with Sz = 1 => vals_shift_dense[0] == vals_dense[1] + # so effectively shifting the charges by -1 + H_shift = MPSKit.add_physical_charge(H, U1Irrep.([1, 0, 0, 0])) + H_shift_dense = convert(TensorMap, H_shift) + vals_shift_dense = eigvals(H_shift_dense) + for (sector, vals) in vals_dense + sector′ = only(sector ⊗ U1Irrep(-1)) + @test vals ≈ vals_shift_dense[sector′] + end end -end end diff --git a/test/operators.jl b/test/operators.jl index 010c485c0..0f2b6011f 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -5,521 +5,510 @@ println(" ") module TestOperators -using ..TestSetup -using Test, TestExtras -using MPSKit -using MPSKit: _transpose_front, _transpose_tail, C_hamiltonian, AC_hamiltonian, - AC2_hamiltonian -using TensorKit -using TensorKit: ℙ -using VectorInterface: One - -pspaces = (ℙ^4, Rep[U₁](0 => 2), Rep[SU₂](1 => 1)) -vspaces = (ℙ^10, Rep[U₁]((0 => 20)), Rep[SU₂](1 // 2 => 10, 3 // 2 => 5, 5 // 2 => 1)) - -@testset "FiniteMPO" begin - # start from random operators - L = 4 - T = ComplexF64 - - for V in (ℂ^2, U1Space(0 => 1, 1 => 1)) - O₁ = rand(T, V^L, V^L) - O₂ = rand(T, space(O₁)) - O₃ = rand(real(T), space(O₁)) - - # create MPO and convert it back to see if it is the same - mpo₁ = FiniteMPO(O₁) # type-unstable for now! - mpo₂ = FiniteMPO(O₂) - mpo₃ = FiniteMPO(O₃) - @test convert(TensorMap, mpo₁) ≈ O₁ - @test convert(TensorMap, -mpo₂) ≈ -O₂ - @test convert(TensorMap, @constinferred complex(mpo₃)) ≈ complex(O₃) - - # test scalar multiplication - α = rand(T) - @test convert(TensorMap, α * mpo₁) ≈ α * O₁ - @test convert(TensorMap, mpo₁ * α) ≈ O₁ * α - @test α * mpo₃ ≈ α * complex(mpo₃) atol = 1e-6 - - # test addition and multiplication - @test convert(TensorMap, mpo₁ + mpo₂) ≈ O₁ + O₂ - @test convert(TensorMap, mpo₁ + mpo₃) ≈ O₁ + O₃ - @test convert(TensorMap, mpo₁ * mpo₂) ≈ O₁ * O₂ - @test convert(TensorMap, mpo₁ * mpo₃) ≈ O₁ * O₃ - - # test application to a state - ψ₁ = rand(T, domain(O₁)) - ψ₂ = rand(real(T), domain(O₂)) - mps₁ = FiniteMPS(ψ₁) - mps₂ = FiniteMPS(ψ₂) - - @test convert(TensorMap, mpo₁ * mps₁) ≈ O₁ * ψ₁ - @test mpo₁ * ψ₁ ≈ O₁ * ψ₁ - @test convert(TensorMap, mpo₃ * mps₁) ≈ O₃ * ψ₁ - @test mpo₃ * ψ₁ ≈ O₃ * ψ₁ - @test convert(TensorMap, mpo₁ * mps₂) ≈ O₁ * ψ₂ - @test mpo₁ * ψ₂ ≈ O₁ * ψ₂ - - @test dot(mps₁, mpo₁, mps₁) ≈ dot(ψ₁, O₁, ψ₁) - @test dot(mps₁, mpo₁, mps₁) ≈ dot(mps₁, mpo₁ * mps₁) - # test conversion to and from mps - mpomps₁ = convert(FiniteMPS, mpo₁) - mpompsmpo₁ = convert(FiniteMPO, mpomps₁) - - @test convert(FiniteMPO, mpomps₁) ≈ mpo₁ rtol = 1e-6 - - @test dot(mpomps₁, mpomps₁) ≈ dot(mpo₁, mpo₁) + using ..TestSetup + using Test, TestExtras + using MPSKit + using MPSKit: _transpose_front, _transpose_tail, C_hamiltonian, AC_hamiltonian, + AC2_hamiltonian + using TensorKit + using TensorKit: ℙ + using VectorInterface: One + + pspaces = (ℙ^4, Rep[U₁](0 => 2), Rep[SU₂](1 => 1)) + vspaces = (ℙ^10, Rep[U₁]((0 => 20)), Rep[SU₂](1 // 2 => 10, 3 // 2 => 5, 5 // 2 => 1)) + + @testset "FiniteMPO" begin + # start from random operators + L = 4 + T = ComplexF64 + + for V in (ℂ^2, U1Space(0 => 1, 1 => 1)) + O₁ = rand(T, V^L, V^L) + O₂ = rand(T, space(O₁)) + O₃ = rand(real(T), space(O₁)) + + # create MPO and convert it back to see if it is the same + mpo₁ = FiniteMPO(O₁) # type-unstable for now! + mpo₂ = FiniteMPO(O₂) + mpo₃ = FiniteMPO(O₃) + @test convert(TensorMap, mpo₁) ≈ O₁ + @test convert(TensorMap, -mpo₂) ≈ -O₂ + @test convert(TensorMap, @constinferred complex(mpo₃)) ≈ complex(O₃) + + # test scalar multiplication + α = rand(T) + @test convert(TensorMap, α * mpo₁) ≈ α * O₁ + @test convert(TensorMap, mpo₁ * α) ≈ O₁ * α + @test α * mpo₃ ≈ α * complex(mpo₃) atol = 1.0e-6 + + # test addition and multiplication + @test convert(TensorMap, mpo₁ + mpo₂) ≈ O₁ + O₂ + @test convert(TensorMap, mpo₁ + mpo₃) ≈ O₁ + O₃ + @test convert(TensorMap, mpo₁ * mpo₂) ≈ O₁ * O₂ + @test convert(TensorMap, mpo₁ * mpo₃) ≈ O₁ * O₃ + + # test application to a state + ψ₁ = rand(T, domain(O₁)) + ψ₂ = rand(real(T), domain(O₂)) + mps₁ = FiniteMPS(ψ₁) + mps₂ = FiniteMPS(ψ₂) + + @test convert(TensorMap, mpo₁ * mps₁) ≈ O₁ * ψ₁ + @test mpo₁ * ψ₁ ≈ O₁ * ψ₁ + @test convert(TensorMap, mpo₃ * mps₁) ≈ O₃ * ψ₁ + @test mpo₃ * ψ₁ ≈ O₃ * ψ₁ + @test convert(TensorMap, mpo₁ * mps₂) ≈ O₁ * ψ₂ + @test mpo₁ * ψ₂ ≈ O₁ * ψ₂ + + @test dot(mps₁, mpo₁, mps₁) ≈ dot(ψ₁, O₁, ψ₁) + @test dot(mps₁, mpo₁, mps₁) ≈ dot(mps₁, mpo₁ * mps₁) + # test conversion to and from mps + mpomps₁ = convert(FiniteMPS, mpo₁) + mpompsmpo₁ = convert(FiniteMPO, mpomps₁) + + @test convert(FiniteMPO, mpomps₁) ≈ mpo₁ rtol = 1.0e-6 + + @test dot(mpomps₁, mpomps₁) ≈ dot(mpo₁, mpo₁) + end end -end -@testset "MPOHamiltonian constructors" begin - P = ℂ^2 - T = Float64 - - H1 = randn(T, P ← P) - H1 += H1' - D = FiniteMPO(H1)[1] - - H2 = randn(T, P^2 ← P^2) - H2 += H2' - C, B = FiniteMPO(H2)[1:2] - - Elt = Union{Missing,typeof(D),scalartype(D)} - Wmid = Elt[1.0 C D; 0.0 0.0 B; 0.0 0.0 1.0] - Wleft = Wmid[1:1, :] - Wright = Wmid[:, end:end] - - # Finite - Ws = [Wleft, Wmid, Wmid, Wright] - H = FiniteMPOHamiltonian(fill(P, 4), - [(i,) => H1 for i in 1:4]..., - [(i, i + 1) => H2 for i in 1:3]...) - H′ = FiniteMPOHamiltonian(Ws) - @test H ≈ H′ - - H′ = FiniteMPOHamiltonian(map(Base.Fix1(collect, Any), Ws)) # without type info - @test H ≈ H′ - - # Infinite - Ws = [Wmid] - H = InfiniteMPOHamiltonian(fill(P, 1), - [(i,) => H1 for i in 1:1]..., - [(i, i + 1) => H2 for i in 1:1]...) - H′ = InfiniteMPOHamiltonian(Ws) - @test all(parent(H) .≈ parent(H′)) - - H′ = InfiniteMPOHamiltonian(map(Base.Fix1(collect, Any), Ws)) # without type info - @test all(parent(H) .≈ parent(H′)) -end - -@testset "Finite MPOHamiltonian" begin - L = 3 - T = ComplexF64 - for V in (ℂ^2, U1Space(0 => 1, 1 => 1)) - lattice = fill(V, L) - O₁ = rand(T, V, V) - E = id(storagetype(O₁), domain(O₁)) - O₂ = rand(T, V^2 ← V^2) - - H1 = FiniteMPOHamiltonian(lattice, i => O₁ for i in 1:L) - H2 = FiniteMPOHamiltonian(lattice, (i, i + 1) => O₂ for i in 1:(L - 1)) - H3 = FiniteMPOHamiltonian(lattice, 1 => O₁, (2, 3) => O₂, (1, 3) => O₂) - - # check if constructor works by converting back to tensormap - H1_tm = convert(TensorMap, H1) - operators = vcat(fill(E, L - 1), O₁) - @test H1_tm ≈ mapreduce(+, 1:L) do i - return reduce(⊗, circshift(operators, i)) - end - operators = vcat(fill(E, L - 2), O₂) - @test convert(TensorMap, H2) ≈ mapreduce(+, 1:(L - 1)) do i - return reduce(⊗, circshift(operators, i)) - end - @test convert(TensorMap, H3) ≈ - O₁ ⊗ E ⊗ E + E ⊗ O₂ + permute(O₂ ⊗ E, ((1, 3, 2), (4, 6, 5))) - - # check if adding terms on the same site works - single_terms = Iterators.flatten(Iterators.repeated((i => O₁ / 2 for i in 1:L), 2)) - H4 = FiniteMPOHamiltonian(lattice, single_terms) - @test H4 ≈ H1 atol = 1e-6 - double_terms = Iterators.flatten(Iterators.repeated(((i, i + 1) => O₂ / 2 - for i in 1:(L - 1)), 2)) - H5 = FiniteMPOHamiltonian(lattice, double_terms) - @test H5 ≈ H2 atol = 1e-6 - - # test linear algebra - @test H1 ≈ - FiniteMPOHamiltonian(lattice, 1 => O₁) + - FiniteMPOHamiltonian(lattice, 2 => O₁) + - FiniteMPOHamiltonian(lattice, 3 => O₁) - @test 0.8 * H1 + 0.2 * H1 ≈ H1 atol = 1e-6 - @test convert(TensorMap, H1 + H2) ≈ convert(TensorMap, H1) + convert(TensorMap, H2) atol = 1e-6 - - # test dot and application - state = rand(T, prod(lattice)) - mps = FiniteMPS(state) - - @test convert(TensorMap, H1 * mps) ≈ H1_tm * state - @test H1 * state ≈ H1_tm * state - @test dot(mps, H2, mps) ≈ dot(mps, H2 * mps) - - # test constructor from dictionary with mixed linear and Cartesian lattice indices as keys - grid = square = fill(V, 3, 3) - - local_operators = Dict((I,) => O₁ for I in eachindex(grid)) - I_vertical = CartesianIndex(1, 0) - vertical_operators = Dict((I, I + I_vertical) => O₂ - for I in eachindex(IndexCartesian(), square) - if I[1] < size(square, 1)) - operators = merge(local_operators, vertical_operators) - H4 = FiniteMPOHamiltonian(grid, operators) - - @test H4 ≈ - FiniteMPOHamiltonian(grid, local_operators) + - FiniteMPOHamiltonian(grid, vertical_operators) + @testset "MPOHamiltonian constructors" begin + P = ℂ^2 + T = Float64 + + H1 = randn(T, P ← P) + H1 += H1' + D = FiniteMPO(H1)[1] + + H2 = randn(T, P^2 ← P^2) + H2 += H2' + C, B = FiniteMPO(H2)[1:2] + + Elt = Union{Missing, typeof(D), scalartype(D)} + Wmid = Elt[1.0 C D; 0.0 0.0 B; 0.0 0.0 1.0] + Wleft = Wmid[1:1, :] + Wright = Wmid[:, end:end] + + # Finite + Ws = [Wleft, Wmid, Wmid, Wright] + H = FiniteMPOHamiltonian( + fill(P, 4), [(i,) => H1 for i in 1:4]..., [(i, i + 1) => H2 for i in 1:3]... + ) + H′ = FiniteMPOHamiltonian(Ws) + @test H ≈ H′ + + H′ = FiniteMPOHamiltonian(map(Base.Fix1(collect, Any), Ws)) # without type info + @test H ≈ H′ + + # Infinite + Ws = [Wmid] + H = InfiniteMPOHamiltonian( + fill(P, 1), [(i,) => H1 for i in 1:1]..., [(i, i + 1) => H2 for i in 1:1]... + ) + H′ = InfiniteMPOHamiltonian(Ws) + @test all(parent(H) .≈ parent(H′)) + + H′ = InfiniteMPOHamiltonian(map(Base.Fix1(collect, Any), Ws)) # without type info + @test all(parent(H) .≈ parent(H′)) end -end -@testset "InfiniteMPOHamiltonian $(sectortype(pspace))" for (pspace, Dspace) in - zip(pspaces, - vspaces) - # generate a 1-2-3 body interaction - operators = ntuple(3) do i - O = rand(ComplexF64, pspace^i, pspace^i) - return O += O' + @testset "Finite MPOHamiltonian" begin + L = 3 + T = ComplexF64 + for V in (ℂ^2, U1Space(0 => 1, 1 => 1)) + lattice = fill(V, L) + O₁ = rand(T, V, V) + E = id(storagetype(O₁), domain(O₁)) + O₂ = rand(T, V^2 ← V^2) + + H1 = FiniteMPOHamiltonian(lattice, i => O₁ for i in 1:L) + H2 = FiniteMPOHamiltonian(lattice, (i, i + 1) => O₂ for i in 1:(L - 1)) + H3 = FiniteMPOHamiltonian(lattice, 1 => O₁, (2, 3) => O₂, (1, 3) => O₂) + + # check if constructor works by converting back to tensormap + H1_tm = convert(TensorMap, H1) + operators = vcat(fill(E, L - 1), O₁) + @test H1_tm ≈ mapreduce(+, 1:L) do i + return reduce(⊗, circshift(operators, i)) + end + operators = vcat(fill(E, L - 2), O₂) + @test convert(TensorMap, H2) ≈ mapreduce(+, 1:(L - 1)) do i + return reduce(⊗, circshift(operators, i)) + end + @test convert(TensorMap, H3) ≈ + O₁ ⊗ E ⊗ E + E ⊗ O₂ + permute(O₂ ⊗ E, ((1, 3, 2), (4, 6, 5))) + + # check if adding terms on the same site works + single_terms = Iterators.flatten(Iterators.repeated((i => O₁ / 2 for i in 1:L), 2)) + H4 = FiniteMPOHamiltonian(lattice, single_terms) + @test H4 ≈ H1 atol = 1.0e-6 + double_terms = Iterators.flatten( + Iterators.repeated(((i, i + 1) => O₂ / 2 for i in 1:(L - 1)), 2) + ) + H5 = FiniteMPOHamiltonian(lattice, double_terms) + @test H5 ≈ H2 atol = 1.0e-6 + + # test linear algebra + @test H1 ≈ + FiniteMPOHamiltonian(lattice, 1 => O₁) + + FiniteMPOHamiltonian(lattice, 2 => O₁) + + FiniteMPOHamiltonian(lattice, 3 => O₁) + @test 0.8 * H1 + 0.2 * H1 ≈ H1 atol = 1.0e-6 + @test convert(TensorMap, H1 + H2) ≈ convert(TensorMap, H1) + convert(TensorMap, H2) atol = 1.0e-6 + + # test dot and application + state = rand(T, prod(lattice)) + mps = FiniteMPS(state) + + @test convert(TensorMap, H1 * mps) ≈ H1_tm * state + @test H1 * state ≈ H1_tm * state + @test dot(mps, H2, mps) ≈ dot(mps, H2 * mps) + + # test constructor from dictionary with mixed linear and Cartesian lattice indices as keys + grid = square = fill(V, 3, 3) + + local_operators = Dict((I,) => O₁ for I in eachindex(grid)) + I_vertical = CartesianIndex(1, 0) + vertical_operators = Dict( + (I, I + I_vertical) => O₂ for I in eachindex(IndexCartesian(), square) if I[1] < size(square, 1) + ) + operators = merge(local_operators, vertical_operators) + H4 = FiniteMPOHamiltonian(grid, operators) + + @test H4 ≈ + FiniteMPOHamiltonian(grid, local_operators) + + FiniteMPOHamiltonian(grid, vertical_operators) + end end - H1 = InfiniteMPOHamiltonian(operators[1]) - H2 = InfiniteMPOHamiltonian(operators[2]) - H3 = repeat(InfiniteMPOHamiltonian(operators[3]), 2) + @testset "InfiniteMPOHamiltonian $(sectortype(pspace))" for (pspace, Dspace) in zip(pspaces, vspaces) + # generate a 1-2-3 body interaction + operators = ntuple(3) do i + O = rand(ComplexF64, pspace^i, pspace^i) + return O += O' + end - # can you pass in a proper mpo? - # TODO: fix this! - # identity = complex(isomorphism(oneunit(pspace) * pspace, pspace * oneunit(pspace))) - # mpoified = MPSKit.decompose_localmpo(MPSKit.add_util_leg(nnn)) - # d3 = Array{Union{Missing,typeof(identity)},3}(missing, 1, 4, 4) - # d3[1, 1, 1] = identity - # d3[1, end, end] = identity - # d3[1, 1, 2] = mpoified[1] - # d3[1, 2, 3] = mpoified[2] - # d3[1, 3, 4] = mpoified[3] - # h1 = MPOHamiltonian(d3) + H1 = InfiniteMPOHamiltonian(operators[1]) + H2 = InfiniteMPOHamiltonian(operators[2]) + H3 = repeat(InfiniteMPOHamiltonian(operators[3]), 2) - # make a teststate to measure expectation values for - ψ1 = InfiniteMPS([pspace], [Dspace]) - ψ2 = InfiniteMPS([pspace, pspace], [Dspace, Dspace]) + # can you pass in a proper mpo? + # TODO: fix this! + # identity = complex(isomorphism(oneunit(pspace) * pspace, pspace * oneunit(pspace))) + # mpoified = MPSKit.decompose_localmpo(MPSKit.add_util_leg(nnn)) + # d3 = Array{Union{Missing,typeof(identity)},3}(missing, 1, 4, 4) + # d3[1, 1, 1] = identity + # d3[1, end, end] = identity + # d3[1, 1, 2] = mpoified[1] + # d3[1, 2, 3] = mpoified[2] + # d3[1, 3, 4] = mpoified[3] + # h1 = MPOHamiltonian(d3) - e1 = expectation_value(ψ1, H1) - e2 = expectation_value(ψ1, H2) + # make a teststate to measure expectation values for + ψ1 = InfiniteMPS([pspace], [Dspace]) + ψ2 = InfiniteMPS([pspace, pspace], [Dspace, Dspace]) - H1 = 2 * H1 - [1] - @test e1 * 2 - 1 ≈ expectation_value(ψ1, H1) atol = 1e-10 + e1 = expectation_value(ψ1, H1) + e2 = expectation_value(ψ1, H2) - H1 = H1 + H2 + H1 = 2 * H1 - [1] + @test e1 * 2 - 1 ≈ expectation_value(ψ1, H1) atol = 1.0e-10 - @test e1 * 2 + e2 - 1 ≈ expectation_value(ψ1, H1) atol = 1e-10 + H1 = H1 + H2 - H1 = repeat(H1, 2) + @test e1 * 2 + e2 - 1 ≈ expectation_value(ψ1, H1) atol = 1.0e-10 - e1 = expectation_value(ψ2, H1) - e3 = expectation_value(ψ2, H3) + H1 = repeat(H1, 2) - @test e1 + e3 ≈ expectation_value(ψ2, H1 + H3) atol = 1e-10 + e1 = expectation_value(ψ2, H1) + e3 = expectation_value(ψ2, H3) - H4 = H1 + H3 - h4 = H4 * H4 - @test real(expectation_value(ψ2, H4)) >= 0 -end + @test e1 + e3 ≈ expectation_value(ψ2, H1 + H3) atol = 1.0e-10 -@testset "General LazySum of $(eltype(Os))" for Os in (rand(ComplexF64, rand(1:10)), - map(i -> rand(ComplexF64, - ℂ^13, ℂ^7), - 1:rand(1:10)), - map(i -> rand(ComplexF64, - ℂ^1 ⊗ ℂ^2, - ℂ^3 ⊗ ℂ^4), - 1:rand(1:10))) - LazyOs = LazySum(Os) + H4 = H1 + H3 + h4 = H4 * H4 + @test real(expectation_value(ψ2, H4)) >= 0 + end - #test user interface - summed = sum(Os) + @testset "General LazySum of $(eltype(Os))" for Os in ( + rand(ComplexF64, rand(1:10)), + map(i -> rand(ComplexF64, ℂ^13, ℂ^7), 1:rand(1:10)), + map(i -> rand(ComplexF64, ℂ^1 ⊗ ℂ^2, ℂ^3 ⊗ ℂ^4), 1:rand(1:10)), + ) + LazyOs = LazySum(Os) - @test sum(LazyOs) ≈ summed atol = 1 - 08 + #test user interface + summed = sum(Os) - LazyOs_added = +(LazyOs, Os...) + @test sum(LazyOs) ≈ summed atol = 1 - 08 - @test sum(LazyOs_added) ≈ 2 * summed atol = 1 - 08 -end + LazyOs_added = +(LazyOs, Os...) -@testset "MultipliedOperator of $(typeof(O)) with $(typeof(f))" for (O, f) in - zip((rand(ComplexF64), - rand(ComplexF64, - ℂ^13, - ℂ^7), - rand(ComplexF64, - ℂ^1 ⊗ ℂ^2, - ℂ^3 ⊗ ℂ^4)), - (t -> 3t, 1.1, - One())) - tmp = MPSKit.MultipliedOperator(O, f) - if tmp isa TimedOperator - @test tmp(1.1)() ≈ f(1.1) * O atol = 1 - 08 - elseif tmp isa UntimedOperator - @test tmp() ≈ f * O atol = 1 - 08 + @test sum(LazyOs_added) ≈ 2 * summed atol = 1 - 08 end -end -@testset "General Time-dependent LazySum of $(eltype(Os))" for Os in (rand(ComplexF64, 4), - fill(rand(ComplexF64, - ℂ^13, ℂ^7), - 4), - fill(rand(ComplexF64, - ℂ^1 ⊗ ℂ^2, - ℂ^3 ⊗ ℂ^4), - 4)) - - #test user interface - fs = [t -> 3t, t -> t + 2, 4, 1] - Ofs = map(zip(fs, Os)) do (f, O) - if f == 1 - return O - else - return MPSKit.MultipliedOperator(O, f) + + @testset "MultipliedOperator of $(typeof(O)) with $(typeof(f))" for (O, f) in + zip( + (rand(ComplexF64), rand(ComplexF64, ℂ^13, ℂ^7), rand(ComplexF64, ℂ^1 ⊗ ℂ^2, ℂ^3 ⊗ ℂ^4)), + (t -> 3t, 1.1, One()) + ) + tmp = MPSKit.MultipliedOperator(O, f) + if tmp isa TimedOperator + @test tmp(1.1)() ≈ f(1.1) * O atol = 1 - 08 + elseif tmp isa UntimedOperator + @test tmp() ≈ f * O atol = 1 - 08 end end - LazyOs = LazySum(Ofs) - summed = sum(zip(fs, Os)) do (f, O) - if f isa Function - f(1.1) * O - else - f * O + @testset "General Time-dependent LazySum of $(eltype(Os))" for Os in ( + rand(ComplexF64, 4), + fill(rand(ComplexF64, ℂ^13, ℂ^7), 4), + fill(rand(ComplexF64, ℂ^1 ⊗ ℂ^2, ℂ^3 ⊗ ℂ^4), 4), + ) + + #test user interface + fs = [t -> 3t, t -> t + 2, 4, 1] + Ofs = map(zip(fs, Os)) do (f, O) + if f == 1 + return O + else + return MPSKit.MultipliedOperator(O, f) + end + end + LazyOs = LazySum(Ofs) + summed = sum(zip(fs, Os)) do (f, O) + if f isa Function + f(1.1) * O + else + f * O + end end - end - @test sum(LazyOs(1.1)) ≈ summed atol = 1 - 08 + @test sum(LazyOs(1.1)) ≈ summed atol = 1 - 08 - LazyOs_added = +(LazyOs, Ofs...) + LazyOs_added = +(LazyOs, Ofs...) - @test sum(LazyOs_added(1.1)) ≈ 2 * summed atol = 1 - 08 -end + @test sum(LazyOs_added(1.1)) ≈ 2 * summed atol = 1 - 08 + end -@testset "DenseMPO" for ham in (transverse_field_ising(), heisenberg_XXX(; spin=1)) - pspace = physicalspace(ham, 1) - ou = oneunit(pspace) + @testset "DenseMPO" for ham in (transverse_field_ising(), heisenberg_XXX(; spin = 1)) + pspace = physicalspace(ham, 1) + ou = oneunit(pspace) - ψ = InfiniteMPS([pspace], [ou ⊕ pspace]) + ψ = InfiniteMPS([pspace], [ou ⊕ pspace]) - W = MPSKit.DenseMPO(make_time_mpo(ham, 1im * 0.5, WII())) - @test W * (W * ψ) ≈ (W * W) * ψ atol = 1e-2 # TODO: there is a normalization issue here -end + W = MPSKit.DenseMPO(make_time_mpo(ham, 1im * 0.5, WII())) + @test W * (W * ψ) ≈ (W * W) * ψ atol = 1.0e-2 # TODO: there is a normalization issue here + end -pspaces = (ℙ^4, Rep[U₁](0 => 2), Rep[SU₂](1 => 1, 2 => 1)) -vspaces = (ℙ^10, Rep[U₁]((0 => 20)), Rep[SU₂](1 => 10, 3 => 5, 5 => 1)) + pspaces = (ℙ^4, Rep[U₁](0 => 2), Rep[SU₂](1 => 1, 2 => 1)) + vspaces = (ℙ^10, Rep[U₁]((0 => 20)), Rep[SU₂](1 => 10, 3 => 5, 5 => 1)) -@testset "LazySum of (effective) Hamiltonian $(sectortype(pspace))" for (pspace, Dspace) in - zip(pspaces, - vspaces) - Os = map(1:3) do i - O = rand(ComplexF64, pspace^i, pspace^i) - return O += O' - end - fs = [t -> 3t, 2, 1] - - @testset "LazySum FiniteMPOHamiltonian" begin - L = rand(3:2:20) - ψ = FiniteMPS(rand, ComplexF64, L, pspace, Dspace) - lattice = fill(pspace, L) - Hs = map(enumerate(Os)) do (i, O) - return FiniteMPOHamiltonian(lattice, - ntuple(x -> x + j, i) => O for j in 0:(L - i)) + @testset "LazySum of (effective) Hamiltonian $(sectortype(pspace))" for (pspace, Dspace) in + zip(pspaces, vspaces) + Os = map(1:3) do i + O = rand(ComplexF64, pspace^i, pspace^i) + return O += O' end - summedH = LazySum(Hs) + fs = [t -> 3t, 2, 1] + + @testset "LazySum FiniteMPOHamiltonian" begin + L = rand(3:2:20) + ψ = FiniteMPS(rand, ComplexF64, L, pspace, Dspace) + lattice = fill(pspace, L) + Hs = map(enumerate(Os)) do (i, O) + return FiniteMPOHamiltonian( + lattice, + ntuple(x -> x + j, i) => O for j in 0:(L - i) + ) + end + summedH = LazySum(Hs) - envs = map(H -> environments(ψ, H), Hs) - summed_envs = environments(ψ, summedH) + envs = map(H -> environments(ψ, H), Hs) + summed_envs = environments(ψ, summedH) - expval = sum(zip(Hs, envs)) do (H, env) - return expectation_value(ψ, H, env) - end - expval1 = expectation_value(ψ, sum(summedH)) - expval2 = expectation_value(ψ, summedH, summed_envs) - expval3 = expectation_value(ψ, summedH) - @test expval ≈ expval1 - @test expval ≈ expval2 - @test expval ≈ expval3 - - # test derivatives - summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum1 = sum(zip(Hs, envs)) do (H, env) - return C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) - end - @test summedhct(ψ.C[1], 0.0) ≈ sum1 + expval = sum(zip(Hs, envs)) do (H, env) + return expectation_value(ψ, H, env) + end + expval1 = expectation_value(ψ, sum(summedH)) + expval2 = expectation_value(ψ, summedH, summed_envs) + expval3 = expectation_value(ψ, summedH) + @test expval ≈ expval1 + @test expval ≈ expval2 + @test expval ≈ expval3 + + # test derivatives + summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum1 = sum(zip(Hs, envs)) do (H, env) + return C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) + end + @test summedhct(ψ.C[1], 0.0) ≈ sum1 - summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum2 = sum(zip(Hs, envs)) do (H, env) - return AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) - end - @test summedhct(ψ.AC[1], 0.0) ≈ sum2 + summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum2 = sum(zip(Hs, envs)) do (H, env) + return AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) + end + @test summedhct(ψ.AC[1], 0.0) ≈ sum2 - v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) - summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum3 = sum(zip(Hs, envs)) do (H, env) - return AC2_hamiltonian(1, ψ, H, ψ, env)(v) - end - @test summedhct(v, 0.0) ≈ sum3 + v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) + summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum3 = sum(zip(Hs, envs)) do (H, env) + return AC2_hamiltonian(1, ψ, H, ψ, env)(v) + end + @test summedhct(v, 0.0) ≈ sum3 - Hts = [MultipliedOperator(Hs[1], fs[1]), MultipliedOperator(Hs[2], fs[2]), Hs[3]] - summedH = LazySum(Hts) - t = 1.1 - summedH_at = summedH(t) + Hts = [MultipliedOperator(Hs[1], fs[1]), MultipliedOperator(Hs[2], fs[2]), Hs[3]] + summedH = LazySum(Hts) + t = 1.1 + summedH_at = summedH(t) - envs = map(H -> environments(ψ, H), Hs) - summed_envs = environments(ψ, summedH) + envs = map(H -> environments(ψ, H), Hs) + summed_envs = environments(ψ, summedH) - expval = sum(zip(fs, Hs, envs)) do (f, H, env) - return (f isa Function ? f(t) : f) * expectation_value(ψ, H, env) - end - expval1 = expectation_value(ψ, sum(summedH_at)) - expval2 = expectation_value(ψ, summedH_at, summed_envs) - expval3 = expectation_value(ψ, summedH_at) - @test expval ≈ expval1 - @test expval ≈ expval2 - @test expval ≈ expval3 - - # test derivatives - summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum1 = sum(zip(fs, Hs, envs)) do (f, H, env) - if f isa Function - f = f(t) + expval = sum(zip(fs, Hs, envs)) do (f, H, env) + return (f isa Function ? f(t) : f) * expectation_value(ψ, H, env) end - return f * C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) - end - @test summedhct(ψ.C[1], t) ≈ sum1 + expval1 = expectation_value(ψ, sum(summedH_at)) + expval2 = expectation_value(ψ, summedH_at, summed_envs) + expval3 = expectation_value(ψ, summedH_at) + @test expval ≈ expval1 + @test expval ≈ expval2 + @test expval ≈ expval3 + + # test derivatives + summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum1 = sum(zip(fs, Hs, envs)) do (f, H, env) + if f isa Function + f = f(t) + end + return f * C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) + end + @test summedhct(ψ.C[1], t) ≈ sum1 + + summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum2 = sum(zip(fs, Hs, envs)) do (f, H, env) + if f isa Function + f = f(t) + end + return f * AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) + end + @test summedhct(ψ.AC[1], t) ≈ sum2 - summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum2 = sum(zip(fs, Hs, envs)) do (f, H, env) - if f isa Function - f = f(t) + v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) + summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum3 = sum(zip(fs, Hs, envs)) do (f, H, env) + return (f isa Function ? f(t) : f) * AC2_hamiltonian(1, ψ, H, ψ, env)(v) end - return f * AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) + @test summedhct(v, t) ≈ sum3 end - @test summedhct(ψ.AC[1], t) ≈ sum2 - v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) - summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum3 = sum(zip(fs, Hs, envs)) do (f, H, env) - return (f isa Function ? f(t) : f) * AC2_hamiltonian(1, ψ, H, ψ, env)(v) - end - @test summedhct(v, t) ≈ sum3 - end - - @testset "LazySum InfiniteMPOHamiltonian" begin - ψ = repeat(InfiniteMPS(pspace, Dspace), 2) - Hs = map(Os) do O - H = InfiniteMPOHamiltonian(O) - return repeat(H, 2) - end - summedH = LazySum(Hs) - envs = map(H -> environments(ψ, H), Hs) - summed_envs = environments(ψ, summedH) + @testset "LazySum InfiniteMPOHamiltonian" begin + ψ = repeat(InfiniteMPS(pspace, Dspace), 2) + Hs = map(Os) do O + H = InfiniteMPOHamiltonian(O) + return repeat(H, 2) + end + summedH = LazySum(Hs) + envs = map(H -> environments(ψ, H), Hs) + summed_envs = environments(ψ, summedH) - expval = sum(zip(Hs, envs)) do (H, Env) - return expectation_value(ψ, H, Env) - end - expval1 = expectation_value(ψ, sum(summedH)) - expval2 = expectation_value(ψ, summedH, summed_envs) - expval3 = expectation_value(ψ, summedH) - @test expval ≈ expval1 - @test expval ≈ expval2 - @test expval ≈ expval3 - - # test derivatives - summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum1 = sum(zip(Hs, envs)) do (H, env) - return C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) - end - @test summedhct(ψ.C[1], 0.0) ≈ sum1 + expval = sum(zip(Hs, envs)) do (H, Env) + return expectation_value(ψ, H, Env) + end + expval1 = expectation_value(ψ, sum(summedH)) + expval2 = expectation_value(ψ, summedH, summed_envs) + expval3 = expectation_value(ψ, summedH) + @test expval ≈ expval1 + @test expval ≈ expval2 + @test expval ≈ expval3 + + # test derivatives + summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum1 = sum(zip(Hs, envs)) do (H, env) + return C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) + end + @test summedhct(ψ.C[1], 0.0) ≈ sum1 - summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum2 = sum(zip(Hs, envs)) do (H, env) - return AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) - end - @test summedhct(ψ.AC[1], 0.0) ≈ sum2 + summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum2 = sum(zip(Hs, envs)) do (H, env) + return AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) + end + @test summedhct(ψ.AC[1], 0.0) ≈ sum2 - v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) - summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum3 = sum(zip(Hs, envs)) do (H, env) - return AC2_hamiltonian(1, ψ, H, ψ, env)(v) - end - @test summedhct(v, 0.0) ≈ sum3 + v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) + summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum3 = sum(zip(Hs, envs)) do (H, env) + return AC2_hamiltonian(1, ψ, H, ψ, env)(v) + end + @test summedhct(v, 0.0) ≈ sum3 - Hts = [MultipliedOperator(Hs[1], fs[1]), MultipliedOperator(Hs[2], fs[2]), Hs[3]] - summedH = LazySum(Hts) - t = 1.1 - summedH_at = summedH(t) + Hts = [MultipliedOperator(Hs[1], fs[1]), MultipliedOperator(Hs[2], fs[2]), Hs[3]] + summedH = LazySum(Hts) + t = 1.1 + summedH_at = summedH(t) - envs = map(H -> environments(ψ, H), Hs) - summed_envs = environments(ψ, summedH) + envs = map(H -> environments(ψ, H), Hs) + summed_envs = environments(ψ, summedH) - expval = sum(zip(fs, Hs, envs)) do (f, H, env) - return (f isa Function ? f(t) : f) * expectation_value(ψ, H, env) - end - expval1 = expectation_value(ψ, sum(summedH_at)) - expval2 = expectation_value(ψ, summedH_at, summed_envs) - expval3 = expectation_value(ψ, summedH_at) - @test expval ≈ expval1 - @test expval ≈ expval2 - @test expval ≈ expval3 - - # test derivatives - summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum1 = sum(zip(fs, Hs, envs)) do (f, H, env) - if f isa Function - f = f(t) + expval = sum(zip(fs, Hs, envs)) do (f, H, env) + return (f isa Function ? f(t) : f) * expectation_value(ψ, H, env) end - return f * C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) - end - @test summedhct(ψ.C[1], t) ≈ sum1 - - summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum2 = sum(zip(fs, Hs, envs)) do (f, H, env) - if f isa Function - f = f(t) + expval1 = expectation_value(ψ, sum(summedH_at)) + expval2 = expectation_value(ψ, summedH_at, summed_envs) + expval3 = expectation_value(ψ, summedH_at) + @test expval ≈ expval1 + @test expval ≈ expval2 + @test expval ≈ expval3 + + # test derivatives + summedhct = C_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum1 = sum(zip(fs, Hs, envs)) do (f, H, env) + if f isa Function + f = f(t) + end + return f * C_hamiltonian(1, ψ, H, ψ, env)(ψ.C[1]) end - return f * AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) - end - @test summedhct(ψ.AC[1], t) ≈ sum2 + @test summedhct(ψ.C[1], t) ≈ sum1 + + summedhct = AC_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum2 = sum(zip(fs, Hs, envs)) do (f, H, env) + if f isa Function + f = f(t) + end + return f * AC_hamiltonian(1, ψ, H, ψ, env)(ψ.AC[1]) + end + @test summedhct(ψ.AC[1], t) ≈ sum2 - v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) - summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) - sum3 = sum(zip(fs, Hs, envs)) do (f, H, env) - return (f isa Function ? f(t) : f) * AC2_hamiltonian(1, ψ, H, ψ, env)(v) + v = _transpose_front(ψ.AC[1]) * _transpose_tail(ψ.AR[2]) + summedhct = AC2_hamiltonian(1, ψ, summedH, ψ, summed_envs) + sum3 = sum(zip(fs, Hs, envs)) do (f, H, env) + return (f isa Function ? f(t) : f) * AC2_hamiltonian(1, ψ, H, ψ, env)(v) + end + @test summedhct(v, t) ≈ sum3 end - @test summedhct(v, t) ≈ sum3 end -end -@testset "ProjectionOperator" begin - L = 10 - psi = FiniteMPS(rand, ComplexF64, L, ℙ^2, ℙ^2) - psi2 = FiniteMPS(rand, ComplexF64, L, ℙ^2, ℙ^2) - O = MPSKit.ProjectionOperator(psi) + @testset "ProjectionOperator" begin + L = 10 + psi = FiniteMPS(rand, ComplexF64, L, ℙ^2, ℙ^2) + psi2 = FiniteMPS(rand, ComplexF64, L, ℙ^2, ℙ^2) + O = MPSKit.ProjectionOperator(psi) - @test expectation_value(psi, O) ≈ 1.0 - @test expectation_value(psi2, O) ≈ dot(psi, psi2) * dot(psi2, psi) -end + @test expectation_value(psi, O) ≈ 1.0 + @test expectation_value(psi2, O) ≈ dot(psi, psi2) * dot(psi2, psi) + end -@testset "MPO copy behaviour" begin - # testset that checks the fix for issue #288 - H = transverse_field_ising() - O = make_time_mpo(H, 0.1, TaylorCluster(2, true, true)) - FO = open_boundary_conditions(O, 4) - FH = open_boundary_conditions(H, 4) - - # check if the copy of the MPO is the same type as the original - @test typeof(copy(O)) == typeof(O) - @test typeof(copy(FO)) == typeof(FO) - @test typeof(copy(H)) == typeof(H) - @test typeof(copy(FH)) == typeof(FH) -end + @testset "MPO copy behaviour" begin + # testset that checks the fix for issue #288 + H = transverse_field_ising() + O = make_time_mpo(H, 0.1, TaylorCluster(2, true, true)) + FO = open_boundary_conditions(O, 4) + FH = open_boundary_conditions(H, 4) + + # check if the copy of the MPO is the same type as the original + @test typeof(copy(O)) == typeof(O) + @test typeof(copy(FO)) == typeof(FO) + @test typeof(copy(H)) == typeof(H) + @test typeof(copy(FH)) == typeof(FH) + end end diff --git a/test/other.jl b/test/other.jl index 1b9b84bb4..2cc1ff95f 100644 --- a/test/other.jl +++ b/test/other.jl @@ -5,98 +5,100 @@ println(" ") module TestMiscellaneous -using ..TestSetup -using Test, TestExtras -using MPSKit -using TensorKit -using TensorKit: ℙ -using Plots -using Aqua + using ..TestSetup + using Test, TestExtras + using MPSKit + using TensorKit + using TensorKit: ℙ + using Plots + using Aqua -@testset "Aqua" begin - # TODO fix this - Aqua.test_all(MPSKit; ambiguities=false, piracies=false) -end + @testset "Aqua" begin + # TODO fix this + Aqua.test_all(MPSKit; ambiguities = false, piracies = false) + end -@testset "plot tests" begin - ψ = InfiniteMPS([ℙ^2], [ℙ^5]) - @test transferplot(ψ) isa Plots.Plot - @test entanglementplot(ψ) isa Plots.Plot -end + @testset "plot tests" begin + ψ = InfiniteMPS([ℙ^2], [ℙ^5]) + @test transferplot(ψ) isa Plots.Plot + @test entanglementplot(ψ) isa Plots.Plot + end -@testset "Old bugs" verbose = true begin - @testset "IDMRG2 space mismatch" begin - N = 6 - H = repeat(bilinear_biquadratic_model(SU2Irrep; θ=atan(1 / 3)), N) - ψ₀ = InfiniteMPS(fill(SU2Space(1 => 1), N), - fill(SU2Space(1 // 2 => 2, 3 // 2 => 1), N)) - alg = IDMRG2(; verbosity=0, tol=1e-5, trscheme=truncdim(32)) + @testset "Old bugs" verbose = true begin + @testset "IDMRG2 space mismatch" begin + N = 6 + H = repeat(bilinear_biquadratic_model(SU2Irrep; θ = atan(1 / 3)), N) + ψ₀ = InfiniteMPS( + fill(SU2Space(1 => 1), N), + fill(SU2Space(1 // 2 => 2, 3 // 2 => 1), N) + ) + alg = IDMRG2(; verbosity = 0, tol = 1.0e-5, trscheme = truncdim(32)) - ψ, envs, δ = find_groundstate(ψ₀, H, alg) # used to error - @test ψ isa InfiniteMPS - end + ψ, envs, δ = find_groundstate(ψ₀, H, alg) # used to error + @test ψ isa InfiniteMPS + end - @testset "NaN entanglement entropy" begin - ψ = InfiniteMPS([ℂ^2], [ℂ^5]) - ψ = changebonds(ψ, RandExpand(; trscheme=truncdim(2))) - @test !isnan(sum(entropy(ψ))) - @test !isnan(sum(entropy(ψ, 2))) - end + @testset "NaN entanglement entropy" begin + ψ = InfiniteMPS([ℂ^2], [ℂ^5]) + ψ = changebonds(ψ, RandExpand(; trscheme = truncdim(2))) + @test !isnan(sum(entropy(ψ))) + @test !isnan(sum(entropy(ψ, 2))) + end - @testset "changebonds with unitcells" begin - ψ = InfiniteMPS([ℂ^2, ℂ^2, ℂ^2], [ℂ^2, ℂ^3, ℂ^4]) - H = repeat(transverse_field_ising(), 3) - ψ1, envs = changebonds(ψ, H, OptimalExpand(; trscheme=truncdim(2))) - @test ψ1 isa InfiniteMPS - @test norm(ψ1) ≈ 1 + @testset "changebonds with unitcells" begin + ψ = InfiniteMPS([ℂ^2, ℂ^2, ℂ^2], [ℂ^2, ℂ^3, ℂ^4]) + H = repeat(transverse_field_ising(), 3) + ψ1, envs = changebonds(ψ, H, OptimalExpand(; trscheme = truncdim(2))) + @test ψ1 isa InfiniteMPS + @test norm(ψ1) ≈ 1 - ψ2 = changebonds(ψ, RandExpand(; trscheme=truncdim(2))) - @test ψ2 isa InfiniteMPS - @test norm(ψ2) ≈ 1 - end + ψ2 = changebonds(ψ, RandExpand(; trscheme = truncdim(2))) + @test ψ2 isa InfiniteMPS + @test norm(ψ2) ≈ 1 + end - @testset "Stackoverflow with gauging" begin - ψ = FiniteMPS(10_000, ℂ^2, ℂ^1) - @test ψ.AR[1] isa MPSKit.MPSTensor - ψ.AC[1] = -ψ.AR[1] # force invalidation of ALs - @test ψ.AL[end] isa MPSKit.MPSTensor + @testset "Stackoverflow with gauging" begin + ψ = FiniteMPS(10_000, ℂ^2, ℂ^1) + @test ψ.AR[1] isa MPSKit.MPSTensor + ψ.AC[1] = -ψ.AR[1] # force invalidation of ALs + @test ψ.AL[end] isa MPSKit.MPSTensor + end end -end -@testset "braille" begin - # Infinite Hamiltonians and MPOs - # ------------------------------- - H = transverse_field_ising() - buffer = IOBuffer() - braille(buffer, H) - output = String(take!(buffer)) - check = """ - ... 🭻⎡⠉⢈⎤🭻 ... - ⎣⠀⢀⎦ - """ - @test output == check + @testset "braille" begin + # Infinite Hamiltonians and MPOs + # ------------------------------- + H = transverse_field_ising() + buffer = IOBuffer() + braille(buffer, H) + output = String(take!(buffer)) + check = """ + ... 🭻⎡⠉⢈⎤🭻 ... + ⎣⠀⢀⎦ + """ + @test output == check - O = make_time_mpo(H, 1.0, TaylorCluster(3, false, false)) - braille(buffer, O) - output = String(take!(buffer)) - check = """ - ... 🭻⎡⡏⠉⠛⠟⎤🭻 ... - ⎣⡇⠀⠀⡂⎦ - """ - @test output == check + O = make_time_mpo(H, 1.0, TaylorCluster(3, false, false)) + braille(buffer, O) + output = String(take!(buffer)) + check = """ + ... 🭻⎡⡏⠉⠛⠟⎤🭻 ... + ⎣⡇⠀⠀⡂⎦ + """ + @test output == check - # Finite Hamiltonians and MPOs - # ---------------------------- - H = transverse_field_ising(; L=4) - braille(buffer, H) - output = String(take!(buffer)) - check = " ⎡⠉⠈⎤🭻🭻⎡⠉⢈⎤🭻🭻⎡⠉⢈⎤🭻🭻⎡⡁⠀⎤ \n ⎣⠀⠀⎦ ⎣⠀⢀⎦ ⎣⠀⢀⎦ ⎣⡀⠀⎦ \n" - @test output == check + # Finite Hamiltonians and MPOs + # ---------------------------- + H = transverse_field_ising(; L = 4) + braille(buffer, H) + output = String(take!(buffer)) + check = " ⎡⠉⠈⎤🭻🭻⎡⠉⢈⎤🭻🭻⎡⠉⢈⎤🭻🭻⎡⡁⠀⎤ \n ⎣⠀⠀⎦ ⎣⠀⢀⎦ ⎣⠀⢀⎦ ⎣⡀⠀⎦ \n" + @test output == check - O = make_time_mpo(H, 1.0, TaylorCluster(3, false, false)) - braille(buffer, O) - output = String(take!(buffer)) - check = " ⎡⠉⠉⠉⠉⎤🭻🭻⎡⡏⠉⠛⠟⎤🭻🭻⎡⡏⠉⠛⠟⎤🭻🭻⎡⡇⠀⎤ \n ⎣⠀⠀⠀⠀⎦ ⎣⡇⠀⠀⡂⎦ ⎣⡇⠀⠀⡂⎦ ⎣⡇⠀⎦ \n" - @test output == check -end + O = make_time_mpo(H, 1.0, TaylorCluster(3, false, false)) + braille(buffer, O) + output = String(take!(buffer)) + check = " ⎡⠉⠉⠉⠉⎤🭻🭻⎡⡏⠉⠛⠟⎤🭻🭻⎡⡏⠉⠛⠟⎤🭻🭻⎡⡇⠀⎤ \n ⎣⠀⠀⠀⠀⎦ ⎣⡇⠀⠀⡂⎦ ⎣⡇⠀⠀⡂⎦ ⎣⡇⠀⎦ \n" + @test output == check + end end diff --git a/test/setup.jl b/test/setup.jl index 4689a2aff..09a18a83f 100644 --- a/test/setup.jl +++ b/test/setup.jl @@ -17,7 +17,7 @@ export c_plusmin, c_minplus, c_number export force_planar export symm_mul_mpo export transverse_field_ising, heisenberg_XXX, bilinear_biquadratic_model, XY_model, - kitaev_model + kitaev_model export classical_ising, finite_classical_ising, sixvertex # using TensorOperations @@ -27,10 +27,9 @@ force_planar(x::Number) = x force_planar(c::Sector) = PlanarTrivial() ⊠ c # convert spaces -force_planar(V::Union{CartesianSpace,ComplexSpace}) = ℙ^dim(V) +force_planar(V::Union{CartesianSpace, ComplexSpace}) = ℙ^dim(V) function force_planar(V::GradedSpace) - return Vect[PlanarTrivial ⊠ sectortype(V)](force_planar(c) => dim(V, c) - for c in sectors(V)) + return Vect[PlanarTrivial ⊠ sectortype(V)](force_planar(c) => dim(V, c) for c in sectors(V)) end force_planar(V::SumSpace) = SumSpace(map(force_planar, V.spaces)) force_planar(V::ProductSpace) = ProductSpace(map(force_planar, V.spaces)) @@ -86,7 +85,7 @@ end # Toy models # ---------------------------- -function S_x(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where {T<:Number} +function S_x(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return if spin == 1 // 2 TensorMap(T[0 1; 1 0], ℂ^2 ← ℂ^2) elseif spin == 1 @@ -95,7 +94,7 @@ function S_x(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where { throw(ArgumentError("spin $spin not supported")) end end -function S_y(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where {T<:Number} +function S_y(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return if spin == 1 // 2 TensorMap(T[0 -im; im 0], ℂ^2 ← ℂ^2) elseif spin == 1 @@ -104,7 +103,7 @@ function S_y(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where { throw(ArgumentError("spin $spin not supported")) end end -function S_z(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where {T<:Number} +function S_z(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return if spin == 1 // 2 TensorMap(T[1 0; 0 -1], ℂ^2 ← ℂ^2) elseif spin == 1 @@ -113,19 +112,19 @@ function S_z(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where { throw(ArgumentError("spin $spin not supported")) end end -function S_xx(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where {T<:Number} +function S_xx(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return S_x(Trivial, T; spin) ⊗ S_x(Trivial, T; spin) end -function S_yy(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where {T<:Number} +function S_yy(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return S_y(Trivial, T; spin) ⊗ S_y(Trivial, T; spin) end -function S_zz(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1 // 2) where {T<:Number} +function S_zz(::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1 // 2) where {T <: Number} return S_z(Trivial, T; spin) ⊗ S_z(Trivial, T; spin) end -function transverse_field_ising(::Type{T}=ComplexF64; g=1.0, L=Inf) where {T<:Number} - X = S_x(Trivial, T; spin=1 // 2) - ZZ = S_zz(Trivial, T; spin=1 // 2) +function transverse_field_ising(::Type{T} = ComplexF64; g = 1.0, L = Inf) where {T <: Number} + X = S_x(Trivial, T; spin = 1 // 2) + ZZ = S_zz(Trivial, T; spin = 1 // 2) if L == Inf lattice = PeriodicArray([ℂ^2]) @@ -139,7 +138,7 @@ function transverse_field_ising(::Type{T}=ComplexF64; g=1.0, L=Inf) where {T<:Nu return H₁ + H₂ end -function heisenberg_XXX(::Type{SU2Irrep}; spin=1, L=Inf) +function heisenberg_XXX(::Type{SU2Irrep}; spin = 1, L = Inf) h = ones(ComplexF64, SU2Space(spin => 1)^2 ← SU2Space(spin => 1)^2) for (c, b) in blocks(h) S = (dim(c) - 1) / 2 @@ -156,8 +155,10 @@ function heisenberg_XXX(::Type{SU2Irrep}; spin=1, L=Inf) end end -function heisenberg_XXX(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1, - L=Inf) where {T<:Number} +function heisenberg_XXX( + ::Type{Trivial} = Trivial, ::Type{T} = ComplexF64; spin = 1, + L = Inf + ) where {T <: Number} h = ones(T, SU2Space(spin => 1)^2 ← SU2Space(spin => 1)^2) for (c, b) in blocks(h) S = (dim(c) - 1) / 2 @@ -176,9 +177,11 @@ function heisenberg_XXX(::Type{Trivial}=Trivial, ::Type{T}=ComplexF64; spin=1, end end -function XY_model(::Type{U1Irrep}; g=1 / 2, L=Inf) - h = zeros(ComplexF64, - U1Space(-1 // 2 => 1, 1 // 2 => 1)^2 ← U1Space(-1 // 2 => 1, 1 // 2 => 1)^2) +function XY_model(::Type{U1Irrep}; g = 1 / 2, L = Inf) + h = zeros( + ComplexF64, + U1Space(-1 // 2 => 1, 1 // 2 => 1)^2 ← U1Space(-1 // 2 => 1, 1 // 2 => 1)^2 + ) h[U1Irrep.((-1 // 2, 1 // 2, -1 // 2, 1 // 2))] .= 1 h[U1Irrep.((1 // 2, -1 // 2, 1 // 2, -1 // 2))] .= 1 Sz = zeros(ComplexF64, space(h, 1) ← space(h, 1)) @@ -197,7 +200,7 @@ function XY_model(::Type{U1Irrep}; g=1 / 2, L=Inf) end end -function bilinear_biquadratic_model(::Type{SU2Irrep}; θ=atan(1 / 3), L=Inf) +function bilinear_biquadratic_model(::Type{SU2Irrep}; θ = atan(1 / 3), L = Inf) h1 = ones(ComplexF64, SU2Space(1 => 1)^2 ← SU2Space(1 => 1)^2) for (c, b) in blocks(h1) S = (dim(c) - 1) / 2 @@ -253,7 +256,7 @@ function c_number() return t end -function kitaev_model(; t=1.0, mu=1.0, Delta=1.0, L=Inf) +function kitaev_model(; t = 1.0, mu = 1.0, Delta = 1.0, L = Inf) TB = scale!(c_plusmin() + c_minplus(), -t / 2) # tight-binding term SC = scale!(c_plusplus() + c_minmin(), Delta / 2) # superconducting term CP = scale!(c_number(), -mu) # chemical potential term @@ -263,8 +266,9 @@ function kitaev_model(; t=1.0, mu=1.0, Delta=1.0, L=Inf) return InfiniteMPOHamiltonian(lattice, (1, 2) => TB + SC, (1,) => CP) else lattice = fill(space(TB, 1), L) - terms = Iterators.flatten((((i, i + 1) => TB + SC for i in 1:(L - 1)), - (i,) => CP for i in 1:L)) + onsite_terms = ((i,) => CP for i in 1:L) + twosite_terms = ((i, i + 1) => TP + SC for i in 1:(L - 1)) + terms = Iterators.flatten(twosite_terms, onsite_terms) return FiniteMPOHamiltonian(lattice, terms) end end @@ -276,14 +280,14 @@ function ising_bond_tensor(β) return nt end -function classical_ising(; β=log(1 + sqrt(2)) / 2, L=Inf) +function classical_ising(; β = log(1 + sqrt(2)) / 2, L = Inf) nt = ising_bond_tensor(β) δbulk = zeros(ComplexF64, (2, 2, 2, 2)) δbulk[1, 1, 1, 1] = 1 δbulk[2, 2, 2, 2] = 1 @tensor obulk[-1 -2; -3 -4] := δbulk[1 2; 3 4] * nt[-1; 1] * nt[-2; 2] * nt[-3; 3] * - nt[-4; 4] + nt[-4; 4] Obulk = TensorMap(obulk, ℂ^2 * ℂ^2, ℂ^2 * ℂ^2) L == Inf && return InfiniteMPO([Obulk]) @@ -303,40 +307,13 @@ function classical_ising(; β=log(1 + sqrt(2)) / 2, L=Inf) return FiniteMPO([Oleft, fill(Obulk, L - 2)..., Oright]) end -function finite_classical_ising(N) - return classical_ising(; L=N) - β = log(1 + sqrt(2)) / 2 - nt = ising_bond_tensor(β) - - # bulk - O = zeros(ComplexF64, (2, 2, 2, 2)) - O[1, 1, 1, 1] = 1 - O[2, 2, 2, 2] = 1 - @tensor o[-1 -2; -3 -4] := O[1 2; 3 4] * nt[-1; 1] * nt[-2; 2] * nt[-3; 3] * nt[-4; 4] - Obulk = TensorMap(o, ℂ^2 * ℂ^2, ℂ^2 * ℂ^2) - - # left - OL = zeros(ComplexF64, (1, 2, 2, 2)) - OL[1, 1, 1, 1] = 1 - OL[1, 2, 2, 2] = 1 - @tensor oL[-1 -2; -3 -4] := OL[-1 1; 2 3] * nt[-2; 1] * nt[-3; 2] * nt[-4; 3] - Oleft = TensorMap(oL, ℂ^1 * ℂ^2, ℂ^2 * ℂ^2) - - # right - OR = zeros(ComplexF64, (2, 2, 2, 1)) - OR[1, 1, 1, 1] = 1 - OR[2, 2, 2, 1] = 1 - @tensor oR[-1 -2; -3 -4] := OR[1 2; 3 -4] * nt[-1; 1] * nt[-2; 2] * nt[-3; 3] - Oright = TensorMap(oR, ℂ^2 * ℂ^2, ℂ^2 * ℂ^1) - - return DenseMPO([Oleft, fill(Obulk, N - 2)..., Oright]) -end - -function sixvertex(; a=1.0, b=1.0, c=1.0) - d = ComplexF64[a 0 0 0 - 0 c b 0 - 0 b c 0 - 0 0 0 a] +function sixvertex(; a = 1.0, b = 1.0, c = 1.0) + d = ComplexF64[ + a 0 0 0 + 0 c b 0 + 0 b c 0 + 0 0 0 a + ] return InfiniteMPO([permute(TensorMap(d, ℂ^2 ⊗ ℂ^2, ℂ^2 ⊗ ℂ^2), ((1, 2), (4, 3)))]) end diff --git a/test/states.jl b/test/states.jl index 24d1690cb..f763904af 100644 --- a/test/states.jl +++ b/test/states.jl @@ -5,233 +5,247 @@ println(" ") module TestStates -using ..TestSetup -using Test, TestExtras -using MPSKit -using MPSKit: _transpose_front, _transpose_tail -using MPSKit: TransferMatrix -using TensorKit -using TensorKit: ℙ - -@testset "FiniteMPS ($(sectortype(D)), $elt)" for (D, d, elt) in [(ℙ^10, ℙ^2, ComplexF64), - (Rep[SU₂](1 => 1, 0 => 3), - Rep[SU₂](0 => 1) * Rep[SU₂](0 => 1), - ComplexF32)] - ψ = FiniteMPS(rand, elt, rand(3:20), d, D) - - ovl = dot(ψ, ψ) - - @test ovl ≈ norm(ψ.AC[1])^2 - - for i in 1:length(ψ) - @test ψ.AC[i] ≈ ψ.AL[i] * ψ.C[i] - @test ψ.AC[i] ≈ _transpose_front(ψ.C[i - 1] * _transpose_tail(ψ.AR[i])) + using ..TestSetup + using Test, TestExtras + using MPSKit + using MPSKit: _transpose_front, _transpose_tail + using MPSKit: TransferMatrix + using TensorKit + using TensorKit: ℙ + + @testset "FiniteMPS ($(sectortype(D)), $elt)" for (D, d, elt) in [ + (ℙ^10, ℙ^2, ComplexF64), + ( + Rep[SU₂](1 => 1, 0 => 3), + Rep[SU₂](0 => 1) * Rep[SU₂](0 => 1), + ComplexF32, + ), + ] + ψ = FiniteMPS(rand, elt, rand(3:20), d, D) + + ovl = dot(ψ, ψ) + + @test ovl ≈ norm(ψ.AC[1])^2 + + for i in 1:length(ψ) + @test ψ.AC[i] ≈ ψ.AL[i] * ψ.C[i] + @test ψ.AC[i] ≈ _transpose_front(ψ.C[i - 1] * _transpose_tail(ψ.AR[i])) + end + + @test elt == scalartype(ψ) + + ψ = ψ * 3 + @test ovl * 9 ≈ norm(ψ)^2 + ψ = 3 * ψ + @test ovl * 9 * 9 ≈ norm(ψ)^2 + + @test norm(2 * ψ + ψ - 3 * ψ) ≈ 0.0 atol = sqrt(eps(real(elt))) end - @test elt == scalartype(ψ) - - ψ = ψ * 3 - @test ovl * 9 ≈ norm(ψ)^2 - ψ = 3 * ψ - @test ovl * 9 * 9 ≈ norm(ψ)^2 - - @test norm(2 * ψ + ψ - 3 * ψ) ≈ 0.0 atol = sqrt(eps(real(elt))) -end - -@testset "FiniteMPS ($(sectortype(D)), $elt)" for (D, d, elt) in [(ℙ^10, ℙ^2, ComplexF64), - (Rep[U₁](-1 => 3, 0 => 3, 1 => 3), - Rep[U₁](-1 => 1, 0 => 1, 1 => 1), - ComplexF64)] - ψ_small = FiniteMPS(rand, elt, 4, d, D) - ψ_small2 = FiniteMPS(convert(TensorMap, ψ_small)) - @test dot(ψ_small, ψ_small2) ≈ dot(ψ_small, ψ_small) - - ψ′ = @constinferred complex(ψ_small) - @test scalartype(ψ′) <: Complex - if elt <: Complex - @test ψ_small === ψ′ - else - @test norm(ψ_small) ≈ norm(ψ′) - @test complex(convert(TensorMap, ψ_small)) ≈ convert(TensorMap, ψ′) + @testset "FiniteMPS ($(sectortype(D)), $elt)" for (D, d, elt) in [ + (ℙ^10, ℙ^2, ComplexF64), + ( + Rep[U₁](-1 => 3, 0 => 3, 1 => 3), + Rep[U₁](-1 => 1, 0 => 1, 1 => 1), + ComplexF64, + ), + ] + ψ_small = FiniteMPS(rand, elt, 4, d, D) + ψ_small2 = FiniteMPS(convert(TensorMap, ψ_small)) + @test dot(ψ_small, ψ_small2) ≈ dot(ψ_small, ψ_small) + + ψ′ = @constinferred complex(ψ_small) + @test scalartype(ψ′) <: Complex + if elt <: Complex + @test ψ_small === ψ′ + else + @test norm(ψ_small) ≈ norm(ψ′) + @test complex(convert(TensorMap, ψ_small)) ≈ convert(TensorMap, ψ′) + end end -end -@testset "FiniteMPS center + (slice) indexing" begin - L = 11 - ψ = FiniteMPS(L, ℂ^2, ℂ^16) + @testset "FiniteMPS center + (slice) indexing" begin + L = 11 + ψ = FiniteMPS(L, ℂ^2, ℂ^16) - ψ.AC[6] # moving the center to site 6 + ψ.AC[6] # moving the center to site 6 - @test ψ.center == 6 + @test ψ.center == 6 - @test ψ[5] == ψ.ALs[5] - @test ψ[6] == ψ.ACs[6] - @test ψ[7] == ψ.ARs[7] + @test ψ[5] == ψ.ALs[5] + @test ψ[6] == ψ.ACs[6] + @test ψ[7] == ψ.ARs[7] - @test ψ[5:7] == [ψ.ALs[5], ψ.ACs[6], ψ.ARs[7]] + @test ψ[5:7] == [ψ.ALs[5], ψ.ACs[6], ψ.ARs[7]] - @inferred ψ[5] + @inferred ψ[5] - @test_throws BoundsError ψ[0] - @test_throws BoundsError ψ[L + 1] + @test_throws BoundsError ψ[0] + @test_throws BoundsError ψ[L + 1] - ψ.C[6] = randn(ComplexF64, space(ψ.C[6])) # setting the center between sites 6 and 7 - @test ψ.center == 13 / 2 - @test ψ[5:7] == [ψ.ALs[5], ψ.ACs[6], ψ.ARs[7]] -end + ψ.C[6] = randn(ComplexF64, space(ψ.C[6])) # setting the center between sites 6 and 7 + @test ψ.center == 13 / 2 + @test ψ[5:7] == [ψ.ALs[5], ψ.ACs[6], ψ.ARs[7]] + end -@testset "InfiniteMPS ($(sectortype(D)), $elt)" for (D, d, elt) in - [(ℙ^10, ℙ^2, ComplexF64), - (Rep[U₁](1 => 3), Rep[U₁](0 => 1), - ComplexF64)] - tol = Float64(eps(real(elt)) * 100) + @testset "InfiniteMPS ($(sectortype(D)), $elt)" for (D, d, elt) in + [(ℙ^10, ℙ^2, ComplexF64), (Rep[U₁](1 => 3), Rep[U₁](0 => 1), ComplexF64)] + tol = Float64(eps(real(elt)) * 100) - ψ = InfiniteMPS([rand(elt, D * d, D), rand(elt, D * d, D)]; tol) + ψ = InfiniteMPS([rand(elt, D * d, D), rand(elt, D * d, D)]; tol) - for i in 1:length(ψ) - @plansor difference[-1 -2; -3] := ψ.AL[i][-1 -2; 1] * ψ.C[i][1; -3] - - ψ.C[i - 1][-1; 1] * ψ.AR[i][1 -2; -3] - @test norm(difference, Inf) < tol * 10 + for i in 1:length(ψ) + @plansor difference[-1 -2; -3] := ψ.AL[i][-1 -2; 1] * ψ.C[i][1; -3] - + ψ.C[i - 1][-1; 1] * ψ.AR[i][1 -2; -3] + @test norm(difference, Inf) < tol * 10 - @test l_LL(ψ, i) * TransferMatrix(ψ.AL[i], ψ.AL[i]) ≈ l_LL(ψ, i + 1) - @test l_LR(ψ, i) * TransferMatrix(ψ.AL[i], ψ.AR[i]) ≈ l_LR(ψ, i + 1) - @test l_RL(ψ, i) * TransferMatrix(ψ.AR[i], ψ.AL[i]) ≈ l_RL(ψ, i + 1) - @test l_RR(ψ, i) * TransferMatrix(ψ.AR[i], ψ.AR[i]) ≈ l_RR(ψ, i + 1) + @test l_LL(ψ, i) * TransferMatrix(ψ.AL[i], ψ.AL[i]) ≈ l_LL(ψ, i + 1) + @test l_LR(ψ, i) * TransferMatrix(ψ.AL[i], ψ.AR[i]) ≈ l_LR(ψ, i + 1) + @test l_RL(ψ, i) * TransferMatrix(ψ.AR[i], ψ.AL[i]) ≈ l_RL(ψ, i + 1) + @test l_RR(ψ, i) * TransferMatrix(ψ.AR[i], ψ.AR[i]) ≈ l_RR(ψ, i + 1) - @test TransferMatrix(ψ.AL[i], ψ.AL[i]) * r_LL(ψ, i) ≈ r_LL(ψ, i + 1) - @test TransferMatrix(ψ.AL[i], ψ.AR[i]) * r_LR(ψ, i) ≈ r_LR(ψ, i + 1) - @test TransferMatrix(ψ.AR[i], ψ.AL[i]) * r_RL(ψ, i) ≈ r_RL(ψ, i + 1) - @test TransferMatrix(ψ.AR[i], ψ.AR[i]) * r_RR(ψ, i) ≈ r_RR(ψ, i + 1) + @test TransferMatrix(ψ.AL[i], ψ.AL[i]) * r_LL(ψ, i) ≈ r_LL(ψ, i + 1) + @test TransferMatrix(ψ.AL[i], ψ.AR[i]) * r_LR(ψ, i) ≈ r_LR(ψ, i + 1) + @test TransferMatrix(ψ.AR[i], ψ.AL[i]) * r_RL(ψ, i) ≈ r_RL(ψ, i + 1) + @test TransferMatrix(ψ.AR[i], ψ.AR[i]) * r_RR(ψ, i) ≈ r_RR(ψ, i + 1) + end end -end -@testset "MultilineMPS ($(sectortype(D)), $elt)" for (D, d, elt) in - [(ℙ^10, ℙ^2, ComplexF64), - (Rep[U₁](1 => 3), Rep[U₁](0 => 1), - ComplexF32)] - tol = Float64(eps(real(elt)) * 100) - ψ = MultilineMPS([rand(elt, D * d, D) rand(elt, D * d, D) - rand(elt, D * d, D) rand(elt, D * d, D)]; tol) - - for i in 1:size(ψ, 1), j in 1:size(ψ, 2) - @plansor difference[-1 -2; -3] := ψ.AL[i, j][-1 -2; 1] * ψ.C[i, j][1; -3] - - ψ.C[i, j - 1][-1; 1] * ψ.AR[i, j][1 -2; -3] - @test norm(difference, Inf) < tol * 10 - - @test l_LL(ψ, i, j) * TransferMatrix(ψ.AL[i, j], ψ.AL[i, j]) ≈ l_LL(ψ, i, j + 1) - @test l_LR(ψ, i, j) * TransferMatrix(ψ.AL[i, j], ψ.AR[i, j]) ≈ l_LR(ψ, i, j + 1) - @test l_RL(ψ, i, j) * TransferMatrix(ψ.AR[i, j], ψ.AL[i, j]) ≈ l_RL(ψ, i, j + 1) - @test l_RR(ψ, i, j) * TransferMatrix(ψ.AR[i, j], ψ.AR[i, j]) ≈ l_RR(ψ, i, j + 1) - - @test TransferMatrix(ψ.AL[i, j], ψ.AL[i, j]) * r_LL(ψ, i, j) ≈ r_LL(ψ, i, j + 1) - @test TransferMatrix(ψ.AL[i, j], ψ.AR[i, j]) * r_LR(ψ, i, j) ≈ r_LR(ψ, i, j + 1) - @test TransferMatrix(ψ.AR[i, j], ψ.AL[i, j]) * r_RL(ψ, i, j) ≈ r_RL(ψ, i, j + 1) - @test TransferMatrix(ψ.AR[i, j], ψ.AR[i, j]) * r_RR(ψ, i, j) ≈ r_RR(ψ, i, j + 1) + @testset "MultilineMPS ($(sectortype(D)), $elt)" for (D, d, elt) in + [(ℙ^10, ℙ^2, ComplexF64), (Rep[U₁](1 => 3), Rep[U₁](0 => 1), ComplexF32)] + tol = Float64(eps(real(elt)) * 100) + ψ = MultilineMPS( + [ + rand(elt, D * d, D) rand(elt, D * d, D) + rand(elt, D * d, D) rand(elt, D * d, D) + ]; tol + ) + + for i in 1:size(ψ, 1), j in 1:size(ψ, 2) + @plansor difference[-1 -2; -3] := ψ.AL[i, j][-1 -2; 1] * ψ.C[i, j][1; -3] - + ψ.C[i, j - 1][-1; 1] * ψ.AR[i, j][1 -2; -3] + @test norm(difference, Inf) < tol * 10 + + @test l_LL(ψ, i, j) * TransferMatrix(ψ.AL[i, j], ψ.AL[i, j]) ≈ l_LL(ψ, i, j + 1) + @test l_LR(ψ, i, j) * TransferMatrix(ψ.AL[i, j], ψ.AR[i, j]) ≈ l_LR(ψ, i, j + 1) + @test l_RL(ψ, i, j) * TransferMatrix(ψ.AR[i, j], ψ.AL[i, j]) ≈ l_RL(ψ, i, j + 1) + @test l_RR(ψ, i, j) * TransferMatrix(ψ.AR[i, j], ψ.AR[i, j]) ≈ l_RR(ψ, i, j + 1) + + @test TransferMatrix(ψ.AL[i, j], ψ.AL[i, j]) * r_LL(ψ, i, j) ≈ r_LL(ψ, i, j + 1) + @test TransferMatrix(ψ.AL[i, j], ψ.AR[i, j]) * r_LR(ψ, i, j) ≈ r_LR(ψ, i, j + 1) + @test TransferMatrix(ψ.AR[i, j], ψ.AL[i, j]) * r_RL(ψ, i, j) ≈ r_RL(ψ, i, j + 1) + @test TransferMatrix(ψ.AR[i, j], ψ.AR[i, j]) * r_RR(ψ, i, j) ≈ r_RR(ψ, i, j + 1) + end end -end - -@testset "WindowMPS" begin - g = 8.0 - ham = force_planar(transverse_field_ising(; g)) - - # operator for testing expectation_value - X = S_x(; spin=1 // 2) - E = TensorMap(ComplexF64[1 0; 0 1], ℂ^2 ← ℂ^2) - O = force_planar(-(S_zz(; spin=1 // 2) + (g / 2) * (X ⊗ E + E ⊗ X))) - gs, = find_groundstate(InfiniteMPS([ℙ^2], [ℙ^10]), ham, VUMPS(; verbosity=0)) + @testset "WindowMPS" begin + g = 8.0 + ham = force_planar(transverse_field_ising(; g)) - # constructor 1 - give it a plain array of tensors - window_1 = WindowMPS(gs, copy.([gs.AC[1]; [gs.AR[i] for i in 2:10]]), gs) + # operator for testing expectation_value + X = S_x(; spin = 1 // 2) + E = TensorMap(ComplexF64[1 0; 0 1], ℂ^2 ← ℂ^2) + O = force_planar(-(S_zz(; spin = 1 // 2) + (g / 2) * (X ⊗ E + E ⊗ X))) - # constructor 2 - used to take a "slice" from an infinite mps - window_2 = WindowMPS(gs, 10) + gs, = find_groundstate(InfiniteMPS([ℙ^2], [ℙ^10]), ham, VUMPS(; verbosity = 0)) - # we should logically have that window_1 approximates window_2 - ovl = dot(window_1, window_2) - @test ovl ≈ 1 atol = 1e-8 + # constructor 1 - give it a plain array of tensors + window_1 = WindowMPS(gs, copy.([gs.AC[1]; [gs.AR[i] for i in 2:10]]), gs) - # constructor 3 - random initial tensors - window = WindowMPS(rand, ComplexF64, 10, ℙ^2, ℙ^10, gs, gs) - normalize!(window) + # constructor 2 - used to take a "slice" from an infinite mps + window_2 = WindowMPS(gs, 10) - for i in 1:length(window) - @test window.AC[i] ≈ window.AL[i] * window.C[i] - @test window.AC[i] ≈ - _transpose_front(window.C[i - 1] * _transpose_tail(window.AR[i])) - end - - @test norm(window) ≈ 1 - window = window * 3 - @test 9 ≈ norm(window)^2 - window = 3 * window - @test 9 * 9 ≈ norm(window)^2 - normalize!(window) - - e1 = expectation_value(window, (2, 3) => O) + # we should logically have that window_1 approximates window_2 + ovl = dot(window_1, window_2) + @test ovl ≈ 1 atol = 1.0e-8 - window, envs, _ = find_groundstate(window, ham, DMRG(; verbosity=0)) + # constructor 3 - random initial tensors + window = WindowMPS(rand, ComplexF64, 10, ℙ^2, ℙ^10, gs, gs) + normalize!(window) - e2 = expectation_value(window, (2, 3) => O) + for i in 1:length(window) + @test window.AC[i] ≈ window.AL[i] * window.C[i] + @test window.AC[i] ≈ + _transpose_front(window.C[i - 1] * _transpose_tail(window.AR[i])) + end - @test real(e2) ≤ real(e1) + @test norm(window) ≈ 1 + window = window * 3 + @test 9 ≈ norm(window)^2 + window = 3 * window + @test 9 * 9 ≈ norm(window)^2 + normalize!(window) - window, envs = timestep(window, ham, 0.1, 0.0, TDVP2(; trscheme=truncdim(20)), envs) - window, envs = timestep(window, ham, 0.1, 0.0, TDVP(), envs) - - e3 = expectation_value(window, (2, 3) => O) - - @test e2 ≈ e3 atol = 1e-4 -end + e1 = expectation_value(window, (2, 3) => O) -@testset "Quasiparticle state" verbose = true begin - L = 10 - @testset "Finite" verbose = true for (H, D, d) in - [(force_planar(transverse_field_ising(; L)), ℙ^10, - ℙ^2), - (heisenberg_XXX(SU2Irrep; spin=1, L), - Rep[SU₂](1 => 1, 0 => 3), Rep[SU₂](1 => 1))] - ψ = FiniteMPS(rand, ComplexF64, L, d, D) - normalize!(ψ) + window, envs, _ = find_groundstate(window, ham, DMRG(; verbosity = 0)) - #rand_quasiparticle is a private non-exported function - ϕ₁ = LeftGaugedQP(rand, ψ) - ϕ₂ = LeftGaugedQP(rand, ψ) + e2 = expectation_value(window, (2, 3) => O) - @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) - @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) + @test real(e2) ≤ real(e1) - normalize!(ϕ₁) + window, envs = timestep(window, ham, 0.1, 0.0, TDVP2(; trscheme = truncdim(20)), envs) + window, envs = timestep(window, ham, 0.1, 0.0, TDVP(), envs) - ϕ₁_f = convert(FiniteMPS, ϕ₁) - ϕ₂_f = convert(FiniteMPS, ϕ₂) + e3 = expectation_value(window, (2, 3) => O) - @test dot(ϕ₁_f, ϕ₂_f) ≈ dot(ϕ₁, ϕ₂) atol = 1e-5 - @test norm(ϕ₁_f) ≈ norm(ϕ₁) atol = 1e-5 - - ev_f = expectation_value(ϕ₁_f, H) - expectation_value(ψ, H) - ev_q = dot(ϕ₁, MPSKit.effective_excitation_hamiltonian(H, ϕ₁)) - @test ev_f ≈ ev_q atol = 1e-5 + @test e2 ≈ e3 atol = 1.0e-4 end - @testset "Infinite" for (th, D, d) in - [(force_planar(transverse_field_ising()), ℙ^10, ℙ^2), - (heisenberg_XXX(SU2Irrep; spin=1), Rep[SU₂](1 => 3, 0 => 2), - Rep[SU₂](1 => 1))] - period = rand(1:4) - ψ = InfiniteMPS(fill(d, period), fill(D, period)) - - #rand_quasiparticle is a private non-exported function - ϕ₁ = LeftGaugedQP(rand, ψ) - ϕ₂ = LeftGaugedQP(rand, ψ) - - @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) - @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) - - @test dot(ϕ₁, - convert(LeftGaugedQP, convert(RightGaugedQP, ϕ₁))) ≈ - dot(ϕ₁, ϕ₁) atol = 1e-10 + @testset "Quasiparticle state" verbose = true begin + L = 10 + @testset "Finite" verbose = true for (H, D, d) in + [ + (force_planar(transverse_field_ising(; L)), ℙ^10, ℙ^2), + (heisenberg_XXX(SU2Irrep; spin = 1, L), Rep[SU₂](1 => 1, 0 => 3), Rep[SU₂](1 => 1)), + ] + ψ = FiniteMPS(rand, ComplexF64, L, d, D) + normalize!(ψ) + + #rand_quasiparticle is a private non-exported function + ϕ₁ = LeftGaugedQP(rand, ψ) + ϕ₂ = LeftGaugedQP(rand, ψ) + + @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) + @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) + + normalize!(ϕ₁) + + ϕ₁_f = convert(FiniteMPS, ϕ₁) + ϕ₂_f = convert(FiniteMPS, ϕ₂) + + @test dot(ϕ₁_f, ϕ₂_f) ≈ dot(ϕ₁, ϕ₂) atol = 1.0e-5 + @test norm(ϕ₁_f) ≈ norm(ϕ₁) atol = 1.0e-5 + + ev_f = expectation_value(ϕ₁_f, H) - expectation_value(ψ, H) + ev_q = dot(ϕ₁, MPSKit.effective_excitation_hamiltonian(H, ϕ₁)) + @test ev_f ≈ ev_q atol = 1.0e-5 + end + + @testset "Infinite" for (th, D, d) in + [ + (force_planar(transverse_field_ising()), ℙ^10, ℙ^2), + ( + heisenberg_XXX(SU2Irrep; spin = 1), Rep[SU₂](1 => 3, 0 => 2), + Rep[SU₂](1 => 1), + ), + ] + period = rand(1:4) + ψ = InfiniteMPS(fill(d, period), fill(D, period)) + + #rand_quasiparticle is a private non-exported function + ϕ₁ = LeftGaugedQP(rand, ψ) + ϕ₂ = LeftGaugedQP(rand, ψ) + + @test norm(axpy!(1, ϕ₁, copy(ϕ₂))) ≤ norm(ϕ₁) + norm(ϕ₂) + @test norm(ϕ₁) * 3 ≈ norm(ϕ₁ * 3) + + @test dot( + ϕ₁, + convert(LeftGaugedQP, convert(RightGaugedQP, ϕ₁)) + ) ≈ + dot(ϕ₁, ϕ₁) atol = 1.0e-10 + end end -end end