Hamiltonian
The Hamiltonian class represents a symbolic Hamiltonian as a sum of weighted Pauli operators. You can create Hamiltonians using the built-in Pauli operators and combine them with standard arithmetic operations.
Constructing
To construct a Hamiltonian with a single Pauli, you can use the constructors X(i), Y(i), Z(i), I(i).
From these single-qubit operators, you can build multi-qubit Hamiltonians using arithmetic operations.
The operations follow Python syntax, for example: 2 * Z(0) + Z(1) and Z(0) * Z(1) build multi-qubit Hamiltonians.
Common Hamiltonians
Alternatively, for the models that come up most often there are named constructors:
Constructor |
Hamiltonian |
|---|---|
\(\sum_i h_x X_i\) |
|
\(\sum_i h_z Z_i\) |
|
\(\sum_{i<j} J Z_i Z_j + \sum_i h_z Z_i\) |
|
\(\sum_i J Z_i Z_{i+1} + \sum_i h_z Z_i\) |
|
\(\sum_{\langle i, j \rangle} J Z_i Z_j + \sum_i h_z Z_i\) on a square lattice |
|
\(\sum_{i<j} J Z_i Z_j + \sum_i h_x X_i + \sum_i h_z Z_i\) |
|
\(\sum_{i<j} \left( J_x X_i X_j + J_y Y_i Y_j + J_z Z_i Z_j \right) + \sum_i h_z Z_i\) |
from qilisdk.analog import Hamiltonian
H = Hamiltonian.transverse_field_ising(nqubits=2, x_coefficient=1.3, zz_coefficient=-2)
print(H)
Output:
1.3 X(0) + 1.3 X(1) - 2 Z(0) Z(1)
Every coefficient argument accepts either a single value, shared by every term it weights, or a list holding one value per term, so any of the constructors above can build a non-uniform model just by passing a list instead of a number. The values are taken in the order the terms are generated, which each constructor documents.
from qilisdk.analog import Hamiltonian
H = Hamiltonian.transverse_field_ising(nqubits=2, x_coefficient=[1.3, -0.7], zz_coefficient=[-2])
print(H)
Output:
1.3 X(0) - 0.7 X(1) - 2 Z(0) Z(1)
List of Operations
Arithmetic operations:
Addition:
H1 + H2Scalar multiplication:
5 * Hmultiplication:
H0 * H1Subtraction:
H1 - H2Division by scalar:
H / 5Negation:
-H
Extra Symbolic Operators:
commutator:
H1.commutator(H2)anticommutator:
H1.anticommutator(H2)vector_norm:
H.vector_norm()frobenius_norm:
H.frobenius_norm()trace:
H.trace()
Exporting Hamiltonians:
to matrix:
H.to_matrix(nqubits)to qtensor:
H.to_qtensor(nqubits)
Importing Hamiltonians:
from qtensor:
Hamiltonian.from_qtensor(qtensor)from string:
Hamiltonian.parse(hamiltonian_string)
Common Hamiltonians:
transverse field:
Hamiltonian.transverse_field(nqubits)longitudinal field:
Hamiltonian.longitudinal_field(nqubits)Ising:
Hamiltonian.ising(nqubits)Ising chain:
Hamiltonian.ising_chain(nqubits)Ising grid:
Hamiltonian.ising_grid(rows, columns)transverse-field Ising:
Hamiltonian.transverse_field_ising(nqubits)Heisenberg:
Hamiltonian.heisenberg(nqubits)
Example: Ising Hamiltonian
To define an Ising Hamiltonian of the form:
you can use the Pauli Z operators from the library:
from qilisdk.analog import Z
nqubits = 3
J = {(0, 1): 1, (0, 2): 2, (1, 2): 4}
h = {0: 1, 1: 2, 2: 3}
coupling = sum(weight * Z(i) * Z(j) for (i, j), weight in J.items())
fields = sum(weight * Z(i) for i, weight in h.items())
H = -(coupling + fields)
print(H)
Output:
- Z(0) Z(1) - 2 Z(0) Z(2) - 4 Z(1) Z(2) - Z(0) - 2 Z(1) - 3 Z(2)
Visualizing
H.draw() renders a Hamiltonian as an interaction graph:
Every qubit is a node, whose disc is split into one slice per local field acting on it, labelled with its Pauli type.
Every two-qubit term is an edge between the qubits it couples, drawn with a line style per coupling type (see the legend).
Every term acting on three or more qubits is a star-shaped hyperedge joined at the centroid of the qubits involved.
Slice and edge colours encode the coefficient of the corresponding term, as described by the colour bar.
A constant (identity) term is annotated below the graph as an energy offset.
from qilisdk.analog import X, Z
nqubits = 3
J = {(0, 1): 1, (0, 2): 2, (1, 2): 4}
H = sum(weight * Z(i) * Z(j) for (i, j), weight in J.items()) + sum(X(i) for i in range(nqubits))
H.draw()
The appearance is controlled with HamiltonianStyle, which shares the
themes of the circuit and schedule renderers. It selects the rustworkx layout used to place the qubits
("spring", "circular", "shell", "spiral" or "random", or explicit positions), whether local
fields and couplings share a single colour scale, and which annotations are drawn:
from qilisdk.analog import X, Z
from qilisdk.utils.visualization.style import HamiltonianStyle
from qilisdk.utils.visualization.themes import dark
H = 5 * X(0) - 3 * Z(0) + X(1) + 0.1 * Z(0) * Z(1) + 0.05 * X(0) * X(1)
H.draw(
HamiltonianStyle(
theme=dark,
layout="circular",
title="My Hamiltonian",
# Local fields and couplings live on very different scales here, so give each its own colour bar.
separate_color_scales=True,
)
)
To save the figure instead of showing it, pass a filepath (the format is inferred from the extension):
from qilisdk.analog import X, Z
H = X(0) + Z(0) * Z(1)
H.draw(filepath="hamiltonian.png")