Skip to content

Commit 804ae84

Browse files
Improve Low Rank Tutorial (#17)
1 parent 91ace98 commit 804ae84

File tree

1 file changed

+114
-49
lines changed

1 file changed

+114
-49
lines changed

QuantumToolbox.jl/time_evolution/lowrank.qmd

Lines changed: 114 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,121 @@
11
---
22
title: Low rank master equation
33
author: Luca Gravina
4-
date: 2025-01-13 # last update (keep this comment as a reminder)
4+
date: 2025-01-20 # last update (keep this comment as a reminder)
55

66
engine: julia
77
---
88

9-
In this tutorial, we will show how to solve the master equation using the low-rank method. For a detailed explanation of the method, we recommend to read the Ref. [@gravina2024adaptive].
9+
## Introduction
10+
In this tutorial we will demonstrate how to solve the master equation without the quadratic overhead associated with the full manipulation of the density matrix. For a detailed explanation of the method, we recommend to read the Ref. [@gravina2024adaptive].
1011

11-
As a test, we will consider the dissipative Ising model with a transverse field. The Hamiltonian is given by
12+
The proposed approach is based on the realization that many quantum systems, particularly those with low entropy, can be effectively represented by a density matrix of significantly lower rank than what the whole Hilbert space would require. This reduction is achieved by focusing on a subset of states that capture the essential structure of the statistical ensemble characterizing the mixed quantum state, thereby reducing computational complexity while maintaining the exactness of the method.
1213

14+
### Low-rank master equations
15+
We consider a decomposition of the density matrix of the form
1316
$$
14-
\hat{H} = \frac{J_x}{2} \sum_{\langle i,j\rangle} \sigma_i^x \sigma_j^x + \frac{J_y}{2} \sum_{\langle i,j\rangle} \sigma_i^y \sigma_j^y + \frac{J_z}{2} \sum_{\langle i,j\rangle} \sigma_i^z \sigma_j^z - \sum_i h_i \sigma_i^z + h_x \sum_i \sigma_i^x + h_y \sum_i \sigma_i^y + h_z \sum_i \sigma_i^z
17+
\hat\rho(t) = \sum_{i,j=1}^{M(t)} B_{i,j}(t) | \varphi_i(t) \rangle \langle \varphi_j(t) |.
1518
$$
16-
K
17-
where the sums are over nearest neighbors, and the collapse operators are given by
18-
19+
The states $\{|\varphi_k(t)\rangle\,;\,k=1,\ldots,M(t)\}$ spanning the low-rank manifold, can in turn be decomposed as
1920
$$
20-
c_i = \sqrt{\gamma} \sigma_i^{-}
21+
|\varphi_k(t)\rangle = \sum_{\alpha=1}^{N} z_{\alpha,k}(t) |e_\alpha\rangle,
2122
$$
23+
where $\{|e_\alpha\rangle\,;\,\alpha=1,\ldots,N\}$ is a fixed basis of the Hilbert space, and $z_{\alpha,k}(t)$ are the time-dependent coefficients.
24+
25+
The coefficients $B_{i,j}(t)$ are collected in the matrix $B(t)$, and the coefficients $z_{\alpha,k}(t)$ are collected in the matrix $z(t)$.
26+
27+
In [@gravina2024adaptive] all coefficients $B_{i,j}(t)$ and $z_{\alpha,k}(t)$ are taken to be variational parameters. The evolution equation for the density matrix is consequently mapped onto a set of differential equations for such parameters via the time-dependent variational principle (TDVP).
28+
29+
The TDVP ensures a dynamical adjustment of the variational states, guaranteeing the optimal set of states is selected at all times to best approximate the dissipative evolution. This allows for a significant reduction in computational complexity as the number of states $M(t)$ necessary to accurately capture the dynamics of the system is as small as can be, hopefully much smaller than the full Hilbert space dimension $N$.
2230

23-
We start by importing the packages
31+
## Low-rank dynamics of the transverse field Heisenberg model
32+
In this example we consider the dynamics of the transverse field Ising model (TFIM) on a 2x3 lattice. We start by importing the packages
2433

2534
```{julia}
2635
using QuantumToolbox
2736
using CairoMakie
2837
```
2938

30-
Define lattice
39+
We define the lattice with dimensions `Nx = 2` and `Ny = 3` and use the `Lattice` class to generate the lattice.
3140

3241
```{julia}
3342
Nx, Ny = 2, 3
3443
latt = Lattice(Nx = Nx, Ny = Ny)
3544
```
3645

37-
Define lr-space dimensions
46+
The Hamiltonian of the TFIM reads
47+
$$
48+
H = J_x \sum_{\langle i,j \rangle} \sigma_i^x \sigma_j^x + J_y \sum_{\langle i,j \rangle} \sigma_i^y \sigma_j^y + J_z \sum_{\langle i,j \rangle} \sigma_i^z \sigma_j^z + h_x \sum_i \sigma_i^x,
49+
$$
50+
where $ \sigma_i^{x,y,z} $ are the Pauli matrices acting on site $ i $ and $ \langle i,j \rangle $ denotes nearest neighbors. The collapse operators are given by
51+
$$
52+
c_i = \sqrt{\gamma} \sigma_i^-,
53+
$$
54+
where $ \sigma_i^- $ is the lowering operator acting on site $ i$. The many-body operators are constructed using
55+
56+
```{julia}
57+
Jx = 0.9
58+
Jy = 1.04
59+
Jz = 1.0
60+
hx = 0.0
61+
γ = 1
62+
63+
Sx = mapreduce(i->MultiSiteOperator(latt, i => sigmax()), +, 1:latt.N)
64+
Sy = mapreduce(i->MultiSiteOperator(latt, i => sigmay()), +, 1:latt.N)
65+
Sz = mapreduce(i->MultiSiteOperator(latt, i => sigmaz()), +, 1:latt.N)
66+
67+
SFxx = sum([MultiSiteOperator(latt, i => sigmax()) * MultiSiteOperator(latt, j => sigmax()) for i in 1:latt.N for j in 1:latt.N])
68+
69+
H, c_ops = DissipativeIsing(Jx, Jy, Jz, hx, 0., 0., γ, latt; boundary_condition=:periodic_bc, order=1)
70+
e_ops = (Sx, Sy, Sz, SFxx)
71+
72+
tl = LinRange(0, 10, 100);
73+
```
74+
75+
### Constructing the low-rank basis
76+
We proceed by constructing the low-rank basis. `N_cut` is the dimension of the Hilbert space of each mode, and `N_modes` is the number of modes (or spins).
77+
We consider an initial low-rank basis with `M = Nx * Ny + 1` states.
78+
79+
We first define the lr-space dimensions
3880

3981
```{julia}
40-
N_cut = 2
41-
N_modes = latt.N
42-
N = N_cut^N_modes
43-
M = latt.N + 1
82+
N_cut = 2 # Number of states of each mode
83+
N_modes = latt.N # Number of modes
84+
N = N_cut^N_modes # Total number of states
85+
86+
M = latt.N + 1; # Number of states in the LR basis
4487
```
4588

46-
Define `lr` states. Take as initial state all spins up. All other `N` states are taken as those with minimum Hamming distance to the initial state.
89+
Since we will take as initial state for our dynamics the pure state with all spins pointing up, the initial low-rank basis must include at least this state.
4790

4891
```{julia}
4992
ϕ = Vector{QuantumObject{KetQuantumObject,Dimensions{M - 1,NTuple{M - 1,Space}},Vector{ComplexF64}}}(undef, M)
5093
ϕ[1] = kron(fill(basis(2, 1), N_modes)...)
94+
```
95+
96+
The remaining `M-1` states are taken as those with minimal Hamming distance from the latter state, that is those we obtain by flipping the spin of a single site with respect to the completely polarized state.
5197

98+
```{julia}
5299
i = 1
53100
for j in 1:N_modes
54101
global i += 1
55102
i <= M && (ϕ[i] = MultiSiteOperator(latt, j=>sigmap()) * ϕ[1])
56103
end
104+
57105
for k in 1:N_modes-1
58106
for l in k+1:N_modes
59107
global i += 1
60108
i <= M && (ϕ[i] = MultiSiteOperator(latt, k=>sigmap(), l=>sigmap()) * ϕ[1])
61109
end
62110
end
63-
for i in i+1:M
64-
ϕ[i] = QuantumObject(rand(ComplexF64, size(ϕ[1])[1]), dims = ϕ[1].dims)
65-
normalize!(ϕ[i])
66-
end
67111
```
68112

69-
Define the initial state
113+
At this point the vector of states `ϕ` contains the full representation of our low-rank states. These coefficients comprise matrix `z`.
114+
115+
The matrix `B`, on the other hand, contains the populations and coherences with which each of the low-rank states contributes to the density matrix.
116+
We initialize it so that only the first state is populated, and all other states are unpopulated.
117+
118+
We also compute the full density matrix `ρ` from the low-rank representation. Of course this defeats the purpose of the low-rank representation. We use it here for illustrative purposes to show that the low-rank predictions match the exact dynamics.
70119

71120
```{julia}
72121
z = hcat(get_data.(ϕ)...)
@@ -77,38 +126,33 @@ B = B / tr(S * B) # Normalize B
77126
ρ = QuantumObject(z * B * z', dims = ntuple(i->N_cut, Val(N_modes))); # Full density matrix
78127
```
79128

80-
Define the Hamiltonian and collapse operators
129+
### Full evolution
130+
We now compare the results of the low-rank evolution with the full evolution. We first evolve the system using the `mesolve` function
81131

82132
```{julia}
83-
# Define Hamiltonian and collapse operators
84-
Jx = 0.9
85-
Jy = 1.04
86-
Jz = 1.0
87-
hx = 0.0
88-
hy = 0.0
89-
hz = 0.0
90-
γ = 1
91-
92-
Sx = mapreduce(i->MultiSiteOperator(latt, i=>sigmax()), +, 1:latt.N)
93-
Sy = mapreduce(i->MultiSiteOperator(latt, i=>sigmay()), +, 1:latt.N)
94-
Sz = mapreduce(i->MultiSiteOperator(latt, i=>sigmaz()), +, 1:latt.N)
95-
96-
H, c_ops = DissipativeIsing(Jx, Jy, Jz, hx, hy, hz, γ, latt; boundary_condition = Val(:periodic_bc), order = 1)
97-
e_ops = (Sx, Sy, Sz)
98-
99-
tl = range(0, 10, 100)
133+
sol_me = mesolve(H, ρ, tl, c_ops; e_ops = [e_ops...])
134+
Strue = entropy_vn(sol_me.states[end], base=2) / latt.N;
100135
```
101136

102-
## Full evolution
137+
### Low Rank evolution
103138

104-
```{julia}
105-
sol_me = mesolve(H, ρ, tl, c_ops; e_ops = [e_ops...]);
106-
Strue = entropy_vn(sol_me.states[end], base=2) / latt.N
107-
```
139+
The `lr_mesolve` function allows to conveniently keep track of non-linear functionals of the density matrix during the evolution without ever computing the full density matrix and without the need to store `z` and `B` at each time step.
140+
To do so we define the functionals of the density matrix that we want to keep track of and that will be evaluated at each time step.
108141

109-
## Low Rank Evolution
142+
We compute the purity
143+
$$
144+
P = \mathrm{Tr}(\rho^2),
145+
$$
146+
the von Neumann entropy
147+
$$
148+
S = -\mathrm{Tr}(\rho \log_2(\rho)),
149+
$$
150+
and the trace
151+
$$
152+
\mathrm{Tr}(\rho).
153+
$$
110154

111-
Define functions to be evaluated during the low-rank evolution
155+
To maximize efficiency and minimize memory allocations we make use of preallocated variables stores in the `parameters` constructor of the solver.
112156

113157
```{julia}
114158
function f_purity(p, z, B)
@@ -138,18 +182,39 @@ function f_entropy(p, z, B)
138182
mul!(C, z, sqrt(B))
139183
mul!(σ, C', C)
140184
return entropy_vn(Qobj(Hermitian(σ), type=Operator), base=2)
141-
end
185+
end;
142186
```
143187

144-
Define the options for the low-rank evolution
188+
A critical aspect of the LR truncation is the possibility to dynamically adjust the dimension of the basis throughout the system's evolution $M=M(t)$. This adaptability is essential for accommodating changes in the system's entropy over time.
189+
190+
To adapt the dimension of the low-rank basis, we look at a control parameter $\chi$ that is positively correlated with the entropy of the system and provides a measure of the quality of the low-rank approximation. When $\chi$ exceeds a certain threshold, the dimension of the low-rank basis is increased by one.
191+
192+
The options below specify how the dimension of the low-rank basis is adjusted during the evolution.
145193

146194
```{julia}
147195
opt = (err_max = 1e-3, p0 = 0.0, atol_inv = 1e-6, adj_condition = "variational", Δt = 0.0);
196+
```
197+
198+
`err_max` is the maximum error allowed in the time evolution of the density matrix.
199+
200+
`p0` is the initial population with which the new state is added to the basis after crossing the threshold.
148201

202+
`adj_condition = "variational"` selects one of three possible definitions for the control quantity `chi`. Specifically, the selected option consists in the leakage from the variational manifold and is defined as
203+
$$
204+
\chi = \operatorname{Tr}(S^{-1} L).
205+
$$
206+
207+
Finally, `Δt` specifies the checkpointing interval by which the simulation is rewinded upon the basis expansion.
208+
209+
Not directly related to the basis expansion, but still important for the stability of the algorithm, are the options `atol_inv` (the tolerance for the inverse of the overlap matrix) and `alg` (the ODE solver).
210+
211+
We now launch the evolution using the `lr_mesolve` function
212+
213+
```{julia}
149214
sol_lr = lr_mesolve(H, z, B, tl, c_ops; e_ops = e_ops, f_ops = (f_purity, f_entropy, f_trace), opt = opt);
150215
```
151216

152-
Plot the results
217+
We can now compare the results of the low-rank evolution with the full evolution.
153218

154219
```{julia}
155220
m_me = real(sol_me.expect[3, :]) / Nx / Ny

0 commit comments

Comments
 (0)