"""Model Hamiltonian utilities."""
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import numpy as np
from qdk_chemistry._core.utils.model_hamiltonians import (
create_hubbard_hamiltonian,
create_huckel_hamiltonian,
create_ppp_hamiltonian,
mataga_nishimoto_potential,
ohno_potential,
pairwise_potential,
to_pair_param,
to_site_param,
)
from qdk_chemistry.data import LatticeGraph, PauliOperator, QubitHamiltonian
__all__ = [
"create_heisenberg_hamiltonian",
"create_hubbard_hamiltonian",
"create_huckel_hamiltonian",
"create_ising_hamiltonian",
"create_ppp_hamiltonian",
"mataga_nishimoto_potential",
"ohno_potential",
"pairwise_potential",
]
def _pauli_expr_to_qubit_hamiltonian(expr, num_qubits: int) -> QubitHamiltonian:
"""Convert a simplified PauliOperator expression to a QubitHamiltonian."""
simplified = expr.simplify()
terms = simplified.to_canonical_terms(num_qubits)
pauli_strings = [t[1][::-1] for t in terms]
coefficients = np.array([complex(t[0]) for t in terms])
return QubitHamiltonian(pauli_strings, coefficients)
[docs]
def create_heisenberg_hamiltonian(
graph: LatticeGraph,
jx: np.ndarray | float,
jy: np.ndarray | float,
jz: np.ndarray | float,
hx: np.ndarray | float = 0.0,
hy: np.ndarray | float = 0.0,
hz: np.ndarray | float = 0.0,
) -> QubitHamiltonian:
r"""Create the anisotropic Heisenberg model Hamiltonian on a lattice.
.. math::
H = \sum_{\langle i,j \rangle} w_{ij}\,\bigl[
J_x^{ij}\,\sigma_i^x \sigma_j^x
+ J_y^{ij}\,\sigma_i^y \sigma_j^y
+ J_z^{ij}\,\sigma_i^z \sigma_j^z
\bigr]
+ \sum_i \bigl[
h_x^{i}\,\sigma_i^x
+ h_y^{i}\,\sigma_i^y
+ h_z^{i}\,\sigma_i^z
\bigr]
where :math:`w_{ij}` is the edge weight from the lattice adjacency matrix.
Each qubit corresponds to a lattice site.
Args:
graph: Lattice graph defining the connectivity.
jx: Coupling constant for XX interactions. Scalar (uniform) or ``(n, n)`` array for per-pair values.
jy: Coupling constant for YY interactions (same format as *jx*).
jz: Coupling constant for ZZ interactions (same format as *jx*).
hx: External magnetic field in the x direction. Scalar or length-n array. Defaults to 0.
hy: External magnetic field in the y direction. Defaults to 0.
hz: External magnetic field in the z direction. Defaults to 0.
Returns:
QubitHamiltonian: The Heisenberg model as a qubit Hamiltonian.
"""
if not graph.is_symmetric:
raise ValueError("Lattice graph must be symmetric for a valid Hamiltonian.")
n = graph.num_sites
adj = graph.adjacency_matrix()
jx_mat = to_pair_param(jx, graph, "jx")
jy_mat = to_pair_param(jy, graph, "jy")
jz_mat = to_pair_param(jz, graph, "jz")
hx_vec = to_site_param(hx, graph, "hx")
hy_vec = to_site_param(hy, graph, "hy")
hz_vec = to_site_param(hz, graph, "hz")
h = 0.0 * PauliOperator.I(0) # seed with zero expression
# Two-body interaction terms (each edge counted once: i < j), scaled by the lattice edge weight.
for i in range(n):
for j in range(i + 1, n):
edge_weight = adj[i, j]
if edge_weight == 0.0:
continue
if jx_mat[i, j] != 0.0:
h = h + jx_mat[i, j] * edge_weight * PauliOperator.X(i) * PauliOperator.X(j)
if jy_mat[i, j] != 0.0:
h = h + jy_mat[i, j] * edge_weight * PauliOperator.Y(i) * PauliOperator.Y(j)
if jz_mat[i, j] != 0.0:
h = h + jz_mat[i, j] * edge_weight * PauliOperator.Z(i) * PauliOperator.Z(j)
# Single-body field terms
for i in range(n):
if hx_vec[i] != 0.0:
h = h + hx_vec[i] * PauliOperator.X(i)
if hy_vec[i] != 0.0:
h = h + hy_vec[i] * PauliOperator.Y(i)
if hz_vec[i] != 0.0:
h = h + hz_vec[i] * PauliOperator.Z(i)
return _pauli_expr_to_qubit_hamiltonian(h, n)
[docs]
def create_ising_hamiltonian(
graph: LatticeGraph,
j: np.ndarray | float,
h: np.ndarray | float = 0.0,
) -> QubitHamiltonian:
r"""Create the Ising model Hamiltonian on a lattice.
.. math::
H = \sum_{\langle i,j \rangle} w_{ij}\,J^{ij}\,\sigma_i^z \sigma_j^z
+ \sum_i h^{i}\,\sigma_i^x
where :math:`w_{ij}` is the edge weight from the lattice adjacency matrix.
Args:
graph: Lattice graph defining the connectivity.
j: Coupling constant for ZZ interactions. Scalar or ``(n, n)`` array.
h: Transverse field strength (x direction). Scalar or length-n array. Defaults to 0.
Returns:
QubitHamiltonian: The Ising model as a qubit Hamiltonian.
"""
return create_heisenberg_hamiltonian(graph, jx=0.0, jy=0.0, jz=j, hx=h)