The algorithm#
Problem formulation#
In the simplest setting, Pymablock finds a series of the unitary transformation \(\mathcal{U}\) (we use calligraphic letters to denote series) that block-diagonalizes the Hamiltonian
with \(\mathcal{H}' = \mathcal{H}'_{S} + \mathcal{H}'_{R}\) containing an arbitrary number and orders of perturbations with block-diagonal components \(\mathcal{H}'_{S}\) and block-offdiagonal components \(\mathcal{H}'_{R}\), respectively. Pymablock’s algorithm, however, does not just work with \(2 \times 2\) block matrices: it can perturbatively cancel any set of offdiagonal elements of \(\mathcal{H}\). For that we define the selected part of an operator (subscript \(S\)) as the subset of its matrix elements that we want to keep, and the remaining part (subscript \(R\)) as the rest. For example, selected and remaining parts may look like this:
As another generalization compared to the simplest case, the series here may be multivariate, and they represent sums of the form
where \(\lambda_i\) are the perturbation parameters and \(A_{n_1, n_2, \ldots, n_k}\) are linear operators.
The problem statement, therefore, is finding \(\mathcal{U}\) and \(\tilde{\mathcal{H}}\) such that:
where series multiply according to the Cauchy product:
Computational complexity of the Cauchy product
The Cauchy product is the most expensive operation in perturbation theory, because it involves a large number of multiplications between potentially large matrices, so let us discuss its complexity. Evaluating \(\mathbf{n}\)-th order of \(\mathcal{C}=\mathcal{AB}\) requires \(\sim\prod_i n_i = N\) multiplications of the series elements. A direct computation of all the possible index combinations in a product between three series \(\mathcal{A}\mathcal{B}\mathcal{C}\) would have a higher cost \(\sim N^2\), however if we use associativity of the product and compute this as \((\mathcal{A}\mathcal{B})\mathcal{C}\), then the scaling of the cost stays \(\sim N\).
How to solve the problem?#
There are many ways to solve this problem that give identical expressions for \(\mathcal{U}\) and \(\tilde{\mathcal{H}}\). We are searching for a procedure that satisfies three additional constraints:
It has the same complexity scaling as a Cauchy product.
It does not require multiplications by \(H_0\). This is because in perturbation theory, \(n\)-th order corrections to \(\tilde{\mathcal{H}}\) carry \(n\) energy denominators \(1/(E_i - E_j)\) (see here). Therefore, any additional multiplications by \(H_0\) must cancel with additional energy denominators. Multiplying by \(H_0\) is therefore unnecessary work, and it gives longer intermediate expressions.
It requires only one Cauchy product by \(\mathcal{H}_{S}\), the selected part of \(\mathcal{H}\). By considering \(\mathcal{H}_{R}=0\), we observe that \(\mathcal{H}_{S}\) must at least enter \(\tilde{\mathcal{H}}\) as an added term, without any products. Moreover, because \(\mathcal{U}\) depends on the entire Hamiltonian, there must be at least one Cauchy product by \(\mathcal{H}'_{S}\). This lower bound turns out to be possible to achieve.
The goal of our algorithm is thus to be efficient and to produce compact results that do not require further simplifications.
Our solution#
To find \(\mathcal{U}\), let us separate it into an identity and \(\mathcal{U}' = \mathcal{W} + \mathcal{V}\):
First, we use the unitarity condition \(\mathcal{U}^\dagger \mathcal{U} = 1\) by substituting \(\mathcal{U}'\) into it and obtain
Because \(\mathcal{U}'\) has no \(0\)-th order term, \((\mathcal{U}'^\dagger \mathcal{U}')_\mathbf{n}\) does not depend on the \(\mathbf{n}\)-th order of \(\mathcal{U}'\) nor \(\mathcal{W}\). More generally, a Cauchy product \(\mathcal{A}\mathcal{B}\) where \(\mathcal{A}\) and \(\mathcal{B}\) have no \(0\)-th order terms depends on \(\mathcal{A}_1, \ldots, \mathcal{A}_{n-1}\) and \(\mathcal{B}_1, \ldots, \mathcal{B}_{n-1}\). This allows us to use Cauchy products to define recurrence relations, which we apply throughout the algorithm. Therefore, we compute \(\mathcal{W}\) as a Cauchy product of \(\mathcal{U}'\) with itself. This recurrence relation is the first secret ingredient of Pymablock✨
Choosing the right definition for \(\mathcal{W}\)
Using the definition of \(\mathcal{W}\) as the Hermitian part of \(\mathcal{U}'\), and the unitarity condition:
we see that we could alternatively define \(\mathcal{W}\) as a Taylor series in \(\mathcal{V}\):
however the scaling of this definition is worse than that of a Cauchy product because we need to compute a Taylor expansion of a series:
A direct computation of all possible products of terms would require \(\sim \exp N\) multiplications. We improve on this by defining a new series as \(\mathcal{A}^{n+1} = \mathcal{A}\mathcal{A}^{n}\) and reusing the previously computed results, which brings these costs down to \(\sim N^2\)—still worse than a cost of a single Cauchy product. Using the Taylor expansion approach is therefore both more complicated and more computationally expensive than the recurrent definition in (4).
To compute \(\mathcal{U}'\) we also need to find \(\mathcal{V}\), which is defined by the requirement \(\tilde{\mathcal{H}}_{R} = 0\). Additionally, we constrain \(\mathcal{V}\) to have no selected part: \(\mathcal{V}_{S} = 0\), so that its norm and the norm of \(\mathcal{U}'\) are minimal. This also makes our transformation equivalent to Schrieffer-Wolff transformation in the \(2\times 2\) block case.
Equivalence to Schrieffer-Wolff transformation
Both the Pymablock algorithm applied to a \(2\times 2\) block matrix and the Schrieffer-Wolff transformation find a unitary transformation \(\mathcal{U}\) such that \(\tilde{\mathcal{H}}^{AB}=0\). They are therefore equivalent up to a gauge choice in each subspace, \(A\) and \(B\). We establish the equivalence between the two by demonstrating that this gauge choice is the same for both algorithms.
The Schrieffer-Wolff transformation uses \(\mathcal{U} = \exp \mathcal{S}\), where \(\mathcal{S} = -\mathcal{S}^\dagger\) and \(\mathcal{S}^{AA} = \mathcal{S}^{BB} = 0\).
The series \(\exp\mathcal{S}\) contains all possible products of \(S_n\) of all lengths with fractional prefactors. For every term \(S_{k_1}S_{k_2}\cdots S_{k_n}\), there is a corresponding term \(S_{k_n}S_{k_{n-1}}\cdots S_{k_1}\) with the same prefactor. If the number of \(S_{k_n}\) is even, then both terms are block-diagonal since each \(S_n\) is block off-diagonal. Because \(S_n\) are anti-Hermitian, the two terms are Hermitian conjugates of each other, and therefore their sum is Hermitian. On the other hand, if the number of \(S_{k_n}\) is odd, then the two terms are block off-diagonal and their sum is anti-Hermitian by the same reasoning.
Therefore, just like in our algorithm, the diagonal blocks of \(\exp S\) are Hermitian, while off-diagonal blocks are anti-Hermitian. Schrieffer-Wolff transformation produces a unique answer and satisfies the same diagonalization requirements as our algorithm, which means that the two are equivalent.
To find \(\mathcal{V}\), we need to first look at the transformed Hamiltonian:
where we used \(\mathcal{U}=1+\mathcal{U}'\) and \(\mathcal{H} = \mathcal{H}_{S} + \mathcal{H}'_{R}\), since \((H_0)_{R}=0\) by definition.
Because we want to avoid unnecessary products by \(\mathcal{H}_{S}\), we need to get rid of the terms that contain it by replacing them with an alternative expression. Our strategy is to define an auxiliary operator \(\mathcal{X}\) that we can compute without ever multiplying by \(\mathcal{H}_{S}\). Like \(\mathcal{U}'\), \(\mathcal{X}\) needs to be defined via a recurrence relation, which we will find later. Because the expression above has \(\mathcal{H}_{S}\) multiplied by \(\mathcal{U}'\) from the left and from the right, we get rid of these terms by making sure that \(\mathcal{H}_{S}\) multiplies terms from one side only. To achieve this, we choose \(\mathcal{X}=\mathcal{Y}+\mathcal{Z}\) to be the commutator between \(\mathcal{U}'\) and \(\mathcal{H}_{S}\):
In the common case, when \(\mathcal{A}_{S}\) is a block-diagonal part of a matrix, \(\mathcal{Y}\) is block off-diagonal. Additionally, in the case of \(2\times 2\) block diagonalization, \(\mathcal{Z}\) is block-diagonal. We use \(\mathcal{H}_{S} \mathcal{U}' = \mathcal{U}' \mathcal{H}_{S} -\mathcal{X}\) to move \(\mathcal{H}_{S}\) through to the right and find
where the terms multiplied by \(\mathcal{H}_{S}\) cancel by unitarity.
The transformed Hamiltonian does not contain products by \(\mathcal{H}_{S}\) anymore, but it does depend on \(\mathcal{X}\), an auxiliary operator whose recurrent definition we do not know yet. To find it, we first focus on its anti-Hermitian part, \(\mathcal{Z}\). Since recurrence relations are expressions whose right hand side contains Cauchy products between series, we need to find a way to make a product appear. This is where the unitarity condition \(\mathcal{U}'^\dagger + \mathcal{U}' = -\mathcal{U}'^\dagger \mathcal{U}'\) comes in handy and gives:
Similar to computing \(W_\mathbf{n}\), computing \(Z_\mathbf{n}\) requires lower orders of \(\mathcal{X}\) and \(\mathcal{U}'\), all blocks included. This is our second secret ingredient✨
Then, we compute \(\mathcal{Y}\) (the Hermitian part of \(\mathcal{X}\)) by requiring that \(\tilde{\mathcal{H}}_{R} = 0\) and find
Once again, despite \(\mathcal{X}\) enters the right hand side, because all the terms lack 0-th order, this defines a recursive relation for \(\mathcal{Y}_{R}\). To find \(\mathcal{Y}_{S}\) we use the definition of \(\mathcal{Y}\) in (5), which fixes \(\mathcal{V}\) as a solution of:
which is a continuous-time Lyapunov equation for \(\mathcal{V}\). In order for this equation to be satisfiable, the selected part of the right hand side must vanish, since the left hand side only has the remaining part. Therefore we find the selected part of \(\mathcal{Y}\) as:
and it vanishes if \(\mathcal{A}_{S}\) corresponds to a block-diagonal matrix. This is our last secret ingredient✨
The final part is straightforward. Finding \(\mathcal{V}\) from \(\mathcal{Y}\) amounts to solving a Sylvester’s equation, which we only need to solve once for every new order. This is the only step in the algorithm that requires a direct multiplication by \(\mathcal{H}'_{S}\). In the eigenbasis of \(H_0\), the solution of Sylvester’s equation is \(V_{\mathbf{n}, ij} = (\mathcal{Y}_{R} - [\mathcal{V}, \mathcal{H}'_{S}]_{R})_{\mathbf{n}, ij}/(E_i - E_j)\), where \(E_i\) are the eigenvalues of \(H_0\). However, even if the eigenbasis of \(H_0\) is not available, there are efficient algorithms to solve Sylvester’s equation, see below.
Actual algorithm#
We now have the complete algorithm:
Define series \(\mathcal{U}'\) and \(\mathcal{X}\) and make use of their block structure and Hermiticity.
To define the Hermitian part \(\mathcal{U}'\), use \(\mathcal{W} = -\mathcal{U}'^\dagger\mathcal{U}'/2\).
To find the antihermitian part of \(\mathcal{U}'\), solve Sylvester’s equation \([\mathcal{V}, H_0] = (\mathcal{Y} - [\mathcal{V}, \mathcal{H}'_{S}])_{R}\). This requires \(\mathcal{X}\).
To find the antihermitian part of \(\mathcal{X}\), define \(\mathcal{Z} = (-\mathcal{U}'^\dagger\mathcal{X} + \mathcal{X}^\dagger\mathcal{U}')/2\).
For the Hermitian part of \(\mathcal{X}\), use \(\mathcal{Y} = (-\mathcal{U}'^\dagger\mathcal{X} + \mathcal{U}^\dagger\mathcal{H}'_{R}\mathcal{U})_{R} + [\mathcal{V}, \mathcal{H}'_{S}]_{S}\).
Compute the effective Hamiltonian as \(\tilde{\mathcal{H}}_{S} = \mathcal{H}_{S} - \mathcal{X} - \mathcal{U}'^\dagger \mathcal{X} + \mathcal{U}^\dagger\mathcal{H}'_{R}\mathcal{U}\).
Extra optimization: common subexpression elimination
We further optimize the algorithm by reusing products that are needed in several places.
Firstly, we rewrite the expressions for \(\mathcal{Z}\) and \(\tilde{\mathcal{H}}\) by utilizing the Hermitian conjugate of \(\mathcal{U}'^\dagger \mathcal{X}\) without recomputing it:
where \(\textrm{h.c.}\) is the Hermitian conjugate, and \(\mathcal{Z}\) drops out from \(\tilde{\mathcal{H}}_{S}\) because \(\mathcal{Z}\) is anti-Hermitian.
To compute \(\mathcal{U}^\dagger \mathcal{H}'_{R} \mathcal{U}\) faster, we express it using \(\mathcal{A} \equiv \mathcal{H}'_{R}\mathcal{U}'\):
To further optimize the computations, we observe that some products appear both in \(\mathcal{U}'^\dagger \mathcal{X}\) and \(\mathcal{U}^\dagger \mathcal{H}'_{R} \mathcal{U}\). We reuse these products by introducing \(\mathcal{B} = \mathcal{X} - \mathcal{H}'_{R} - \mathcal{A}\).
Using this definition, we first express the remaining part of \(\mathcal{B}\) as follows:
where we also used Eq. (8) and the definition of \(\mathcal{A}\).
The selected part of \(\mathcal{B}\) is then
where we used Eq. (7) and that the \(\mathcal{U}'^\dagger \mathcal{A}\) is Hermitian.
Using \(\mathcal{B}\) changes the relation for \(\mathcal{V}\) in Eq. (9) to
Finally, we compute the selected part of \(\tilde{\mathcal{H}}\) as
How to use Pymablock on large numerical Hamiltonians?#
Solving Sylvester’s equation and computing the matrix products are the most expensive steps of the algorithms for large Hamiltonians. Pymablock can efficiently construct an effective Hamiltonian of a small subspace even when the full Hamiltonian is a sparse matrix that is too costly to diagonalize. It does so by avoiding forming a matrix representation of operators in the implicit subspace—the one without known eigenvectors, and by utilizing the sparsity of the Hamiltonian to compute the Green’s function. To do so, Pymablock uses either the MUMPS sparse solver via the python-mumps wrapper or the KPM method.
This approach was originally introduced in this work.
Implementation details
We use the matrix \(\Psi_E\) of the known eigenvectors of the explicit subspace to rewrite the Hamiltonian as
where \(P_I = 1 - \Psi_E \Psi_E^\dagger\) is the projector onto the implicit subspace. This Hamiltonian is larger in size than the original one because the implicit block has additional eigenvectors with zero eigenvalue corresponding to the explicit subspace. This, however, allows to preserve the sparsity structure of the Hamiltonian by applying \(P_I\) and \(\mathcal{H}\) separately. Additionally, applying \(P_I\) is efficient because \(\Psi_E\) is a low rank matrix. We then perform perturbation theory of the rewritten \(\mathcal{H}\).
To solve the Sylvester’s equation for the modified Hamiltonian, we write it for every row of \(V_{\mathbf{n}}^{EI}\) separately:
This equation is well-defined despite \(E_i - H_0\) is not invertible because \(Y_{\mathbf{n}}\) has no components in the explicit subspace.