Hamiltonian Construction¶
A core design philosophy of Ket is to make the programmatic construction of observables, including Hamiltonians, mirror their formal mathematical definitions as closely as possible. By utilizing linear combinations and tensor products of the Pauli matrices \(I\), \(X\), \(Y\), and \(Z\), Ket provides a complete basis for defining any \(n\)-qubit observable.
To translate these mathematical expressions into native Python syntax, Ket introduces the with obs context manager. Within this execution block, the standard Pauli gate functions (I, X, Y, and Z) are temporarily redefined. Rather than applying unitary operations to a quantum circuit, they return observable objects, allowing programmers to construct operators using a syntax that directly reflects the underlying equations.
Scalar multiplication and division are fully supported. Complex scalars are also permitted, which is particularly useful for defining non-Hermitian operators such as the fermionic creation operator:
In Ket, this is implemented as:
with obs():
a_dg = (X(q) - 1j * Y(q)) / 2
The use of complex scalars further facilitates the description of observables involving matrix multiplication, which is supported natively via the Python @ operator. A practical example is the observable \(A\) utilized in the FALQON algorithm [7], defined by the commutator:
In Ket, this can be implemented using the commutator function or matmul @ operator:
A = 1j * commutator(Hm, Hc)
# Equivalently, using the matmul @ operator:
A = 1j * (Hm @ Hc - Hc @ Hm)
Note that because Hm and Hc are already instantiated as Hamiltonian objects, the with obs context manager is not required for these operations.
It is crucial to note that for expectation value calculations (as discussed in Section Expectation Values and Gradients), the final observable must be Hermitian. While intermediate steps or specific operators (such as \(\hat{a}^\dagger_q\)) may rely on complex coefficients, any Hamiltonian object passed directly to the exp_value function must ultimately resolve to real (float) coefficients, as physical observables strictly correspond to real eigenvalues.
Quadratic Unconstrained Binary Optimization (QUBO) formulations are frequently used in quantum optimization algorithms. QUBO variables take binary values \(x_q \in \{0, 1\}\), whereas the Pauli-\(Z\) observable has eigenvalues \(\{-1, +1\}\).
To map between these spaces, Ket provides a mapping function B for the transformation:
By utilizing the function B, arbitrary QUBO Hamiltonians can be implemented directly within Ket. As a practical example, the QUBO Hamiltonian for the Knapsack problem:
This mathematical formulation translates intuitively into the following Ket code:
def knapsack(w: list[float], v: list[float],
C: float, qubits: Quant, A: float) -> Hamiltonian:
obj = sum(v_i * B(q) for v_i, q in zip(v, qubits))
rest = (sum(w_i * B(q) for w_i, q in zip(w, qubits)) - C)**2
return -obj + A * rest
Hamiltonians are also used to define the time evolution of a quantum system via the unitary operator \(e^{-iH}\). Ket provides streamlined support for this through the evolve function. Currently, this function natively evolves Hamiltonians composed of 1-local terms and 2-local terms of the form \(XX\), \(YY\), and \(ZZ\).
A critical requirement of the evolve function is that it assumes all individual terms within the provided Hamiltonian mutually commute. If the Hamiltonian contains non-commuting terms, the function will not automatically approximate the evolution. Despite this restriction, the evolve function perfectly accommodates the mixing and cost layers of algorithms like QAOA and FALQON.