This is a tool to calculate the general form of a \(\mathbf{k}\cdot\mathbf{p}\) Hamiltonian under a given symmetry constraint.

Please cite:

  • Identifying Topological Semimetals, D. Gresch, PhD Thesis.



You can install this tool with with pip:

pip install kdotp-symmetry

Example: TaAs2

Its usage is best explained with an example – I’ll pick a four-band Hamiltonian for TaAs2. If you’re interested in the physics of this particular example, it comes from a paper of ours where we also used this \(\mathbf{k}\cdot\mathbf{p}\) model. As for now, all we will need to know about the material is its symmetry, and the relevant representations for the given bands.

The symmetry group of the material is \(C2 / m\) (space group 12), which means it has rotation \(C_{2y}\), parity \(P\), mirror \(M_y\) and time-reversal symmetry \(\mathcal{T}\). For the analysis of the Hamiltonian we only need a generating set of the group, so we can pick \(C_{2y}\), \(P\) and \(\mathcal{T}\). In the particular basis we chose for this analysis, the real-space matrices for these symmetries are as follows:

\[\begin{split}C_{2y} =& ~\begin{pmatrix} 0&1&0 \\ 1&0&0 \\ 0&0&-1 \end{pmatrix}\\ P =& ~-\mathbb{1}_{3\times 3}\\ \mathcal{T} =& ~\mathbb{1}_{3\times 3}\end{split}\]

The corresponding representations are

\[\begin{split}C_{2y} =& ~\begin{pmatrix} i&0&0&0 \\ 0&-i&0&0 \\ 0&0&i&0 \\ 0&0&0&-i \end{pmatrix} \\ P =& ~\begin{pmatrix} 1&0&0&0 \\ 0&1&0&0 \\ 0&0&-1&0 \\ 0&0&0&-1 \end{pmatrix} \\ \mathcal{T} =& ~\begin{pmatrix} 0&-1&0&0 \\ 1&0&0&0 \\ 0&0&0&-1 \\ 0&0&1&0 \end{pmatrix} ~\hat{K},\end{split}\]

where \(\hat{K}\) is complex conjugation.

Creating the symmetry operations

In order to run the code, we must first specify the symmetries as described above. To do this, we use the symmetry_representation.SymmetryOperation class, which has three attributes:

  • rotation_matrix: The matrix describing the symmetry operation in real space coordinates.
  • repr_matrix: The matrix \(U\) of the symmetry representation.
  • repr_has_cc: A boolean flag which determines whether the representation is given by \(U\) alone, or if it contains a complex conjugation (i.e., the representation is given by \(U\hat{K}\)).

A more detailed description can be found on the symmetry_representation documentation .

The following code creates the symmetries described above:

from sympy.core.numbers import I
import sympy.physics.matrices as sm
from sympy.physics.quantum import TensorProduct
import symmetry_representation as sr

import kdotp_symmetry as kp

# In this project we used the basis of tensor products of Pauli matrices
pauli_vec = [sp.eye(2), *(sm.msigma(i) for i in range(1, 4))]
basis = [TensorProduct(p1, p2) for p1 in pauli_vec for p2 in pauli_vec]

