From 38a8781c1a37406de3e3aa933784e5e9dccc0732 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Sun, 21 Apr 2024 20:44:23 -0700 Subject: [PATCH 01/12] add 2D Fermi-Hubbard model draft --- python/ffsim/__init__.py | 1 + python/ffsim/operators/__init__.py | 1 + python/ffsim/operators/fermi_hubbard.py | 73 ++++++++++++++++++++ tests/python/operators/fermi_hubbard_test.py | 20 +++++- 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/python/ffsim/__init__.py b/python/ffsim/__init__.py index c8fd03742..26eb00789 100644 --- a/python/ffsim/__init__.py +++ b/python/ffsim/__init__.py @@ -126,6 +126,7 @@ "expectation_one_body_power", "expectation_one_body_product", "fermi_hubbard_1d", + "fermi_hubbard_2d", "fermion_operator", "hartree_fock_state", "indices_to_strings", diff --git a/python/ffsim/operators/__init__.py b/python/ffsim/operators/__init__.py index 4f1beddd1..06ac59099 100644 --- a/python/ffsim/operators/__init__.py +++ b/python/ffsim/operators/__init__.py @@ -33,5 +33,6 @@ "des_a", "des_b", "fermi_hubbard_1d", + "fermi_hubbard_2d", "number_operator", ] diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 5f5eee6eb..abd64c08b 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -85,3 +85,76 @@ def fermi_hubbard_1d( coeffs[(cre_b(p), des_b(p))] = -chemical_potential return FermionOperator(coeffs) + + +def fermi_hubbard_2d( + norb_x: int, + norb_y: int, + tunneling: float, + interaction: float, + *, + chemical_potential: float = 0, + nearest_neighbor_interaction: float = 0, + periodic: bool = False, +) -> FermionOperator: + r"""Two-dimensional Fermi-Hubbard model Hamiltonian. + + The Hamiltonian for the two-dimensional Fermi-Hubbard model on a square lattice with + :math:`N = N_x \times N_y` spatial orbitals is given by + + .. math:: + + H = -t \sum_{\sigma} \sum_{\braket{pq}} + (a^\dagger_{\sigma, p} a_{\sigma, q} + \text{h.c.}) + + U \sum_{p=1}^{N} n_{\alpha, p} n_{\beta, p} + - \mu \sum_{p=1}^{N} (n_{\alpha, p} + n_{\beta, p}) + + V \sum_{\sigma, \sigma'} \sum_{\braket{pq}} n_{\sigma, p} n_{\sigma', q} + + where :math:`\braket{\dots}` denotes nearest-neighbor pairs and + :math:`n_{\sigma, p} = a_{\sigma, p}^\dagger a_{\sigma, p}` is the number operator + on orbital :math:`p` with spin :math:`\sigma`. + + References: + - `The Hubbard Model`_ + + Args: + norb_x: The number of spatial orbitals in the x-direction :math:`N_x`. + norb_y: The number of spatial orbitals in the y-direction :math:`N_y`. + tunneling: The tunneling amplitude :math:`t`. + interaction: The onsite interaction strength :math:`U`. + chemical_potential: The chemical potential :math:`\mu`. + nearest_neighbor_interaction: The nearest-neighbor interaction strength + :math:`V`. + periodic: Whether to use periodic boundary conditions. + + Returns: + The two-dimensional Fermi-Hubbard model Hamiltonian. + + .. _The Hubbard Model: https://doi.org/10.1146/annurev-conmatphys-031620-102024 + """ + coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = {} + + for x in range(norb_x): + for y in range(norb_y): + x_right = (x + 1) % norb_x + y_up = (y + 1) % norb_y + + p = norb_y * x + y + p_right = norb_y * x_right + y + p_up = norb_y * x + y_up + + print(f"(p, u, r) = ({p}, {p_up}, {p_right})") + + if y != norb_y - 1 or periodic: + coeffs[(cre_a(p), des_a(p_up))] = -tunneling + coeffs[(cre_a(p_up), des_a(p))] = -tunneling + coeffs[(cre_b(p), des_b(p_up))] = -tunneling + coeffs[(cre_b(p_up), des_b(p))] = -tunneling + + if x != norb_x - 1 or periodic: + coeffs[(cre_a(p), des_a(p_right))] = -tunneling + coeffs[(cre_a(p_right), des_a(p))] = -tunneling + coeffs[(cre_b(p), des_b(p_right))] = -tunneling + coeffs[(cre_b(p_right), des_b(p))] = -tunneling + + return FermionOperator(coeffs) diff --git a/tests/python/operators/fermi_hubbard_test.py b/tests/python/operators/fermi_hubbard_test.py index 36e04a297..4af34b2c8 100644 --- a/tests/python/operators/fermi_hubbard_test.py +++ b/tests/python/operators/fermi_hubbard_test.py @@ -16,7 +16,7 @@ import scipy import ffsim -from ffsim.operators.fermi_hubbard import fermi_hubbard_1d +from ffsim.operators.fermi_hubbard import fermi_hubbard_1d, fermi_hubbard_2d from ffsim.operators.fermion_action import cre_a, cre_b, des_a, des_b @@ -150,6 +150,24 @@ def test_non_interacting_fermi_hubbard_1d_eigenvalue(): np.testing.assert_allclose(eigs_periodic[0], -4.000000000000) +def test_non_interacting_fermi_hubbard_2d_eigenvalue(): + """Test ground-state eigenvalue of the non-interacting two-dimensional Fermi-Hubbard + model Hamiltonian.""" + + # open boundary conditions + op = fermi_hubbard_2d(norb_x=2, norb_y=2, tunneling=1, interaction=0) + ham = ffsim.linear_operator(op, norb=4, nelec=(2, 2)) + eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) + np.testing.assert_allclose(eigs[0], -4.000000000000) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d(norb_x=3, norb_y=3, tunneling=1, interaction=0, periodic=True) + ham_periodic = ffsim.linear_operator(op_periodic, norb=9, nelec=(5, 4)) + eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) + print(eigs_periodic) + np.testing.assert_allclose(eigs_periodic[0], -15.000000000000) + + def test_fermi_hubbard_1d_with_interaction_eigenvalue(): """Test ground-state eigenvalue of the one-dimensional Fermi-Hubbard model Hamiltonian with onsite interaction.""" From c96f937a006057a112a361e2db466c35ba90c736 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Mon, 22 Apr 2024 09:54:52 -0700 Subject: [PATCH 02/12] add 2D Fermi-Hubbard model with tests --- python/ffsim/operators/fermi_hubbard.py | 76 ++++- tests/python/operators/fermi_hubbard_test.py | 294 ++++++++++++++++++- 2 files changed, 358 insertions(+), 12 deletions(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index abd64c08b..5cac0bc3c 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -134,27 +134,85 @@ def fermi_hubbard_2d( """ coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = {} + # initialize tunneling keys for x in range(norb_x): for y in range(norb_y): + # position in Cartesian coordinates x_right = (x + 1) % norb_x y_up = (y + 1) % norb_y + # position on C-style chain p = norb_y * x + y p_right = norb_y * x_right + y p_up = norb_y * x + y_up - print(f"(p, u, r) = ({p}, {p_up}, {p_right})") + if x != norb_x - 1 or periodic: + coeffs[(cre_a(p), des_a(p_right))] = 0 + coeffs[(cre_a(p_right), des_a(p))] = 0 + coeffs[(cre_b(p), des_b(p_right))] = 0 + coeffs[(cre_b(p_right), des_b(p))] = 0 if y != norb_y - 1 or periodic: - coeffs[(cre_a(p), des_a(p_up))] = -tunneling - coeffs[(cre_a(p_up), des_a(p))] = -tunneling - coeffs[(cre_b(p), des_b(p_up))] = -tunneling - coeffs[(cre_b(p_up), des_b(p))] = -tunneling + coeffs[(cre_a(p), des_a(p_up))] = 0 + coeffs[(cre_a(p_up), des_a(p))] = 0 + coeffs[(cre_b(p), des_b(p_up))] = 0 + coeffs[(cre_b(p_up), des_b(p))] = 0 + + # populate keys + for x in range(norb_x): + for y in range(norb_y): + # position in Cartesian coordinates + x_right = (x + 1) % norb_x + y_up = (y + 1) % norb_y + + # position on C-style chain + p = norb_y * x + y + p_right = norb_y * x_right + y + p_up = norb_y * x + y_up if x != norb_x - 1 or periodic: - coeffs[(cre_a(p), des_a(p_right))] = -tunneling - coeffs[(cre_a(p_right), des_a(p))] = -tunneling - coeffs[(cre_b(p), des_b(p_right))] = -tunneling - coeffs[(cre_b(p_right), des_b(p))] = -tunneling + coeffs[(cre_a(p), des_a(p_right))] -= tunneling + coeffs[(cre_a(p_right), des_a(p))] -= tunneling + coeffs[(cre_b(p), des_b(p_right))] -= tunneling + coeffs[(cre_b(p_right), des_b(p))] -= tunneling + if nearest_neighbor_interaction: + coeffs[(cre_a(p), des_a(p), cre_a(p_right), des_a(p_right))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_a(p), des_a(p), cre_b(p_right), des_b(p_right))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(p), des_b(p), cre_a(p_right), des_a(p_right))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(p), des_b(p), cre_b(p_right), des_b(p_right))] = ( + nearest_neighbor_interaction + ) + + if y != norb_y - 1 or periodic: + coeffs[(cre_a(p), des_a(p_up))] -= tunneling + coeffs[(cre_a(p_up), des_a(p))] -= tunneling + coeffs[(cre_b(p), des_b(p_up))] -= tunneling + coeffs[(cre_b(p_up), des_b(p))] -= tunneling + if nearest_neighbor_interaction: + coeffs[(cre_a(p), des_a(p), cre_a(p_up), des_a(p_up))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_a(p), des_a(p), cre_b(p_up), des_b(p_up))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(p), des_b(p), cre_a(p_up), des_a(p_up))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(p), des_b(p), cre_b(p_up), des_b(p_up))] = ( + nearest_neighbor_interaction + ) + + for p in range(norb_x * norb_y): + if interaction: + coeffs[(cre_a(p), des_a(p), cre_b(p), des_b(p))] = interaction + if chemical_potential: + coeffs[(cre_a(p), des_a(p))] = -chemical_potential + coeffs[(cre_b(p), des_b(p))] = -chemical_potential return FermionOperator(coeffs) diff --git a/tests/python/operators/fermi_hubbard_test.py b/tests/python/operators/fermi_hubbard_test.py index 4af34b2c8..e4457b6f7 100644 --- a/tests/python/operators/fermi_hubbard_test.py +++ b/tests/python/operators/fermi_hubbard_test.py @@ -133,6 +133,145 @@ def test_fermi_hubbard_1d(): ) +def test_fermi_hubbard_2d(): + """Test terms of the two-dimensional Fermi-Hubbard model Hamiltonian.""" + + # open boundary conditions + op = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + ) + np.testing.assert_equal( + dict(op), + { + (cre_a(0), des_a(2)): -1, + (cre_b(0), des_b(2)): -1, + (cre_a(2), des_a(0)): -1, + (cre_b(2), des_b(0)): -1, + (cre_a(0), des_a(1)): -1, + (cre_b(0), des_b(1)): -1, + (cre_a(1), des_a(0)): -1, + (cre_b(1), des_b(0)): -1, + (cre_a(1), des_a(3)): -1, + (cre_b(1), des_b(3)): -1, + (cre_a(3), des_a(1)): -1, + (cre_b(3), des_b(1)): -1, + (cre_a(2), des_a(3)): -1, + (cre_b(2), des_b(3)): -1, + (cre_a(3), des_a(2)): -1, + (cre_b(3), des_b(2)): -1, + (cre_a(0), des_a(0), cre_b(0), des_b(0)): 2, + (cre_a(1), des_a(1), cre_b(1), des_b(1)): 2, + (cre_a(2), des_a(2), cre_b(2), des_b(2)): 2, + (cre_a(3), des_a(3), cre_b(3), des_b(3)): 2, + (cre_a(0), des_a(0)): -3, + (cre_b(0), des_b(0)): -3, + (cre_a(1), des_a(1)): -3, + (cre_b(1), des_b(1)): -3, + (cre_a(2), des_a(2)): -3, + (cre_b(2), des_b(2)): -3, + (cre_a(3), des_a(3)): -3, + (cre_b(3), des_b(3)): -3, + (cre_a(0), des_a(0), cre_a(2), des_a(2)): 4, + (cre_a(0), des_a(0), cre_b(2), des_b(2)): 4, + (cre_b(0), des_b(0), cre_a(2), des_a(2)): 4, + (cre_b(0), des_b(0), cre_b(2), des_b(2)): 4, + (cre_a(0), des_a(0), cre_a(1), des_a(1)): 4, + (cre_a(0), des_a(0), cre_b(1), des_b(1)): 4, + (cre_b(0), des_b(0), cre_a(1), des_a(1)): 4, + (cre_b(0), des_b(0), cre_b(1), des_b(1)): 4, + (cre_a(1), des_a(1), cre_a(3), des_a(3)): 4, + (cre_a(1), des_a(1), cre_b(3), des_b(3)): 4, + (cre_b(1), des_b(1), cre_a(3), des_a(3)): 4, + (cre_b(1), des_b(1), cre_b(3), des_b(3)): 4, + (cre_a(2), des_a(2), cre_a(3), des_a(3)): 4, + (cre_a(2), des_a(2), cre_b(3), des_b(3)): 4, + (cre_b(2), des_b(2), cre_a(3), des_a(3)): 4, + (cre_b(2), des_b(2), cre_b(3), des_b(3)): 4, + }, + ) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + periodic=True, + ) + np.testing.assert_equal( + dict(op_periodic), + { + (cre_a(0), des_a(2)): -2, + (cre_b(0), des_b(2)): -2, + (cre_a(2), des_a(0)): -2, + (cre_b(2), des_b(0)): -2, + (cre_a(0), des_a(1)): -2, + (cre_b(0), des_b(1)): -2, + (cre_a(1), des_a(0)): -2, + (cre_b(1), des_b(0)): -2, + (cre_a(1), des_a(3)): -2, + (cre_b(1), des_b(3)): -2, + (cre_a(3), des_a(1)): -2, + (cre_b(3), des_b(1)): -2, + (cre_a(2), des_a(3)): -2, + (cre_b(2), des_b(3)): -2, + (cre_a(3), des_a(2)): -2, + (cre_b(3), des_b(2)): -2, + (cre_a(0), des_a(0), cre_b(0), des_b(0)): 2, + (cre_a(1), des_a(1), cre_b(1), des_b(1)): 2, + (cre_a(2), des_a(2), cre_b(2), des_b(2)): 2, + (cre_a(3), des_a(3), cre_b(3), des_b(3)): 2, + (cre_a(0), des_a(0)): -3, + (cre_b(0), des_b(0)): -3, + (cre_a(1), des_a(1)): -3, + (cre_b(1), des_b(1)): -3, + (cre_a(2), des_a(2)): -3, + (cre_b(2), des_b(2)): -3, + (cre_a(3), des_a(3)): -3, + (cre_b(3), des_b(3)): -3, + (cre_a(0), des_a(0), cre_a(2), des_a(2)): 4, + (cre_a(0), des_a(0), cre_b(2), des_b(2)): 4, + (cre_b(0), des_b(0), cre_a(2), des_a(2)): 4, + (cre_b(0), des_b(0), cre_b(2), des_b(2)): 4, + (cre_a(0), des_a(0), cre_a(1), des_a(1)): 4, + (cre_a(0), des_a(0), cre_b(1), des_b(1)): 4, + (cre_b(0), des_b(0), cre_a(1), des_a(1)): 4, + (cre_b(0), des_b(0), cre_b(1), des_b(1)): 4, + (cre_a(1), des_a(1), cre_a(3), des_a(3)): 4, + (cre_a(1), des_a(1), cre_b(3), des_b(3)): 4, + (cre_b(1), des_b(1), cre_a(3), des_a(3)): 4, + (cre_b(1), des_b(1), cre_b(3), des_b(3)): 4, + (cre_a(1), des_a(1), cre_a(0), des_a(0)): 4, + (cre_a(1), des_a(1), cre_b(0), des_b(0)): 4, + (cre_b(1), des_b(1), cre_a(0), des_a(0)): 4, + (cre_b(1), des_b(1), cre_b(0), des_b(0)): 4, + (cre_a(2), des_a(2), cre_a(0), des_a(0)): 4, + (cre_a(2), des_a(2), cre_b(0), des_b(0)): 4, + (cre_b(2), des_b(2), cre_a(0), des_a(0)): 4, + (cre_b(2), des_b(2), cre_b(0), des_b(0)): 4, + (cre_a(2), des_a(2), cre_a(3), des_a(3)): 4, + (cre_a(2), des_a(2), cre_b(3), des_b(3)): 4, + (cre_b(2), des_b(2), cre_a(3), des_a(3)): 4, + (cre_b(2), des_b(2), cre_b(3), des_b(3)): 4, + (cre_a(3), des_a(3), cre_a(1), des_a(1)): 4, + (cre_a(3), des_a(3), cre_b(1), des_b(1)): 4, + (cre_b(3), des_b(3), cre_a(1), des_a(1)): 4, + (cre_b(3), des_b(3), cre_b(1), des_b(1)): 4, + (cre_a(3), des_a(3), cre_a(2), des_a(2)): 4, + (cre_a(3), des_a(3), cre_b(2), des_b(2)): 4, + (cre_b(3), des_b(3), cre_a(2), des_a(2)): 4, + (cre_b(3), des_b(3), cre_b(2), des_b(2)): 4, + }, + ) + + def test_non_interacting_fermi_hubbard_1d_eigenvalue(): """Test ground-state eigenvalue of the non-interacting one-dimensional Fermi-Hubbard model Hamiltonian.""" @@ -161,11 +300,13 @@ def test_non_interacting_fermi_hubbard_2d_eigenvalue(): np.testing.assert_allclose(eigs[0], -4.000000000000) # periodic boundary conditions - op_periodic = fermi_hubbard_2d(norb_x=3, norb_y=3, tunneling=1, interaction=0, periodic=True) - ham_periodic = ffsim.linear_operator(op_periodic, norb=9, nelec=(5, 4)) + op_periodic = fermi_hubbard_2d( + norb_x=2, norb_y=2, tunneling=1, interaction=0, periodic=True + ) + ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) print(eigs_periodic) - np.testing.assert_allclose(eigs_periodic[0], -15.000000000000) + np.testing.assert_allclose(eigs_periodic[0], -8.000000000000) def test_fermi_hubbard_1d_with_interaction_eigenvalue(): @@ -185,6 +326,25 @@ def test_fermi_hubbard_1d_with_interaction_eigenvalue(): np.testing.assert_allclose(eigs_periodic[0], -2.828427124746) +def test_fermi_hubbard_2d_with_interaction_eigenvalue(): + """Test ground-state eigenvalue of the two-dimensional Fermi-Hubbard model + Hamiltonian with onsite interaction.""" + + # open boundary conditions + op = fermi_hubbard_2d(norb_x=2, norb_y=2, tunneling=1, interaction=2) + ham = ffsim.linear_operator(op, norb=4, nelec=(2, 2)) + eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) + np.testing.assert_allclose(eigs[0], -2.828427124746) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d( + norb_x=2, norb_y=2, tunneling=1, interaction=2, periodic=True + ) + ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) + np.testing.assert_allclose(eigs_periodic[0], -6.681695234497) + + def test_fermi_hubbard_1d_with_chemical_potential_eigenvalue(): """Test ground-state eigenvalue of the one-dimensional Fermi-Hubbard model Hamiltonian with onsite interaction and chemical potential.""" @@ -204,6 +364,32 @@ def test_fermi_hubbard_1d_with_chemical_potential_eigenvalue(): np.testing.assert_allclose(eigs_periodic[0], -14.828427124746) +def test_fermi_hubbard_2d_with_chemical_potential_eigenvalue(): + """Test ground-state eigenvalue of the two-dimensional Fermi-Hubbard model + Hamiltonian with onsite interaction and chemical potential.""" + + # open boundary conditions + op = fermi_hubbard_2d( + norb_x=2, norb_y=2, tunneling=1, interaction=2, chemical_potential=3 + ) + ham = ffsim.linear_operator(op, norb=4, nelec=(2, 2)) + eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) + np.testing.assert_allclose(eigs[0], -14.828427124746) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + periodic=True, + ) + ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) + np.testing.assert_allclose(eigs_periodic[0], -18.681695234497) + + def test_fermi_hubbard_1d_with_nearest_neighbor_interaction_eigenvalue(): """Test ground-state eigenvalue of the one-dimensional Fermi-Hubbard model Hamiltonian with onsite interaction, chemical potential, and nearest-neighbor @@ -235,6 +421,39 @@ def test_fermi_hubbard_1d_with_nearest_neighbor_interaction_eigenvalue(): np.testing.assert_allclose(eigs_periodic[0], -8.781962448006) +def test_fermi_hubbard_2d_with_nearest_neighbor_interaction_eigenvalue(): + """Test ground-state eigenvalue of the two-dimensional Fermi-Hubbard model + Hamiltonian with onsite interaction, chemical potential, and nearest-neighbor + interaction.""" + + # open boundary conditions + op = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + ) + ham = ffsim.linear_operator(op, norb=4, nelec=(2, 2)) + eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) + np.testing.assert_allclose(eigs[0], -8.781962448006) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + periodic=True, + ) + ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) + np.testing.assert_allclose(eigs_periodic[0], -9.428197577536) + + def test_fermi_hubbard_1d_with_unequal_filling_eigenvalue(): """Test ground-state eigenvalue of the one-dimensional Fermi-Hubbard model Hamiltonian with unequal filling.""" @@ -265,6 +484,38 @@ def test_fermi_hubbard_1d_with_unequal_filling_eigenvalue(): np.testing.assert_allclose(eigs_periodic[0], -0.828427124746) +def test_fermi_hubbard_2d_with_unequal_filling_eigenvalue(): + """Test ground-state eigenvalue of the two-dimensional Fermi-Hubbard model + Hamiltonian with unequal filling.""" + + # open boundary conditions + op = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + ) + ham = ffsim.linear_operator(op, norb=4, nelec=(1, 3)) + eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) + np.testing.assert_allclose(eigs[0], -0.828427124746) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d( + norb_x=2, + norb_y=2, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + periodic=True, + ) + ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(1, 3)) + eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) + np.testing.assert_allclose(eigs_periodic[0], 8.743352161722) + + def test_fermi_hubbard_1d_hermiticity(): """Test hermiticity of the one-dimensional Fermi-Hubbard model Hamiltonian.""" @@ -296,3 +547,40 @@ def test_fermi_hubbard_1d_hermiticity(): op_periodic, norb=n_orbitals, nelec=n_electrons ) np.testing.assert_allclose(ham_periodic @ np.eye(dim), ham_periodic.H @ np.eye(dim)) + + +def test_fermi_hubbard_2d_hermiticity(): + """Test hermiticity of the two-dimensional Fermi-Hubbard model Hamiltonian.""" + + n_orbitals_x = 2 + n_orbitals_y = 2 + n_orbitals = n_orbitals_x * n_orbitals_y + n_electrons = (3, 1) + dim = ffsim.dim(n_orbitals, n_electrons) + + # open boundary conditions + op = fermi_hubbard_2d( + norb_x=n_orbitals_x, + norb_y=n_orbitals_y, + tunneling=1.1, + interaction=1.2, + chemical_potential=1.3, + nearest_neighbor_interaction=1.4, + ) + ham = ffsim.linear_operator(op, norb=n_orbitals, nelec=n_electrons) + np.testing.assert_allclose(ham @ np.eye(dim), ham.H @ np.eye(dim)) + + # periodic boundary conditions + op_periodic = fermi_hubbard_2d( + norb_x=n_orbitals_x, + norb_y=n_orbitals_y, + tunneling=1.1, + interaction=1.2, + chemical_potential=1.3, + nearest_neighbor_interaction=1.4, + periodic=True, + ) + ham_periodic = ffsim.linear_operator( + op_periodic, norb=n_orbitals, nelec=n_electrons + ) + np.testing.assert_allclose(ham_periodic @ np.eye(dim), ham_periodic.H @ np.eye(dim)) From 9f99090f66e8f4202e6d08f90fc2636beb8e0ebb Mon Sep 17 00:00:00 2001 From: bartandrews Date: Mon, 22 Apr 2024 10:05:55 -0700 Subject: [PATCH 03/12] update init files --- python/ffsim/__init__.py | 1 + python/ffsim/operators/__init__.py | 2 +- tests/python/operators/__init__.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/python/ffsim/__init__.py b/python/ffsim/__init__.py index 26eb00789..8f6990471 100644 --- a/python/ffsim/__init__.py +++ b/python/ffsim/__init__.py @@ -42,6 +42,7 @@ des_a, des_b, fermi_hubbard_1d, + fermi_hubbard_2d, number_operator, ) from ffsim.protocols import ( diff --git a/python/ffsim/operators/__init__.py b/python/ffsim/operators/__init__.py index 06ac59099..29758742b 100644 --- a/python/ffsim/operators/__init__.py +++ b/python/ffsim/operators/__init__.py @@ -11,7 +11,7 @@ """Operators.""" from ffsim.operators.common_operators import number_operator -from ffsim.operators.fermi_hubbard import fermi_hubbard_1d +from ffsim.operators.fermi_hubbard import fermi_hubbard_1d, fermi_hubbard_2d from ffsim.operators.fermion_action import ( FermionAction, cre, diff --git a/tests/python/operators/__init__.py b/tests/python/operators/__init__.py index 8b593392d..5f2c9d9c1 100644 --- a/tests/python/operators/__init__.py +++ b/tests/python/operators/__init__.py @@ -1,4 +1,4 @@ -# (C) Copyright IBM 2023. +# (C) Copyright IBM 2024. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory From f8e1eae46d56f2e6d84f64dd1b03bef7432c5a90 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Mon, 22 Apr 2024 10:13:10 -0700 Subject: [PATCH 04/12] initialize tunneling keys for fermi_hubbard_1d --- python/ffsim/operators/fermi_hubbard.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 5cac0bc3c..26fc3c2b1 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -58,11 +58,19 @@ def fermi_hubbard_1d( """ coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = {} + # initialize tunneling keys + for p in range(norb - 1 + periodic): + coeffs[(cre_a(p), des_a((p + 1) % norb))] = 0 + coeffs[(cre_b(p), des_b((p + 1) % norb))] = 0 + coeffs[(cre_a((p + 1) % norb), des_a(p))] = 0 + coeffs[(cre_b((p + 1) % norb), des_b(p))] = 0 + + # populate keys for p in range(norb - 1 + periodic): - coeffs[(cre_a(p), des_a((p + 1) % norb))] = -tunneling - coeffs[(cre_b(p), des_b((p + 1) % norb))] = -tunneling - coeffs[(cre_a((p + 1) % norb), des_a(p))] = -tunneling - coeffs[(cre_b((p + 1) % norb), des_b(p))] = -tunneling + coeffs[(cre_a(p), des_a((p + 1) % norb))] -= tunneling + coeffs[(cre_b(p), des_b((p + 1) % norb))] -= tunneling + coeffs[(cre_a((p + 1) % norb), des_a(p))] -= tunneling + coeffs[(cre_b((p + 1) % norb), des_b(p))] -= tunneling if nearest_neighbor_interaction: coeffs[ (cre_a(p), des_a(p), cre_a((p + 1) % norb), des_a((p + 1) % norb)) From 90ea49539d88daf3e506a76fd9914656bf8b6cca Mon Sep 17 00:00:00 2001 From: bartandrews Date: Wed, 24 Apr 2024 08:39:54 -0700 Subject: [PATCH 05/12] revert changes to fermi_hubbard_1d --- python/ffsim/operators/fermi_hubbard.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 26fc3c2b1..f1c23bb4d 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -58,19 +58,12 @@ def fermi_hubbard_1d( """ coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = {} - # initialize tunneling keys - for p in range(norb - 1 + periodic): - coeffs[(cre_a(p), des_a((p + 1) % norb))] = 0 - coeffs[(cre_b(p), des_b((p + 1) % norb))] = 0 - coeffs[(cre_a((p + 1) % norb), des_a(p))] = 0 - coeffs[(cre_b((p + 1) % norb), des_b(p))] = 0 - # populate keys for p in range(norb - 1 + periodic): - coeffs[(cre_a(p), des_a((p + 1) % norb))] -= tunneling - coeffs[(cre_b(p), des_b((p + 1) % norb))] -= tunneling - coeffs[(cre_a((p + 1) % norb), des_a(p))] -= tunneling - coeffs[(cre_b((p + 1) % norb), des_b(p))] -= tunneling + coeffs[(cre_a(p), des_a((p + 1) % norb))] = -tunneling + coeffs[(cre_b(p), des_b((p + 1) % norb))] = -tunneling + coeffs[(cre_a((p + 1) % norb), des_a(p))] = -tunneling + coeffs[(cre_b((p + 1) % norb), des_b(p))] = -tunneling if nearest_neighbor_interaction: coeffs[ (cre_a(p), des_a(p), cre_a((p + 1) % norb), des_a((p + 1) % norb)) From 89325f0d85fb1643ae3b71298ed1946419338797 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Wed, 24 Apr 2024 08:41:24 -0700 Subject: [PATCH 06/12] revert more changes to fermi_hubbard_1d --- python/ffsim/operators/fermi_hubbard.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index f1c23bb4d..5cac0bc3c 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -58,7 +58,6 @@ def fermi_hubbard_1d( """ coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = {} - # populate keys for p in range(norb - 1 + periodic): coeffs[(cre_a(p), des_a((p + 1) % norb))] = -tunneling coeffs[(cre_b(p), des_b((p + 1) % norb))] = -tunneling From 379e3217eb58d4c94f86d56399dcfe79ff54ed88 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Wed, 24 Apr 2024 09:17:29 -0700 Subject: [PATCH 07/12] add non-edge-case test --- tests/python/operators/fermi_hubbard_test.py | 219 +++++++++++++++++-- 1 file changed, 203 insertions(+), 16 deletions(-) diff --git a/tests/python/operators/fermi_hubbard_test.py b/tests/python/operators/fermi_hubbard_test.py index e4457b6f7..42e3a8d3b 100644 --- a/tests/python/operators/fermi_hubbard_test.py +++ b/tests/python/operators/fermi_hubbard_test.py @@ -197,6 +197,193 @@ def test_fermi_hubbard_2d(): # periodic boundary conditions op_periodic = fermi_hubbard_2d( + norb_x=3, + norb_y=3, + tunneling=1, + interaction=2, + chemical_potential=3, + nearest_neighbor_interaction=4, + periodic=True, + ) + np.testing.assert_equal( + dict(op_periodic), + { + (cre_a(0), des_a(3)): -1, + (cre_b(0), des_b(3)): -1, + (cre_a(3), des_a(0)): -1, + (cre_b(3), des_b(0)): -1, + (cre_a(0), des_a(1)): -1, + (cre_b(0), des_b(1)): -1, + (cre_a(1), des_a(0)): -1, + (cre_b(1), des_b(0)): -1, + (cre_a(1), des_a(4)): -1, + (cre_b(1), des_b(4)): -1, + (cre_a(4), des_a(1)): -1, + (cre_b(4), des_b(1)): -1, + (cre_a(1), des_a(2)): -1, + (cre_b(1), des_b(2)): -1, + (cre_a(2), des_a(1)): -1, + (cre_b(2), des_b(1)): -1, + (cre_a(2), des_a(5)): -1, + (cre_b(2), des_b(5)): -1, + (cre_a(5), des_a(2)): -1, + (cre_b(5), des_b(2)): -1, + (cre_a(2), des_a(0)): -1, + (cre_b(2), des_b(0)): -1, + (cre_a(0), des_a(2)): -1, + (cre_b(0), des_b(2)): -1, + (cre_a(3), des_a(6)): -1, + (cre_b(3), des_b(6)): -1, + (cre_a(6), des_a(3)): -1, + (cre_b(6), des_b(3)): -1, + (cre_a(3), des_a(4)): -1, + (cre_b(3), des_b(4)): -1, + (cre_a(4), des_a(3)): -1, + (cre_b(4), des_b(3)): -1, + (cre_a(4), des_a(7)): -1, + (cre_b(4), des_b(7)): -1, + (cre_a(7), des_a(4)): -1, + (cre_b(7), des_b(4)): -1, + (cre_a(4), des_a(5)): -1, + (cre_b(4), des_b(5)): -1, + (cre_a(5), des_a(4)): -1, + (cre_b(5), des_b(4)): -1, + (cre_a(5), des_a(8)): -1, + (cre_b(5), des_b(8)): -1, + (cre_a(8), des_a(5)): -1, + (cre_b(8), des_b(5)): -1, + (cre_a(5), des_a(3)): -1, + (cre_b(5), des_b(3)): -1, + (cre_a(3), des_a(5)): -1, + (cre_b(3), des_b(5)): -1, + (cre_a(6), des_a(0)): -1, + (cre_b(6), des_b(0)): -1, + (cre_a(0), des_a(6)): -1, + (cre_b(0), des_b(6)): -1, + (cre_a(6), des_a(7)): -1, + (cre_b(6), des_b(7)): -1, + (cre_a(7), des_a(6)): -1, + (cre_b(7), des_b(6)): -1, + (cre_a(7), des_a(1)): -1, + (cre_b(7), des_b(1)): -1, + (cre_a(1), des_a(7)): -1, + (cre_b(1), des_b(7)): -1, + (cre_a(7), des_a(8)): -1, + (cre_b(7), des_b(8)): -1, + (cre_a(8), des_a(7)): -1, + (cre_b(8), des_b(7)): -1, + (cre_a(8), des_a(2)): -1, + (cre_b(8), des_b(2)): -1, + (cre_a(2), des_a(8)): -1, + (cre_b(2), des_b(8)): -1, + (cre_a(8), des_a(6)): -1, + (cre_b(8), des_b(6)): -1, + (cre_a(6), des_a(8)): -1, + (cre_b(6), des_b(8)): -1, + (cre_a(0), des_a(0), cre_b(0), des_b(0)): 2, + (cre_a(1), des_a(1), cre_b(1), des_b(1)): 2, + (cre_a(2), des_a(2), cre_b(2), des_b(2)): 2, + (cre_a(3), des_a(3), cre_b(3), des_b(3)): 2, + (cre_a(4), des_a(4), cre_b(4), des_b(4)): 2, + (cre_a(5), des_a(5), cre_b(5), des_b(5)): 2, + (cre_a(6), des_a(6), cre_b(6), des_b(6)): 2, + (cre_a(7), des_a(7), cre_b(7), des_b(7)): 2, + (cre_a(8), des_a(8), cre_b(8), des_b(8)): 2, + (cre_a(0), des_a(0)): -3, + (cre_b(0), des_b(0)): -3, + (cre_a(1), des_a(1)): -3, + (cre_b(1), des_b(1)): -3, + (cre_a(2), des_a(2)): -3, + (cre_b(2), des_b(2)): -3, + (cre_a(3), des_a(3)): -3, + (cre_b(3), des_b(3)): -3, + (cre_a(4), des_a(4)): -3, + (cre_b(4), des_b(4)): -3, + (cre_a(5), des_a(5)): -3, + (cre_b(5), des_b(5)): -3, + (cre_a(6), des_a(6)): -3, + (cre_b(6), des_b(6)): -3, + (cre_a(7), des_a(7)): -3, + (cre_b(7), des_b(7)): -3, + (cre_a(8), des_a(8)): -3, + (cre_b(8), des_b(8)): -3, + (cre_a(0), des_a(0), cre_a(3), des_a(3)): 4, + (cre_a(0), des_a(0), cre_b(3), des_b(3)): 4, + (cre_b(0), des_b(0), cre_a(3), des_a(3)): 4, + (cre_b(0), des_b(0), cre_b(3), des_b(3)): 4, + (cre_a(0), des_a(0), cre_a(1), des_a(1)): 4, + (cre_a(0), des_a(0), cre_b(1), des_b(1)): 4, + (cre_b(0), des_b(0), cre_a(1), des_a(1)): 4, + (cre_b(0), des_b(0), cre_b(1), des_b(1)): 4, + (cre_a(1), des_a(1), cre_a(4), des_a(4)): 4, + (cre_a(1), des_a(1), cre_b(4), des_b(4)): 4, + (cre_b(1), des_b(1), cre_a(4), des_a(4)): 4, + (cre_b(1), des_b(1), cre_b(4), des_b(4)): 4, + (cre_a(1), des_a(1), cre_a(2), des_a(2)): 4, + (cre_a(1), des_a(1), cre_b(2), des_b(2)): 4, + (cre_b(1), des_b(1), cre_a(2), des_a(2)): 4, + (cre_b(1), des_b(1), cre_b(2), des_b(2)): 4, + (cre_a(2), des_a(2), cre_a(5), des_a(5)): 4, + (cre_a(2), des_a(2), cre_b(5), des_b(5)): 4, + (cre_b(2), des_b(2), cre_a(5), des_a(5)): 4, + (cre_b(2), des_b(2), cre_b(5), des_b(5)): 4, + (cre_a(2), des_a(2), cre_a(0), des_a(0)): 4, + (cre_a(2), des_a(2), cre_b(0), des_b(0)): 4, + (cre_b(2), des_b(2), cre_a(0), des_a(0)): 4, + (cre_b(2), des_b(2), cre_b(0), des_b(0)): 4, + (cre_a(3), des_a(3), cre_a(6), des_a(6)): 4, + (cre_a(3), des_a(3), cre_b(6), des_b(6)): 4, + (cre_b(3), des_b(3), cre_a(6), des_a(6)): 4, + (cre_b(3), des_b(3), cre_b(6), des_b(6)): 4, + (cre_a(3), des_a(3), cre_a(4), des_a(4)): 4, + (cre_a(3), des_a(3), cre_b(4), des_b(4)): 4, + (cre_b(3), des_b(3), cre_a(4), des_a(4)): 4, + (cre_b(3), des_b(3), cre_b(4), des_b(4)): 4, + (cre_a(4), des_a(4), cre_a(7), des_a(7)): 4, + (cre_a(4), des_a(4), cre_b(7), des_b(7)): 4, + (cre_b(4), des_b(4), cre_a(7), des_a(7)): 4, + (cre_b(4), des_b(4), cre_b(7), des_b(7)): 4, + (cre_a(4), des_a(4), cre_a(5), des_a(5)): 4, + (cre_a(4), des_a(4), cre_b(5), des_b(5)): 4, + (cre_b(4), des_b(4), cre_a(5), des_a(5)): 4, + (cre_b(4), des_b(4), cre_b(5), des_b(5)): 4, + (cre_a(5), des_a(5), cre_a(8), des_a(8)): 4, + (cre_a(5), des_a(5), cre_b(8), des_b(8)): 4, + (cre_b(5), des_b(5), cre_a(8), des_a(8)): 4, + (cre_b(5), des_b(5), cre_b(8), des_b(8)): 4, + (cre_a(5), des_a(5), cre_a(3), des_a(3)): 4, + (cre_a(5), des_a(5), cre_b(3), des_b(3)): 4, + (cre_b(5), des_b(5), cre_a(3), des_a(3)): 4, + (cre_b(5), des_b(5), cre_b(3), des_b(3)): 4, + (cre_a(6), des_a(6), cre_a(0), des_a(0)): 4, + (cre_a(6), des_a(6), cre_b(0), des_b(0)): 4, + (cre_b(6), des_b(6), cre_a(0), des_a(0)): 4, + (cre_b(6), des_b(6), cre_b(0), des_b(0)): 4, + (cre_a(6), des_a(6), cre_a(7), des_a(7)): 4, + (cre_a(6), des_a(6), cre_b(7), des_b(7)): 4, + (cre_b(6), des_b(6), cre_a(7), des_a(7)): 4, + (cre_b(6), des_b(6), cre_b(7), des_b(7)): 4, + (cre_a(7), des_a(7), cre_a(1), des_a(1)): 4, + (cre_a(7), des_a(7), cre_b(1), des_b(1)): 4, + (cre_b(7), des_b(7), cre_a(1), des_a(1)): 4, + (cre_b(7), des_b(7), cre_b(1), des_b(1)): 4, + (cre_a(7), des_a(7), cre_a(8), des_a(8)): 4, + (cre_a(7), des_a(7), cre_b(8), des_b(8)): 4, + (cre_b(7), des_b(7), cre_a(8), des_a(8)): 4, + (cre_b(7), des_b(7), cre_b(8), des_b(8)): 4, + (cre_a(8), des_a(8), cre_a(2), des_a(2)): 4, + (cre_a(8), des_a(8), cre_b(2), des_b(2)): 4, + (cre_b(8), des_b(8), cre_a(2), des_a(2)): 4, + (cre_b(8), des_b(8), cre_b(2), des_b(2)): 4, + (cre_a(8), des_a(8), cre_a(6), des_a(6)): 4, + (cre_a(8), des_a(8), cre_b(6), des_b(6)): 4, + (cre_b(8), des_b(8), cre_a(6), des_a(6)): 4, + (cre_b(8), des_b(8), cre_b(6), des_b(6)): 4, + }, + ) + + # periodic boundary conditions (edge case) + op_periodic_edge = fermi_hubbard_2d( norb_x=2, norb_y=2, tunneling=1, @@ -206,7 +393,7 @@ def test_fermi_hubbard_2d(): periodic=True, ) np.testing.assert_equal( - dict(op_periodic), + dict(op_periodic_edge), { (cre_a(0), des_a(2)): -2, (cre_b(0), des_b(2)): -2, @@ -299,11 +486,11 @@ def test_non_interacting_fermi_hubbard_2d_eigenvalue(): eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) np.testing.assert_allclose(eigs[0], -4.000000000000) - # periodic boundary conditions - op_periodic = fermi_hubbard_2d( + # periodic boundary conditions (edge case) + op_periodic_edge = fermi_hubbard_2d( norb_x=2, norb_y=2, tunneling=1, interaction=0, periodic=True ) - ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + ham_periodic = ffsim.linear_operator(op_periodic_edge, norb=4, nelec=(2, 2)) eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) print(eigs_periodic) np.testing.assert_allclose(eigs_periodic[0], -8.000000000000) @@ -336,11 +523,11 @@ def test_fermi_hubbard_2d_with_interaction_eigenvalue(): eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) np.testing.assert_allclose(eigs[0], -2.828427124746) - # periodic boundary conditions - op_periodic = fermi_hubbard_2d( + # periodic boundary conditions (edge case) + op_periodic_edge = fermi_hubbard_2d( norb_x=2, norb_y=2, tunneling=1, interaction=2, periodic=True ) - ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + ham_periodic = ffsim.linear_operator(op_periodic_edge, norb=4, nelec=(2, 2)) eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) np.testing.assert_allclose(eigs_periodic[0], -6.681695234497) @@ -376,8 +563,8 @@ def test_fermi_hubbard_2d_with_chemical_potential_eigenvalue(): eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) np.testing.assert_allclose(eigs[0], -14.828427124746) - # periodic boundary conditions - op_periodic = fermi_hubbard_2d( + # periodic boundary conditions (edge case) + op_periodic_edge = fermi_hubbard_2d( norb_x=2, norb_y=2, tunneling=1, @@ -385,7 +572,7 @@ def test_fermi_hubbard_2d_with_chemical_potential_eigenvalue(): chemical_potential=3, periodic=True, ) - ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + ham_periodic = ffsim.linear_operator(op_periodic_edge, norb=4, nelec=(2, 2)) eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) np.testing.assert_allclose(eigs_periodic[0], -18.681695234497) @@ -439,8 +626,8 @@ def test_fermi_hubbard_2d_with_nearest_neighbor_interaction_eigenvalue(): eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) np.testing.assert_allclose(eigs[0], -8.781962448006) - # periodic boundary conditions - op_periodic = fermi_hubbard_2d( + # periodic boundary conditions (edge case) + op_periodic_edge = fermi_hubbard_2d( norb_x=2, norb_y=2, tunneling=1, @@ -449,7 +636,7 @@ def test_fermi_hubbard_2d_with_nearest_neighbor_interaction_eigenvalue(): nearest_neighbor_interaction=4, periodic=True, ) - ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(2, 2)) + ham_periodic = ffsim.linear_operator(op_periodic_edge, norb=4, nelec=(2, 2)) eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) np.testing.assert_allclose(eigs_periodic[0], -9.428197577536) @@ -501,8 +688,8 @@ def test_fermi_hubbard_2d_with_unequal_filling_eigenvalue(): eigs, _ = scipy.sparse.linalg.eigsh(ham, which="SA", k=1) np.testing.assert_allclose(eigs[0], -0.828427124746) - # periodic boundary conditions - op_periodic = fermi_hubbard_2d( + # periodic boundary conditions (edge case) + op_periodic_edge = fermi_hubbard_2d( norb_x=2, norb_y=2, tunneling=1, @@ -511,7 +698,7 @@ def test_fermi_hubbard_2d_with_unequal_filling_eigenvalue(): nearest_neighbor_interaction=4, periodic=True, ) - ham_periodic = ffsim.linear_operator(op_periodic, norb=4, nelec=(1, 3)) + ham_periodic = ffsim.linear_operator(op_periodic_edge, norb=4, nelec=(1, 3)) eigs_periodic, _ = scipy.sparse.linalg.eigsh(ham_periodic, which="SA", k=1) np.testing.assert_allclose(eigs_periodic[0], 8.743352161722) From 7c2a296eb067ae62ddd79265ec2bb6f37ba4b5ee Mon Sep 17 00:00:00 2001 From: bartandrews Date: Wed, 24 Apr 2024 10:00:13 -0700 Subject: [PATCH 08/12] add defaultdict --- python/ffsim/operators/fermi_hubbard.py | 29 +++---------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 5cac0bc3c..f60a9eac4 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -10,6 +10,8 @@ """Fermi-Hubbard model Hamiltonian.""" +from collections import defaultdict + from ffsim._lib import FermionOperator from ffsim.operators.fermion_action import cre_a, cre_b, des_a, des_b @@ -132,33 +134,8 @@ def fermi_hubbard_2d( .. _The Hubbard Model: https://doi.org/10.1146/annurev-conmatphys-031620-102024 """ - coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = {} - - # initialize tunneling keys - for x in range(norb_x): - for y in range(norb_y): - # position in Cartesian coordinates - x_right = (x + 1) % norb_x - y_up = (y + 1) % norb_y - - # position on C-style chain - p = norb_y * x + y - p_right = norb_y * x_right + y - p_up = norb_y * x + y_up - - if x != norb_x - 1 or periodic: - coeffs[(cre_a(p), des_a(p_right))] = 0 - coeffs[(cre_a(p_right), des_a(p))] = 0 - coeffs[(cre_b(p), des_b(p_right))] = 0 - coeffs[(cre_b(p_right), des_b(p))] = 0 - - if y != norb_y - 1 or periodic: - coeffs[(cre_a(p), des_a(p_up))] = 0 - coeffs[(cre_a(p_up), des_a(p))] = 0 - coeffs[(cre_b(p), des_b(p_up))] = 0 - coeffs[(cre_b(p_up), des_b(p))] = 0 + coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = defaultdict(lambda: 0) - # populate keys for x in range(norb_x): for y in range(norb_y): # position in Cartesian coordinates From 0e2b142398a016fcb5a2de3ec0efdfe495f84337 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Wed, 24 Apr 2024 11:05:41 -0700 Subject: [PATCH 09/12] add defaultdict(float) --- python/ffsim/operators/fermi_hubbard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index f60a9eac4..87fb1923d 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -134,7 +134,7 @@ def fermi_hubbard_2d( .. _The Hubbard Model: https://doi.org/10.1146/annurev-conmatphys-031620-102024 """ - coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = defaultdict(lambda: 0) + coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = defaultdict(float) for x in range(norb_x): for y in range(norb_y): From 54b0514f8d8e2c597001ecaea7b646c590bf3a13 Mon Sep 17 00:00:00 2001 From: bartandrews Date: Tue, 21 May 2024 16:48:20 -0700 Subject: [PATCH 10/12] implement PR corrections --- python/ffsim/operators/fermi_hubbard.py | 109 ++++++++++++------------ 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 87fb1923d..45548b574 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -134,62 +134,61 @@ def fermi_hubbard_2d( .. _The Hubbard Model: https://doi.org/10.1146/annurev-conmatphys-031620-102024 """ - coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = defaultdict(float) - - for x in range(norb_x): - for y in range(norb_y): - # position in Cartesian coordinates - x_right = (x + 1) % norb_x - y_up = (y + 1) % norb_y - - # position on C-style chain - p = norb_y * x + y - p_right = norb_y * x_right + y - p_up = norb_y * x + y_up - - if x != norb_x - 1 or periodic: - coeffs[(cre_a(p), des_a(p_right))] -= tunneling - coeffs[(cre_a(p_right), des_a(p))] -= tunneling - coeffs[(cre_b(p), des_b(p_right))] -= tunneling - coeffs[(cre_b(p_right), des_b(p))] -= tunneling - if nearest_neighbor_interaction: - coeffs[(cre_a(p), des_a(p), cre_a(p_right), des_a(p_right))] = ( - nearest_neighbor_interaction - ) - coeffs[(cre_a(p), des_a(p), cre_b(p_right), des_b(p_right))] = ( - nearest_neighbor_interaction - ) - coeffs[(cre_b(p), des_b(p), cre_a(p_right), des_a(p_right))] = ( - nearest_neighbor_interaction - ) - coeffs[(cre_b(p), des_b(p), cre_b(p_right), des_b(p_right))] = ( - nearest_neighbor_interaction - ) - - if y != norb_y - 1 or periodic: - coeffs[(cre_a(p), des_a(p_up))] -= tunneling - coeffs[(cre_a(p_up), des_a(p))] -= tunneling - coeffs[(cre_b(p), des_b(p_up))] -= tunneling - coeffs[(cre_b(p_up), des_b(p))] -= tunneling - if nearest_neighbor_interaction: - coeffs[(cre_a(p), des_a(p), cre_a(p_up), des_a(p_up))] = ( - nearest_neighbor_interaction - ) - coeffs[(cre_a(p), des_a(p), cre_b(p_up), des_b(p_up))] = ( - nearest_neighbor_interaction - ) - coeffs[(cre_b(p), des_b(p), cre_a(p_up), des_a(p_up))] = ( - nearest_neighbor_interaction - ) - coeffs[(cre_b(p), des_b(p), cre_b(p_up), des_b(p_up))] = ( - nearest_neighbor_interaction - ) - - for p in range(norb_x * norb_y): + coeffs: dict[tuple[tuple[bool, bool, int], ...], float] = defaultdict(float) + + for orb in range(norb_x * norb_y): + # position in Cartesian coordinates + x = orb // norb_y + y = orb % norb_y + x_right = (x + 1) % norb_x + y_up = (y + 1) % norb_y + + # position on C-style chain + orb_right = norb_y * x_right + y + orb_up = norb_y * x + y_up + + if x != norb_x - 1 or periodic: + coeffs[(cre_a(orb), des_a(orb_right))] -= tunneling + coeffs[(cre_a(orb_right), des_a(orb))] -= tunneling + coeffs[(cre_b(orb), des_b(orb_right))] -= tunneling + coeffs[(cre_b(orb_right), des_b(orb))] -= tunneling + if nearest_neighbor_interaction: + coeffs[(cre_a(orb), des_a(orb), cre_a(orb_right), des_a(orb_right))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_a(orb), des_a(orb), cre_b(orb_right), des_b(orb_right))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(orb), des_b(orb), cre_a(orb_right), des_a(orb_right))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(orb), des_b(orb), cre_b(orb_right), des_b(orb_right))] = ( + nearest_neighbor_interaction + ) + + if y != norb_y - 1 or periodic: + coeffs[(cre_a(orb), des_a(orb_up))] -= tunneling + coeffs[(cre_a(orb_up), des_a(orb))] -= tunneling + coeffs[(cre_b(orb), des_b(orb_up))] -= tunneling + coeffs[(cre_b(orb_up), des_b(orb))] -= tunneling + if nearest_neighbor_interaction: + coeffs[(cre_a(orb), des_a(orb), cre_a(orb_up), des_a(orb_up))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_a(orb), des_a(orb), cre_b(orb_up), des_b(orb_up))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(orb), des_b(orb), cre_a(orb_up), des_a(orb_up))] = ( + nearest_neighbor_interaction + ) + coeffs[(cre_b(orb), des_b(orb), cre_b(orb_up), des_b(orb_up))] = ( + nearest_neighbor_interaction + ) + if interaction: - coeffs[(cre_a(p), des_a(p), cre_b(p), des_b(p))] = interaction + coeffs[(cre_a(orb), des_a(orb), cre_b(orb), des_b(orb))] = interaction if chemical_potential: - coeffs[(cre_a(p), des_a(p))] = -chemical_potential - coeffs[(cre_b(p), des_b(p))] = -chemical_potential + coeffs[(cre_a(orb), des_a(orb))] = -chemical_potential + coeffs[(cre_b(orb), des_b(orb))] = -chemical_potential return FermionOperator(coeffs) From 93412024e740c692fa6917356457e9a096f2920b Mon Sep 17 00:00:00 2001 From: bartandrews Date: Tue, 21 May 2024 16:56:54 -0700 Subject: [PATCH 11/12] revert type change --- python/ffsim/operators/fermi_hubbard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 45548b574..3866491ab 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -134,7 +134,7 @@ def fermi_hubbard_2d( .. _The Hubbard Model: https://doi.org/10.1146/annurev-conmatphys-031620-102024 """ - coeffs: dict[tuple[tuple[bool, bool, int], ...], float] = defaultdict(float) + coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = defaultdict(float) for orb in range(norb_x * norb_y): # position in Cartesian coordinates From 90d38d55f7b889799e02c88dba67d1c22618d99e Mon Sep 17 00:00:00 2001 From: bartandrews Date: Tue, 21 May 2024 21:11:53 -0700 Subject: [PATCH 12/12] implement second PR corrections --- python/ffsim/operators/fermi_hubbard.py | 13 +- tests/python/operators/__init__.py | 2 +- tests/python/operators/fermi_hubbard_test.py | 296 +++++++++---------- 3 files changed, 155 insertions(+), 156 deletions(-) diff --git a/python/ffsim/operators/fermi_hubbard.py b/python/ffsim/operators/fermi_hubbard.py index 757412a70..02e99b647 100644 --- a/python/ffsim/operators/fermi_hubbard.py +++ b/python/ffsim/operators/fermi_hubbard.py @@ -137,15 +137,14 @@ def fermi_hubbard_2d( coeffs: dict[tuple[tuple[bool, bool, int], ...], complex] = defaultdict(float) for orb in range(norb_x * norb_y): - # position in Cartesian coordinates - x = orb // norb_y - y = orb % norb_y + # map from row-major ordering to Cartesian coordinates + y, x = divmod(orb, norb_x) + + # get the coordinates/orbitals to the right and up x_right = (x + 1) % norb_x y_up = (y + 1) % norb_y - - # position on C-style chain - orb_right = norb_y * x_right + y - orb_up = norb_y * x + y_up + orb_right = x_right + norb_x * y + orb_up = x + norb_x * y_up if x != norb_x - 1 or periodic: coeffs[(cre_a(orb), des_a(orb_right))] -= tunneling diff --git a/tests/python/operators/__init__.py b/tests/python/operators/__init__.py index 5f2c9d9c1..8b593392d 100644 --- a/tests/python/operators/__init__.py +++ b/tests/python/operators/__init__.py @@ -1,4 +1,4 @@ -# (C) Copyright IBM 2024. +# (C) Copyright IBM 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/tests/python/operators/fermi_hubbard_test.py b/tests/python/operators/fermi_hubbard_test.py index 727528a0d..69a74f6fd 100644 --- a/tests/python/operators/fermi_hubbard_test.py +++ b/tests/python/operators/fermi_hubbard_test.py @@ -181,22 +181,22 @@ def test_fermi_hubbard_2d(): np.testing.assert_equal( dict(op), { - (cre_a(0), des_a(2)): -1, - (cre_b(0), des_b(2)): -1, - (cre_a(2), des_a(0)): -1, - (cre_b(2), des_b(0)): -1, (cre_a(0), des_a(1)): -1, (cre_b(0), des_b(1)): -1, (cre_a(1), des_a(0)): -1, (cre_b(1), des_b(0)): -1, - (cre_a(1), des_a(3)): -1, - (cre_b(1), des_b(3)): -1, - (cre_a(3), des_a(1)): -1, - (cre_b(3), des_b(1)): -1, + (cre_a(0), des_a(2)): -1, + (cre_b(0), des_b(2)): -1, + (cre_a(2), des_a(0)): -1, + (cre_b(2), des_b(0)): -1, (cre_a(2), des_a(3)): -1, (cre_b(2), des_b(3)): -1, (cre_a(3), des_a(2)): -1, (cre_b(3), des_b(2)): -1, + (cre_a(1), des_a(3)): -1, + (cre_b(1), des_b(3)): -1, + (cre_a(3), des_a(1)): -1, + (cre_b(3), des_b(1)): -1, (cre_a(0), des_a(0), cre_b(0), des_b(0)): 2, (cre_a(1), des_a(1), cre_b(1), des_b(1)): 2, (cre_a(2), des_a(2), cre_b(2), des_b(2)): 2, @@ -209,22 +209,22 @@ def test_fermi_hubbard_2d(): (cre_b(2), des_b(2)): -3, (cre_a(3), des_a(3)): -3, (cre_b(3), des_b(3)): -3, - (cre_a(0), des_a(0), cre_a(2), des_a(2)): 4, - (cre_a(0), des_a(0), cre_b(2), des_b(2)): 4, - (cre_b(0), des_b(0), cre_a(2), des_a(2)): 4, - (cre_b(0), des_b(0), cre_b(2), des_b(2)): 4, (cre_a(0), des_a(0), cre_a(1), des_a(1)): 4, (cre_a(0), des_a(0), cre_b(1), des_b(1)): 4, (cre_b(0), des_b(0), cre_a(1), des_a(1)): 4, (cre_b(0), des_b(0), cre_b(1), des_b(1)): 4, - (cre_a(1), des_a(1), cre_a(3), des_a(3)): 4, - (cre_a(1), des_a(1), cre_b(3), des_b(3)): 4, - (cre_b(1), des_b(1), cre_a(3), des_a(3)): 4, - (cre_b(1), des_b(1), cre_b(3), des_b(3)): 4, + (cre_a(0), des_a(0), cre_a(2), des_a(2)): 4, + (cre_a(0), des_a(0), cre_b(2), des_b(2)): 4, + (cre_b(0), des_b(0), cre_a(2), des_a(2)): 4, + (cre_b(0), des_b(0), cre_b(2), des_b(2)): 4, (cre_a(2), des_a(2), cre_a(3), des_a(3)): 4, (cre_a(2), des_a(2), cre_b(3), des_b(3)): 4, (cre_b(2), des_b(2), cre_a(3), des_a(3)): 4, (cre_b(2), des_b(2), cre_b(3), des_b(3)): 4, + (cre_a(1), des_a(1), cre_a(3), des_a(3)): 4, + (cre_a(1), des_a(1), cre_b(3), des_b(3)): 4, + (cre_b(1), des_b(1), cre_a(3), des_a(3)): 4, + (cre_b(1), des_b(1), cre_b(3), des_b(3)): 4, }, ) @@ -241,78 +241,78 @@ def test_fermi_hubbard_2d(): np.testing.assert_equal( dict(op_periodic), { - (cre_a(0), des_a(3)): -1, - (cre_b(0), des_b(3)): -1, - (cre_a(3), des_a(0)): -1, - (cre_b(3), des_b(0)): -1, (cre_a(0), des_a(1)): -1, (cre_b(0), des_b(1)): -1, (cre_a(1), des_a(0)): -1, (cre_b(1), des_b(0)): -1, - (cre_a(1), des_a(4)): -1, - (cre_b(1), des_b(4)): -1, - (cre_a(4), des_a(1)): -1, - (cre_b(4), des_b(1)): -1, - (cre_a(1), des_a(2)): -1, - (cre_b(1), des_b(2)): -1, - (cre_a(2), des_a(1)): -1, - (cre_b(2), des_b(1)): -1, - (cre_a(2), des_a(5)): -1, - (cre_b(2), des_b(5)): -1, - (cre_a(5), des_a(2)): -1, - (cre_b(5), des_b(2)): -1, - (cre_a(2), des_a(0)): -1, - (cre_b(2), des_b(0)): -1, - (cre_a(0), des_a(2)): -1, - (cre_b(0), des_b(2)): -1, - (cre_a(3), des_a(6)): -1, - (cre_b(3), des_b(6)): -1, - (cre_a(6), des_a(3)): -1, - (cre_b(6), des_b(3)): -1, + (cre_a(0), des_a(3)): -1, + (cre_b(0), des_b(3)): -1, + (cre_a(3), des_a(0)): -1, + (cre_b(3), des_b(0)): -1, (cre_a(3), des_a(4)): -1, (cre_b(3), des_b(4)): -1, (cre_a(4), des_a(3)): -1, (cre_b(4), des_b(3)): -1, - (cre_a(4), des_a(7)): -1, - (cre_b(4), des_b(7)): -1, - (cre_a(7), des_a(4)): -1, - (cre_b(7), des_b(4)): -1, - (cre_a(4), des_a(5)): -1, - (cre_b(4), des_b(5)): -1, - (cre_a(5), des_a(4)): -1, - (cre_b(5), des_b(4)): -1, - (cre_a(5), des_a(8)): -1, - (cre_b(5), des_b(8)): -1, - (cre_a(8), des_a(5)): -1, - (cre_b(8), des_b(5)): -1, - (cre_a(5), des_a(3)): -1, - (cre_b(5), des_b(3)): -1, - (cre_a(3), des_a(5)): -1, - (cre_b(3), des_b(5)): -1, - (cre_a(6), des_a(0)): -1, - (cre_b(6), des_b(0)): -1, - (cre_a(0), des_a(6)): -1, - (cre_b(0), des_b(6)): -1, + (cre_a(3), des_a(6)): -1, + (cre_b(3), des_b(6)): -1, + (cre_a(6), des_a(3)): -1, + (cre_b(6), des_b(3)): -1, (cre_a(6), des_a(7)): -1, (cre_b(6), des_b(7)): -1, (cre_a(7), des_a(6)): -1, (cre_b(7), des_b(6)): -1, - (cre_a(7), des_a(1)): -1, - (cre_b(7), des_b(1)): -1, - (cre_a(1), des_a(7)): -1, - (cre_b(1), des_b(7)): -1, + (cre_a(6), des_a(0)): -1, + (cre_b(6), des_b(0)): -1, + (cre_a(0), des_a(6)): -1, + (cre_b(0), des_b(6)): -1, + (cre_a(1), des_a(2)): -1, + (cre_b(1), des_b(2)): -1, + (cre_a(2), des_a(1)): -1, + (cre_b(2), des_b(1)): -1, + (cre_a(1), des_a(4)): -1, + (cre_b(1), des_b(4)): -1, + (cre_a(4), des_a(1)): -1, + (cre_b(4), des_b(1)): -1, + (cre_a(4), des_a(5)): -1, + (cre_b(4), des_b(5)): -1, + (cre_a(5), des_a(4)): -1, + (cre_b(5), des_b(4)): -1, + (cre_a(4), des_a(7)): -1, + (cre_b(4), des_b(7)): -1, + (cre_a(7), des_a(4)): -1, + (cre_b(7), des_b(4)): -1, (cre_a(7), des_a(8)): -1, (cre_b(7), des_b(8)): -1, (cre_a(8), des_a(7)): -1, (cre_b(8), des_b(7)): -1, - (cre_a(8), des_a(2)): -1, - (cre_b(8), des_b(2)): -1, - (cre_a(2), des_a(8)): -1, - (cre_b(2), des_b(8)): -1, + (cre_a(7), des_a(1)): -1, + (cre_b(7), des_b(1)): -1, + (cre_a(1), des_a(7)): -1, + (cre_b(1), des_b(7)): -1, + (cre_a(2), des_a(0)): -1, + (cre_b(2), des_b(0)): -1, + (cre_a(0), des_a(2)): -1, + (cre_b(0), des_b(2)): -1, + (cre_a(2), des_a(5)): -1, + (cre_b(2), des_b(5)): -1, + (cre_a(5), des_a(2)): -1, + (cre_b(5), des_b(2)): -1, + (cre_a(5), des_a(3)): -1, + (cre_b(5), des_b(3)): -1, + (cre_a(3), des_a(5)): -1, + (cre_b(3), des_b(5)): -1, + (cre_a(5), des_a(8)): -1, + (cre_b(5), des_b(8)): -1, + (cre_a(8), des_a(5)): -1, + (cre_b(8), des_b(5)): -1, (cre_a(8), des_a(6)): -1, (cre_b(8), des_b(6)): -1, (cre_a(6), des_a(8)): -1, (cre_b(6), des_b(8)): -1, + (cre_a(8), des_a(2)): -1, + (cre_b(8), des_b(2)): -1, + (cre_a(2), des_a(8)): -1, + (cre_b(2), des_b(8)): -1, (cre_a(0), des_a(0), cre_b(0), des_b(0)): 2, (cre_a(1), des_a(1), cre_b(1), des_b(1)): 2, (cre_a(2), des_a(2), cre_b(2), des_b(2)): 2, @@ -340,78 +340,78 @@ def test_fermi_hubbard_2d(): (cre_b(7), des_b(7)): -3, (cre_a(8), des_a(8)): -3, (cre_b(8), des_b(8)): -3, - (cre_a(0), des_a(0), cre_a(3), des_a(3)): 4, - (cre_a(0), des_a(0), cre_b(3), des_b(3)): 4, - (cre_b(0), des_b(0), cre_a(3), des_a(3)): 4, - (cre_b(0), des_b(0), cre_b(3), des_b(3)): 4, (cre_a(0), des_a(0), cre_a(1), des_a(1)): 4, (cre_a(0), des_a(0), cre_b(1), des_b(1)): 4, (cre_b(0), des_b(0), cre_a(1), des_a(1)): 4, (cre_b(0), des_b(0), cre_b(1), des_b(1)): 4, - (cre_a(1), des_a(1), cre_a(4), des_a(4)): 4, - (cre_a(1), des_a(1), cre_b(4), des_b(4)): 4, - (cre_b(1), des_b(1), cre_a(4), des_a(4)): 4, - (cre_b(1), des_b(1), cre_b(4), des_b(4)): 4, - (cre_a(1), des_a(1), cre_a(2), des_a(2)): 4, - (cre_a(1), des_a(1), cre_b(2), des_b(2)): 4, - (cre_b(1), des_b(1), cre_a(2), des_a(2)): 4, - (cre_b(1), des_b(1), cre_b(2), des_b(2)): 4, - (cre_a(2), des_a(2), cre_a(5), des_a(5)): 4, - (cre_a(2), des_a(2), cre_b(5), des_b(5)): 4, - (cre_b(2), des_b(2), cre_a(5), des_a(5)): 4, - (cre_b(2), des_b(2), cre_b(5), des_b(5)): 4, - (cre_a(2), des_a(2), cre_a(0), des_a(0)): 4, - (cre_a(2), des_a(2), cre_b(0), des_b(0)): 4, - (cre_b(2), des_b(2), cre_a(0), des_a(0)): 4, - (cre_b(2), des_b(2), cre_b(0), des_b(0)): 4, - (cre_a(3), des_a(3), cre_a(6), des_a(6)): 4, - (cre_a(3), des_a(3), cre_b(6), des_b(6)): 4, - (cre_b(3), des_b(3), cre_a(6), des_a(6)): 4, - (cre_b(3), des_b(3), cre_b(6), des_b(6)): 4, + (cre_a(0), des_a(0), cre_a(3), des_a(3)): 4, + (cre_a(0), des_a(0), cre_b(3), des_b(3)): 4, + (cre_b(0), des_b(0), cre_a(3), des_a(3)): 4, + (cre_b(0), des_b(0), cre_b(3), des_b(3)): 4, (cre_a(3), des_a(3), cre_a(4), des_a(4)): 4, (cre_a(3), des_a(3), cre_b(4), des_b(4)): 4, (cre_b(3), des_b(3), cre_a(4), des_a(4)): 4, (cre_b(3), des_b(3), cre_b(4), des_b(4)): 4, - (cre_a(4), des_a(4), cre_a(7), des_a(7)): 4, - (cre_a(4), des_a(4), cre_b(7), des_b(7)): 4, - (cre_b(4), des_b(4), cre_a(7), des_a(7)): 4, - (cre_b(4), des_b(4), cre_b(7), des_b(7)): 4, - (cre_a(4), des_a(4), cre_a(5), des_a(5)): 4, - (cre_a(4), des_a(4), cre_b(5), des_b(5)): 4, - (cre_b(4), des_b(4), cre_a(5), des_a(5)): 4, - (cre_b(4), des_b(4), cre_b(5), des_b(5)): 4, - (cre_a(5), des_a(5), cre_a(8), des_a(8)): 4, - (cre_a(5), des_a(5), cre_b(8), des_b(8)): 4, - (cre_b(5), des_b(5), cre_a(8), des_a(8)): 4, - (cre_b(5), des_b(5), cre_b(8), des_b(8)): 4, - (cre_a(5), des_a(5), cre_a(3), des_a(3)): 4, - (cre_a(5), des_a(5), cre_b(3), des_b(3)): 4, - (cre_b(5), des_b(5), cre_a(3), des_a(3)): 4, - (cre_b(5), des_b(5), cre_b(3), des_b(3)): 4, - (cre_a(6), des_a(6), cre_a(0), des_a(0)): 4, - (cre_a(6), des_a(6), cre_b(0), des_b(0)): 4, - (cre_b(6), des_b(6), cre_a(0), des_a(0)): 4, - (cre_b(6), des_b(6), cre_b(0), des_b(0)): 4, + (cre_a(3), des_a(3), cre_a(6), des_a(6)): 4, + (cre_a(3), des_a(3), cre_b(6), des_b(6)): 4, + (cre_b(3), des_b(3), cre_a(6), des_a(6)): 4, + (cre_b(3), des_b(3), cre_b(6), des_b(6)): 4, (cre_a(6), des_a(6), cre_a(7), des_a(7)): 4, (cre_a(6), des_a(6), cre_b(7), des_b(7)): 4, (cre_b(6), des_b(6), cre_a(7), des_a(7)): 4, (cre_b(6), des_b(6), cre_b(7), des_b(7)): 4, - (cre_a(7), des_a(7), cre_a(1), des_a(1)): 4, - (cre_a(7), des_a(7), cre_b(1), des_b(1)): 4, - (cre_b(7), des_b(7), cre_a(1), des_a(1)): 4, - (cre_b(7), des_b(7), cre_b(1), des_b(1)): 4, + (cre_a(6), des_a(6), cre_a(0), des_a(0)): 4, + (cre_a(6), des_a(6), cre_b(0), des_b(0)): 4, + (cre_b(6), des_b(6), cre_a(0), des_a(0)): 4, + (cre_b(6), des_b(6), cre_b(0), des_b(0)): 4, + (cre_a(1), des_a(1), cre_a(2), des_a(2)): 4, + (cre_a(1), des_a(1), cre_b(2), des_b(2)): 4, + (cre_b(1), des_b(1), cre_a(2), des_a(2)): 4, + (cre_b(1), des_b(1), cre_b(2), des_b(2)): 4, + (cre_a(1), des_a(1), cre_a(4), des_a(4)): 4, + (cre_a(1), des_a(1), cre_b(4), des_b(4)): 4, + (cre_b(1), des_b(1), cre_a(4), des_a(4)): 4, + (cre_b(1), des_b(1), cre_b(4), des_b(4)): 4, + (cre_a(4), des_a(4), cre_a(5), des_a(5)): 4, + (cre_a(4), des_a(4), cre_b(5), des_b(5)): 4, + (cre_b(4), des_b(4), cre_a(5), des_a(5)): 4, + (cre_b(4), des_b(4), cre_b(5), des_b(5)): 4, + (cre_a(4), des_a(4), cre_a(7), des_a(7)): 4, + (cre_a(4), des_a(4), cre_b(7), des_b(7)): 4, + (cre_b(4), des_b(4), cre_a(7), des_a(7)): 4, + (cre_b(4), des_b(4), cre_b(7), des_b(7)): 4, (cre_a(7), des_a(7), cre_a(8), des_a(8)): 4, (cre_a(7), des_a(7), cre_b(8), des_b(8)): 4, (cre_b(7), des_b(7), cre_a(8), des_a(8)): 4, (cre_b(7), des_b(7), cre_b(8), des_b(8)): 4, - (cre_a(8), des_a(8), cre_a(2), des_a(2)): 4, - (cre_a(8), des_a(8), cre_b(2), des_b(2)): 4, - (cre_b(8), des_b(8), cre_a(2), des_a(2)): 4, - (cre_b(8), des_b(8), cre_b(2), des_b(2)): 4, + (cre_a(7), des_a(7), cre_a(1), des_a(1)): 4, + (cre_a(7), des_a(7), cre_b(1), des_b(1)): 4, + (cre_b(7), des_b(7), cre_a(1), des_a(1)): 4, + (cre_b(7), des_b(7), cre_b(1), des_b(1)): 4, + (cre_a(2), des_a(2), cre_a(0), des_a(0)): 4, + (cre_a(2), des_a(2), cre_b(0), des_b(0)): 4, + (cre_b(2), des_b(2), cre_a(0), des_a(0)): 4, + (cre_b(2), des_b(2), cre_b(0), des_b(0)): 4, + (cre_a(2), des_a(2), cre_a(5), des_a(5)): 4, + (cre_a(2), des_a(2), cre_b(5), des_b(5)): 4, + (cre_b(2), des_b(2), cre_a(5), des_a(5)): 4, + (cre_b(2), des_b(2), cre_b(5), des_b(5)): 4, + (cre_a(5), des_a(5), cre_a(3), des_a(3)): 4, + (cre_a(5), des_a(5), cre_b(3), des_b(3)): 4, + (cre_b(5), des_b(5), cre_a(3), des_a(3)): 4, + (cre_b(5), des_b(5), cre_b(3), des_b(3)): 4, + (cre_a(5), des_a(5), cre_a(8), des_a(8)): 4, + (cre_a(5), des_a(5), cre_b(8), des_b(8)): 4, + (cre_b(5), des_b(5), cre_a(8), des_a(8)): 4, + (cre_b(5), des_b(5), cre_b(8), des_b(8)): 4, (cre_a(8), des_a(8), cre_a(6), des_a(6)): 4, (cre_a(8), des_a(8), cre_b(6), des_b(6)): 4, (cre_b(8), des_b(8), cre_a(6), des_a(6)): 4, (cre_b(8), des_b(8), cre_b(6), des_b(6)): 4, + (cre_a(8), des_a(8), cre_a(2), des_a(2)): 4, + (cre_a(8), des_a(8), cre_b(2), des_b(2)): 4, + (cre_b(8), des_b(8), cre_a(2), des_a(2)): 4, + (cre_b(8), des_b(8), cre_b(2), des_b(2)): 4, }, ) @@ -428,22 +428,22 @@ def test_fermi_hubbard_2d(): np.testing.assert_equal( dict(op_periodic_edge), { - (cre_a(0), des_a(2)): -2, - (cre_b(0), des_b(2)): -2, - (cre_a(2), des_a(0)): -2, - (cre_b(2), des_b(0)): -2, (cre_a(0), des_a(1)): -2, (cre_b(0), des_b(1)): -2, (cre_a(1), des_a(0)): -2, (cre_b(1), des_b(0)): -2, - (cre_a(1), des_a(3)): -2, - (cre_b(1), des_b(3)): -2, - (cre_a(3), des_a(1)): -2, - (cre_b(3), des_b(1)): -2, + (cre_a(0), des_a(2)): -2, + (cre_b(0), des_b(2)): -2, + (cre_a(2), des_a(0)): -2, + (cre_b(2), des_b(0)): -2, (cre_a(2), des_a(3)): -2, (cre_b(2), des_b(3)): -2, (cre_a(3), des_a(2)): -2, (cre_b(3), des_b(2)): -2, + (cre_a(1), des_a(3)): -2, + (cre_b(1), des_b(3)): -2, + (cre_a(3), des_a(1)): -2, + (cre_b(3), des_b(1)): -2, (cre_a(0), des_a(0), cre_b(0), des_b(0)): 2, (cre_a(1), des_a(1), cre_b(1), des_b(1)): 2, (cre_a(2), des_a(2), cre_b(2), des_b(2)): 2, @@ -456,38 +456,38 @@ def test_fermi_hubbard_2d(): (cre_b(2), des_b(2)): -3, (cre_a(3), des_a(3)): -3, (cre_b(3), des_b(3)): -3, - (cre_a(0), des_a(0), cre_a(2), des_a(2)): 4, - (cre_a(0), des_a(0), cre_b(2), des_b(2)): 4, - (cre_b(0), des_b(0), cre_a(2), des_a(2)): 4, - (cre_b(0), des_b(0), cre_b(2), des_b(2)): 4, (cre_a(0), des_a(0), cre_a(1), des_a(1)): 4, (cre_a(0), des_a(0), cre_b(1), des_b(1)): 4, (cre_b(0), des_b(0), cre_a(1), des_a(1)): 4, (cre_b(0), des_b(0), cre_b(1), des_b(1)): 4, - (cre_a(1), des_a(1), cre_a(3), des_a(3)): 4, - (cre_a(1), des_a(1), cre_b(3), des_b(3)): 4, - (cre_b(1), des_b(1), cre_a(3), des_a(3)): 4, - (cre_b(1), des_b(1), cre_b(3), des_b(3)): 4, - (cre_a(1), des_a(1), cre_a(0), des_a(0)): 4, - (cre_a(1), des_a(1), cre_b(0), des_b(0)): 4, - (cre_b(1), des_b(1), cre_a(0), des_a(0)): 4, - (cre_b(1), des_b(1), cre_b(0), des_b(0)): 4, - (cre_a(2), des_a(2), cre_a(0), des_a(0)): 4, - (cre_a(2), des_a(2), cre_b(0), des_b(0)): 4, - (cre_b(2), des_b(2), cre_a(0), des_a(0)): 4, - (cre_b(2), des_b(2), cre_b(0), des_b(0)): 4, + (cre_a(0), des_a(0), cre_a(2), des_a(2)): 4, + (cre_a(0), des_a(0), cre_b(2), des_b(2)): 4, + (cre_b(0), des_b(0), cre_a(2), des_a(2)): 4, + (cre_b(0), des_b(0), cre_b(2), des_b(2)): 4, (cre_a(2), des_a(2), cre_a(3), des_a(3)): 4, (cre_a(2), des_a(2), cre_b(3), des_b(3)): 4, (cre_b(2), des_b(2), cre_a(3), des_a(3)): 4, (cre_b(2), des_b(2), cre_b(3), des_b(3)): 4, - (cre_a(3), des_a(3), cre_a(1), des_a(1)): 4, - (cre_a(3), des_a(3), cre_b(1), des_b(1)): 4, - (cre_b(3), des_b(3), cre_a(1), des_a(1)): 4, - (cre_b(3), des_b(3), cre_b(1), des_b(1)): 4, + (cre_a(2), des_a(2), cre_a(0), des_a(0)): 4, + (cre_a(2), des_a(2), cre_b(0), des_b(0)): 4, + (cre_b(2), des_b(2), cre_a(0), des_a(0)): 4, + (cre_b(2), des_b(2), cre_b(0), des_b(0)): 4, + (cre_a(1), des_a(1), cre_a(0), des_a(0)): 4, + (cre_a(1), des_a(1), cre_b(0), des_b(0)): 4, + (cre_b(1), des_b(1), cre_a(0), des_a(0)): 4, + (cre_b(1), des_b(1), cre_b(0), des_b(0)): 4, + (cre_a(1), des_a(1), cre_a(3), des_a(3)): 4, + (cre_a(1), des_a(1), cre_b(3), des_b(3)): 4, + (cre_b(1), des_b(1), cre_a(3), des_a(3)): 4, + (cre_b(1), des_b(1), cre_b(3), des_b(3)): 4, (cre_a(3), des_a(3), cre_a(2), des_a(2)): 4, (cre_a(3), des_a(3), cre_b(2), des_b(2)): 4, (cre_b(3), des_b(3), cre_a(2), des_a(2)): 4, (cre_b(3), des_b(3), cre_b(2), des_b(2)): 4, + (cre_a(3), des_a(3), cre_a(1), des_a(1)): 4, + (cre_a(3), des_a(3), cre_b(1), des_b(1)): 4, + (cre_b(3), des_b(3), cre_a(1), des_a(1)): 4, + (cre_b(3), des_b(3), cre_b(1), des_b(1)): 4, }, )