Sparse vs Dense ScalingΒΆ
Model. This notebook compares dense and sparse matrix construction for selected tight-binding and Hubbard builders. Sparse matrices store only nonzero entries and are useful for low-energy calculations.
Typical uses. Memory-scaling checks, deciding when to switch to sparse workflows, low-energy spectra with eigsh, and sanity checks that sparse and dense builders agree.
Parameters. n_sites, n_rows, n_cols, and max_occupancy drive dimension growth. nnz counts sparse nonzero entries. lowest_eigenvalues(H, k) computes low-energy values with sparse solvers for sparse matrices.
Useful plots. Dimension and nonzero-count tables, sparse/dense eigenvalue agreement, and low-energy spectra.
import matplotlib.pyplot as plt
import numpy as np
from quantum_lattice_models.models import (
bose_hubbard_chain,
bose_hubbard_chain_sparse,
fermi_hubbard_chain,
fermi_hubbard_chain_sparse,
square_lattice_tight_binding,
square_lattice_tight_binding_sparse,
)
from quantum_lattice_models.spectra import eigenvalues, lowest_eigenvalues
comparisons = []
for n in [2, 3, 4]:
dense = bose_hubbard_chain(n_sites=n, max_occupancy=2)
sparse = bose_hubbard_chain_sparse(n_sites=n, max_occupancy=2)
comparisons.append((f"Bose n={n}", dense.shape[0], sparse.nnz, sparse.nnz / dense.size))
for n in [2, 3]:
dense = fermi_hubbard_chain(n_sites=n)
sparse = fermi_hubbard_chain_sparse(n_sites=n)
comparisons.append((f"Fermi n={n}", dense.shape[0], sparse.nnz, sparse.nnz / dense.size))
for rows, cols in [(4, 4), (6, 6)]:
dense = square_lattice_tight_binding(rows, cols)
sparse = square_lattice_tight_binding_sparse(rows, cols)
comparisons.append((f"Square {rows}x{cols}", dense.shape[0], sparse.nnz, sparse.nnz / dense.size))
print("model | dimension | nonzeros | sparse density")
print("--- | --- | --- | ---")
for label, dimension, nnz, density in comparisons:
print(f"{label:<14s} | {dimension:>9d} | {nnz:>8d} | {density:.4f}")
model | dimension | nonzeros | sparse density --- | --- | --- | --- Bose n=2 | 9 | 13 | 0.1605 Bose n=3 | 27 | 67 | 0.0919 Bose n=4 | 81 | 281 | 0.0428 Fermi n=2 | 16 | 23 | 0.0898 Fermi n=3 | 64 | 165 | 0.0403 Square 4x4 | 16 | 48 | 0.1875 Square 6x6 | 36 | 120 | 0.0926
dense = square_lattice_tight_binding(3, 3)
sparse = square_lattice_tight_binding_sparse(3, 3)
dense_lowest = eigenvalues(dense)[:4]
sparse_lowest = lowest_eigenvalues(sparse, k=4)
print("Square 3x3 lowest eigenvalues")
print(" dense: ", np.array2string(dense_lowest, precision=6, separator=", "))
print(" sparse:", np.array2string(sparse_lowest, precision=6, separator=", "))
Square 3x3 lowest eigenvalues dense: [-2.828427e+00, -1.414214e+00, -1.414214e+00, -1.737460e-16] sparse: [-2.828427e+00, -1.414214e+00, -1.414214e+00, -1.906322e-18]
labels = [row[0] for row in comparisons]
dimensions = [row[1] for row in comparisons]
nnz = [row[2] for row in comparisons]
fig, ax = plt.subplots(figsize=(7, 3.5))
x = np.arange(len(labels))
ax.bar(x - 0.18, dimensions, width=0.36, label="dimension")
ax.bar(x + 0.18, nnz, width=0.36, label="nnz")
ax.set_xticks(x)
ax.set_xticklabels(labels, rotation=35, ha="right")
ax.set_yscale("log")
ax.set_title("Sparse matrix size summary")
ax.legend()
fig.tight_layout()