# creating the symmetry operations
c2y = sr.SymmetryOperation(
    rotation_matrix=[[0, 1, 0], [1, 0, 0], [0, 0, -1]],
    repr_matrix=sp.diag(I, -I, I, -I),

parity = sr.SymmetryOperation(
    repr_matrix=sp.diag(1, 1, -1, -1),

time_reversal = sr.SymmetryOperation(
    repr_matrix=TensorProduct(sp.eye(2), sp.Matrix([[0, -1], [1, 0]])),


Since this tools performs symbolic operations, it uses the sympy module under the hood. To make sure that there are no rounding errors, I strongly recommend using sympy classes such as sympy.Matrix or sympy.Rational for all input.

Getting the basis for the symmetrized Hamiltonian

The basis of the symmetrized Hamiltonian can be constructed with the symmetric_hamiltonian() function. Besides the symmetries, it needs two inputs expr_basis and repr_basis.

The first, expr_basis, is a basis of the functions of \(\mathbf{k}\) that are considered, as a list of sympy expressions. To simply use powers of \(k_x, k_y, k_z\), you can use the monomial_basis() helper function. With this function, you can create the monomial basis for a given set of degrees. For example, to get a constant term and second degree terms as follows:

>>> import kdotp_symmetry as kp
>>> kp.monomial_basis(0, 2)
[1, kx**2, kx*ky, kx*kz, ky**2, ky*kz, kz**2]

The second, required input variable is repr_basis, which must be a basis of the hermitian matrices, with the same size as the symmetry representation. The basis must be orthogonal with respect to the Frobenius product. Again you can use a helper function, hermitian_basis(), giving the size as an argument:

>>> import kdotp_symmetry as kp
>>> kp.hermitian_basis(2)
[1, 0],
[0, 0]]), Matrix([
[0, 0],
[0, 1]]), Matrix([
[0, 1],
[1, 0]]), Matrix([
[0, -I],
[I,  0]])]

Finally, you can use the symmetric_hamiltonian() function to get the result. The complete code for the TaAs2 example can be found here.

The reference gives you an overview of the available functions and classes.


Finally, let me give a more formal description of the problem. Let \(G\) be the symmetry group that the Hamiltonian should respect, with a unitary representation \(D(g), g \in G\). This imposes the symmetry constraint

\[\mathcal{H}(\mathbf{k}) = D(g) \mathcal{H}(g^{-1} \mathbf{k}) D(g^{-1}), \forall g \in G\]

on the \(\mathbf{k} \cdot \mathbf{p}\) Hamiltonian. In the following, we will define a vector space containing \(\mathcal{H}\), and see how the symmetry constraint restricts the Hamiltonian to a certain subspace.

We want to consider only a certain form of \(\mathbf{k}\) - dependence for the Hamiltonian, for example up to second order. So let \(V \subset \mathcal{F}(\mathbb{R}^3, \mathbb{R})\) be the vector space which contains these functions of \(\mathbf{k}\). We require that \(V\) is closed under

\[\begin{split}\hat{F}_g: f \longmapsto \tilde{f}_g \\ \tilde{f}(\mathbf{k}) = f(g^{-1}\mathbf{k}).\end{split}\]

for all \(g \in G\). That is,

\[\forall g \in G, f \in V: \hat{F}_g(f) \in V.\]

\(\hat{F}_g\) is a linear operator on \(V\).

Let \(W\) be the vector space of hermitian \(N \times N\) matrices.

\[\begin{split}\hat{G}_g: ~&W & \longrightarrow W\\ & A & \longmapsto D(g)A D(g^{-1})\end{split}\]

is a linear operator on W. It is unitary under the Frobenius inner product.

Since the Hamiltonian is a hermitian matrix with \(\mathbf{k}\)-dependence as given by \(V\), it follows that \(\mathcal{H} \in V\otimes W\). The symmetry constraints mean that

\[\forall g \in G:~ \left(\hat{F}_g \otimes \hat{G}_g\right)(\mathcal{H}) = \mathcal{H},\]

and thus

\[\mathcal{H} \in \bigcap_{g\in G} \text{Eig}(\hat{F}_g \otimes \hat{G}_g , 1).\]

In conclusion, the problem of finding the general form of the Hamiltonian is equivalent to calculating this subspace.


The dimension of the vector space \(V \otimes W\) on which the kdotp-symmetry code operates grows linearly with the number of functions of \(\mathbf{k}\), and with the square of the dimension \(N\) of the Hamiltonian. Since parts of the algorithm (in particular finding the invariant subspace, and the intersection between invariant subspaces) scale cubically in this dimension of \(V \otimes W\), the scaling of the entire algorithm is quite bad. In short, the kdotp-symmetry code can be applied only for relatively small Hamiltonian sizes and moderate number of functions of \(\mathbf{k}\).