diff --git a/README.md b/README.md index e858774..44ceb06 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![pylint](https://img.shields.io/badge/PyLint-9.52-yellow?logo=python&logoColor=white) +![pylint](https://img.shields.io/badge/PyLint-9.45-yellow?logo=python&logoColor=white) # Quantum Computing Application Specifications Documentation of Applications for Quantum Computers diff --git a/notebooks/HighTemperatureSuperConductorExample.ipynb b/notebooks/HighTemperatureSuperConductorExample.ipynb index a8fcbb4..35c635a 100644 --- a/notebooks/HighTemperatureSuperConductorExample.ipynb +++ b/notebooks/HighTemperatureSuperConductorExample.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The Fermi-Hubbard model is often used to predict the behavior of the electrons of proposed superconductors. It has the advantage of being simply stated while describing a variety of phenomena relating to the phases of real-world materials such as the transition between superconducting and Mott insulator phases. In order to understand the behavior of the Fermi-Hubbard model at high temperatures, it is first critical to understand the phase diagram of the Fermi-Hubbard model at absolute zero (with no heat-bath present). To achieve this, one must understand the ground state energy of the model with various filling coefficients. In this notebook, we will show the necessary steps perform this analysis on a quantum computer.\n", + "The Fermi-Hubbard model is often used to predict the behavior of the electrons of proposed superconductors. It has the advantage of being simply stated while describing a variety of phenomena relating to the phases of real-world materials such as the transition between superconducting and Mott insulator phases. In order to understand the behavior of the Fermi-Hubbard model at high temperatures, it is first critical to understand the phase diagram of the Fermi-Hubbard model at absolute zero (with no heat-bath present). To achieve this, one must understand the ground state energy of the model with various filling coefficients. In this notebook, we will show the necessary steps to perform this analysis on a quantum computer.\n", "\n", "The most basic form of the Fermi-Hubbard model with a single band can be represented through the following equation:\n", "\n", @@ -34,7 +34,7 @@ " + U \\sum n_{i, \\uparrow} n_{i, \\downarrow}\n", "\\end{equation}\n", "\n", - "where $c^{\\dagger}_{i, \\uparrow}$ is the fermionic creation operator on site $i$ with a spin $\\uparrow$ and $c_{i, \\uparrow}$ is the fermionic annihilation operator on site $i$ with a spin $\\uparrow$. The number operator on site $i$ with spin $\\uparrow$ is given by $n_{i, \\uparrow} = c^{\\dagger}_{i, \\uparrow} c_{i, \\uparrow}$. We will assume that the interactions are occurring on a 2-d square lattice, defining the site connectivity $(i,j)$ The coefficients $t_{i,j}$ represent the affinity for tunnelling between two orbitals at sites $i$ and $j$ and the coefficient $U$ represents the strength of the Coulomb interaction between electrons on the same orbital.\n", + "where $c^{\\dagger}_{i, \\uparrow}$ is the fermionic creation operator on site $i$ with a spin $\\uparrow$ and $c_{i, \\uparrow}$ is the fermionic annihilation operator on site $i$ with a spin $\\uparrow$. The number operator on site $i$ with spin $\\uparrow$ is given by $n_{i, \\uparrow} = c^{\\dagger}_{i, \\uparrow} c_{i, \\uparrow}$. We will assume that the interactions are occurring on a 2-d square lattice, defining the site connectivity $(i,j)$. The coefficients $t_{i,j}$ represent the affinity for tunnelling between two orbitals at sites $i$ and $j$ and the coefficient $U$ represents the strength of the Coulomb interaction between electrons on the same orbital.\n", "\n", "There are 3 steps to estimate the ground state energy of this Hamiltonian:\n", "\n", @@ -50,20 +50,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 20, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/jonhas/anaconda3/envs/other_qca/lib/python3.11/site-packages/attr/_make.py:918: RuntimeWarning: Running interpreter doesn't sufficiently support code object introspection. Some features like bare super() or accessing __class__ will not work with slotted classes.\n", - " set_closure_cell(cell, cls)\n" - ] - } - ], + "outputs": [], "source": [ "import time\n", "\n", @@ -75,12 +66,13 @@ "\n", "from qca.utils.utils import EstimateMetaData\n", "from qca.utils.algo_utils import gsee_resource_estimation\n", - "from qca.utils.hamiltonian_utils import generate_two_orbital_nx, nx_to_two_orbital_hamiltonian" + "from qca.utils.hamiltonian_utils import (generate_two_orbital_nx, nx_to_two_orbital_hamiltonian,\n", + " generate_three_orbital_nx, nx_to_three_orbital_hamiltonian)" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 21, "metadata": { "tags": [] }, @@ -88,6 +80,9 @@ "source": [ "#Generating a 20x20 Fermi Hubbard model with a single band. The ratio between Tunneling and Coulomb parameters can be swept to search for the appropriate mean electron filling.\n", "n = 20\n", + "####START UNCOMMENT FOR TESTING\n", + "n = 2\n", + "####END UNCOMMENT FOR TESTING\n", "tunneling = 1\n", "coulomb = 8\n", "ham_one_band = of.fermi_hubbard(n, n, tunneling=tunneling, coulomb=coulomb, periodic=False) #returns an aperiodic fermionic hamiltonian" @@ -179,18 +174,18 @@ "output_type": "stream", "text": [ "Estimating one_band\n", - "Time to generate circuit for GSEE: 0.0008551250000436994 seconds\n", - " Time to decompose high level " ] @@ -273,7 +276,9 @@ "pos = get_node_attributes(g_example, 'pos')\n", "edge_labels = dict([((n1, n2), d['label']) for n1, n2, d in g_example.edges(data=True)]);\n", "draw(g_example, pos)\n", - "draw_networkx_edge_labels(g_example,pos, edge_labels = edge_labels);" + "draw_networkx_edge_labels(g_example,pos, edge_labels = edge_labels);\n", + "\n", + "print(g_example.edges(data=True))" ] }, { @@ -287,84 +292,84 @@ "data": { "text/plain": [ "-1.0 [0^ 0] +\n", - "-0.85 [0^ 4] +\n", - "-0.85 [0^ 8] +\n", + "1.0 [0^ 4] +\n", + "-1.3 [0^ 8] +\n", "-0.85 [0^ 12] +\n", "0.85 [0^ 14] +\n", "-1.0 [1^ 1] +\n", - "-0.85 [1^ 5] +\n", - "-0.85 [1^ 9] +\n", + "1.0 [1^ 5] +\n", + "-1.3 [1^ 9] +\n", "-0.85 [1^ 13] +\n", "0.85 [1^ 15] +\n", "-1.0 [2^ 2] +\n", - "-0.85 [2^ 6] +\n", - "-0.85 [2^ 10] +\n", + "-1.3 [2^ 6] +\n", + "1.0 [2^ 10] +\n", "0.85 [2^ 12] +\n", "-0.85 [2^ 14] +\n", "-1.0 [3^ 3] +\n", - "-0.85 [3^ 7] +\n", - "-0.85 [3^ 11] +\n", + "-1.3 [3^ 7] +\n", + "1.0 [3^ 11] +\n", "0.85 [3^ 13] +\n", "-0.85 [3^ 15] +\n", - "-0.85 [4^ 0] +\n", + "1.0 [4^ 0] +\n", "-1.0 [4^ 4] +\n", "-0.85 [4^ 8] +\n", "-0.85 [4^ 10] +\n", - "-0.85 [4^ 12] +\n", - "-0.85 [5^ 1] +\n", + "-1.3 [4^ 12] +\n", + "1.0 [5^ 1] +\n", "-1.0 [5^ 5] +\n", "-0.85 [5^ 9] +\n", "-0.85 [5^ 11] +\n", - "-0.85 [5^ 13] +\n", - "-0.85 [6^ 2] +\n", + "-1.3 [5^ 13] +\n", + "-1.3 [6^ 2] +\n", "-1.0 [6^ 6] +\n", "-0.85 [6^ 8] +\n", "-0.85 [6^ 10] +\n", - "-0.85 [6^ 14] +\n", - "-0.85 [7^ 3] +\n", + "1.0 [6^ 14] +\n", + "-1.3 [7^ 3] +\n", "-1.0 [7^ 7] +\n", "-0.85 [7^ 9] +\n", "-0.85 [7^ 11] +\n", - "-0.85 [7^ 15] +\n", - "-0.85 [8^ 0] +\n", + "1.0 [7^ 15] +\n", + "-1.3 [8^ 0] +\n", "-0.85 [8^ 4] +\n", "-0.85 [8^ 6] +\n", "-1.0 [8^ 8] +\n", - "-0.85 [8^ 12] +\n", - "-0.85 [9^ 1] +\n", + "1.0 [8^ 12] +\n", + "-1.3 [9^ 1] +\n", "-0.85 [9^ 5] +\n", "-0.85 [9^ 7] +\n", "-1.0 [9^ 9] +\n", - "-0.85 [9^ 13] +\n", - "-0.85 [10^ 2] +\n", + "1.0 [9^ 13] +\n", + "1.0 [10^ 2] +\n", "-0.85 [10^ 4] +\n", "-0.85 [10^ 6] +\n", "-1.0 [10^ 10] +\n", - "-0.85 [10^ 14] +\n", - "-0.85 [11^ 3] +\n", + "-1.3 [10^ 14] +\n", + "1.0 [11^ 3] +\n", "-0.85 [11^ 5] +\n", "-0.85 [11^ 7] +\n", "-1.0 [11^ 11] +\n", - "-0.85 [11^ 15] +\n", + "-1.3 [11^ 15] +\n", "-0.85 [12^ 0] +\n", "0.85 [12^ 2] +\n", - "-0.85 [12^ 4] +\n", - "-0.85 [12^ 8] +\n", + "-1.3 [12^ 4] +\n", + "1.0 [12^ 8] +\n", "-1.0 [12^ 12] +\n", "-0.85 [13^ 1] +\n", "0.85 [13^ 3] +\n", - "-0.85 [13^ 5] +\n", - "-0.85 [13^ 9] +\n", + "-1.3 [13^ 5] +\n", + "1.0 [13^ 9] +\n", "-1.0 [13^ 13] +\n", "0.85 [14^ 0] +\n", "-0.85 [14^ 2] +\n", - "-0.85 [14^ 6] +\n", - "-0.85 [14^ 10] +\n", + "1.0 [14^ 6] +\n", + "-1.3 [14^ 10] +\n", "-1.0 [14^ 14] +\n", "0.85 [15^ 1] +\n", "-0.85 [15^ 3] +\n", - "-0.85 [15^ 7] +\n", - "-0.85 [15^ 11] +\n", + "1.0 [15^ 7] +\n", + "-1.3 [15^ 11] +\n", "-1.0 [15^ 15]" ] }, @@ -394,9 +399,9 @@ "g_ideal = generate_two_orbital_nx(20,20)\n", "\n", "##### START UNCOMMENT FOR TESTING\n", - "#n_test = 2\n", - "#g_current_limit = generate_two_orbital_nx(n_test,n_test) \n", - "#g_ideal = generate_two_orbital_nx(n_test,n_test)\n", + "n_test = 2\n", + "g_current_limit = generate_two_orbital_nx(n_test,n_test) \n", + "g_ideal = generate_two_orbital_nx(n_test,n_test)\n", "##### END UNCOMMENT FOR TESTING\n", "n_qubits_current_limit = len(g_current_limit)\n", "n_qubits_ideal = len(g_ideal)" @@ -493,7 +498,7 @@ " category='scientific',\n", " size=f'{6}x{7}',\n", " task='Ground State Energy Estimation',\n", - " implementations=f'GSEE, evolution_time={t_one_band}, bits_precision={bits_precision_one_band}, trotter_order={trotter_order_one_band}',\n", + " implementations=f'GSEE, evolution_time={t_current_limit}, bits_precision={bits_precision_current_limit}, trotter_order={trotter_order_current_limit}',\n", ")\n", "\n", "metadata_ideal = EstimateMetaData(\n", @@ -502,7 +507,7 @@ " category='scientific',\n", " size=f'{20}x{20}',\n", " task='Ground State Energy Estimation',\n", - " implementations=f'GSEE, evolution_time={t_one_band}, bits_precision={bits_precision_one_band}, trotter_order={trotter_order_one_band}',\n", + " implementations=f'GSEE, evolution_time={t_ideal}, bits_precision={bits_precision_ideal}, trotter_order={trotter_order_ideal}',\n", ")" ] }, @@ -516,31 +521,31 @@ "output_type": "stream", "text": [ "Estimating Current Limit\n", - "Time to generate circuit for GSEE: 0.00016554200010432396 seconds\n", - " Time to decompose high level " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "g_example = generate_three_orbital_nx(2,2)\n", + "pos = get_node_attributes(g_example, 'pos')\n", + "edge_labels = dict([((n1, n2), d['label']) for n1, n2, d in g_example.edges(data=True)]);\n", + "draw(g_example, pos)\n", + "draw_networkx_edge_labels(g_example,pos, edge_labels = edge_labels);\n", + "\n", + "print(g_example.edges(data=True))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1.0 [0^ 0] +\n", + "-0.02 [0^ 4] +\n", + "-0.06 [0^ 8] +\n", + "-0.03 [0^ 12] +\n", + "-0.01 [0^ 14] +\n", + "0.2 [0^ 20] +\n", + "-0.1 [0^ 22] +\n", + "-1.0 [1^ 1] +\n", + "-0.02 [1^ 5] +\n", + "-0.06 [1^ 9] +\n", + "-0.03 [1^ 13] +\n", + "-0.01 [1^ 15] +\n", + "0.2 [1^ 21] +\n", + "-0.1 [1^ 23] +\n", + "-1.0 [2^ 2] +\n", + "-0.06 [2^ 6] +\n", + "-0.02 [2^ 10] +\n", + "-0.01 [2^ 12] +\n", + "-0.03 [2^ 14] +\n", + "0.2 [2^ 18] +\n", + "-0.1 [2^ 22] +\n", + "-1.0 [3^ 3] +\n", + "-0.06 [3^ 7] +\n", + "-0.02 [3^ 11] +\n", + "-0.01 [3^ 13] +\n", + "-0.03 [3^ 15] +\n", + "0.2 [3^ 19] +\n", + "-0.1 [3^ 23] +\n", + "-0.02 [4^ 0] +\n", + "-1.0 [4^ 4] +\n", + "-0.03 [4^ 8] +\n", + "0.01 [4^ 10] +\n", + "-0.06 [4^ 12] +\n", + "0.1 [4^ 20] +\n", + "-0.2 [4^ 22] +\n", + "-0.02 [5^ 1] +\n", + "-1.0 [5^ 5] +\n", + "-0.03 [5^ 9] +\n", + "0.01 [5^ 11] +\n", + "-0.06 [5^ 13] +\n", + "0.1 [5^ 21] +\n", + "-0.2 [5^ 23] +\n", + "-0.06 [6^ 2] +\n", + "-1.0 [6^ 6] +\n", + "0.01 [6^ 8] +\n", + "-0.03 [6^ 10] +\n", + "-0.02 [6^ 14] +\n", + "0.2 [6^ 16] +\n", + "-0.1 [6^ 20] +\n", + "-0.06 [7^ 3] +\n", + "-1.0 [7^ 7] +\n", + "0.01 [7^ 9] +\n", + "-0.03 [7^ 11] +\n", + "-0.02 [7^ 15] +\n", + "0.2 [7^ 17] +\n", + "-0.1 [7^ 21] +\n", + "-0.06 [8^ 0] +\n", + "-0.03 [8^ 4] +\n", + "0.01 [8^ 6] +\n", + "-1.0 [8^ 8] +\n", + "-0.02 [8^ 12] +\n", + "0.2 [8^ 16] +\n", + "-0.1 [8^ 18] +\n", + "-0.06 [9^ 1] +\n", + "-0.03 [9^ 5] +\n", + "0.01 [9^ 7] +\n", + "-1.0 [9^ 9] +\n", + "-0.02 [9^ 13] +\n", + "0.2 [9^ 17] +\n", + "-0.1 [9^ 19] +\n", + "-0.02 [10^ 2] +\n", + "0.01 [10^ 4] +\n", + "-0.03 [10^ 6] +\n", + "-1.0 [10^ 10] +\n", + "-0.06 [10^ 14] +\n", + "0.1 [10^ 18] +\n", + "-0.2 [10^ 22] +\n", + "-0.02 [11^ 3] +\n", + "0.01 [11^ 5] +\n", + "-0.03 [11^ 7] +\n", + "-1.0 [11^ 11] +\n", + "-0.06 [11^ 15] +\n", + "0.1 [11^ 19] +\n", + "-0.2 [11^ 23] +\n", + "-0.03 [12^ 0] +\n", + "-0.01 [12^ 2] +\n", + "-0.06 [12^ 4] +\n", + "-0.02 [12^ 8] +\n", + "-1.0 [12^ 12] +\n", + "0.1 [12^ 16] +\n", + "-0.2 [12^ 18] +\n", + "-0.03 [13^ 1] +\n", + "-0.01 [13^ 3] +\n", + "-0.06 [13^ 5] +\n", + "-0.02 [13^ 9] +\n", + "-1.0 [13^ 13] +\n", + "0.1 [13^ 17] +\n", + "-0.2 [13^ 19] +\n", + "-0.01 [14^ 0] +\n", + "-0.03 [14^ 2] +\n", + "-0.02 [14^ 6] +\n", + "-0.06 [14^ 10] +\n", + "-1.0 [14^ 14] +\n", + "0.1 [14^ 16] +\n", + "-0.2 [14^ 20] +\n", + "-0.01 [15^ 1] +\n", + "-0.03 [15^ 3] +\n", + "-0.02 [15^ 7] +\n", + "-0.06 [15^ 11] +\n", + "-1.0 [15^ 15] +\n", + "0.1 [15^ 17] +\n", + "-0.2 [15^ 21] +\n", + "0.2 [16^ 6] +\n", + "0.2 [16^ 8] +\n", + "0.1 [16^ 12] +\n", + "0.1 [16^ 14] +\n", + "-0.6 [16^ 16] +\n", + "0.2 [16^ 18] +\n", + "0.2 [16^ 20] +\n", + "-0.3 [16^ 22] +\n", + "0.2 [17^ 7] +\n", + "0.2 [17^ 9] +\n", + "0.1 [17^ 13] +\n", + "0.1 [17^ 15] +\n", + "-0.6 [17^ 17] +\n", + "0.2 [17^ 19] +\n", + "0.2 [17^ 21] +\n", + "-0.3 [17^ 23] +\n", + "0.2 [18^ 2] +\n", + "-0.1 [18^ 8] +\n", + "0.1 [18^ 10] +\n", + "-0.2 [18^ 12] +\n", + "0.2 [18^ 16] +\n", + "-0.6 [18^ 18] +\n", + "-0.3 [18^ 20] +\n", + "0.2 [18^ 22] +\n", + "0.2 [19^ 3] +\n", + "-0.1 [19^ 9] +\n", + "0.1 [19^ 11] +\n", + "-0.2 [19^ 13] +\n", + "0.2 [19^ 17] +\n", + "-0.6 [19^ 19] +\n", + "-0.3 [19^ 21] +\n", + "0.2 [19^ 23] +\n", + "0.2 [20^ 0] +\n", + "0.1 [20^ 4] +\n", + "-0.1 [20^ 6] +\n", + "-0.2 [20^ 14] +\n", + "0.2 [20^ 16] +\n", + "-0.3 [20^ 18] +\n", + "-0.6 [20^ 20] +\n", + "0.2 [20^ 22] +\n", + "0.2 [21^ 1] +\n", + "0.1 [21^ 5] +\n", + "-0.1 [21^ 7] +\n", + "-0.2 [21^ 15] +\n", + "0.2 [21^ 17] +\n", + "-0.3 [21^ 19] +\n", + "-0.6 [21^ 21] +\n", + "0.2 [21^ 23] +\n", + "-0.1 [22^ 0] +\n", + "-0.1 [22^ 2] +\n", + "-0.2 [22^ 4] +\n", + "-0.2 [22^ 10] +\n", + "-0.3 [22^ 16] +\n", + "0.2 [22^ 18] +\n", + "0.2 [22^ 20] +\n", + "-0.6 [22^ 22] +\n", + "-0.1 [23^ 1] +\n", + "-0.1 [23^ 3] +\n", + "-0.2 [23^ 5] +\n", + "-0.2 [23^ 11] +\n", + "-0.3 [23^ 17] +\n", + "0.2 [23^ 19] +\n", + "0.2 [23^ 21] +\n", + "-0.6 [23^ 23]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t1 = 0.02\n", + "t2 = 0.06\n", + "t3 = 0.03\n", + "t4 = -0.01\n", + "t5 = 0.2\n", + "t6 = 0.3\n", + "t7 = -0.2\n", + "t8 = -t7/2\n", + "mu = 1\n", + "delta = 0.4\n", + "nx_to_three_orbital_hamiltonian(g_example, t1, t2, t3, t4, t5, t6, t7, t8, mu, delta)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "g_current_limit = generate_three_orbital_nx(6,7)\n", + "g_ideal = generate_three_orbital_nx(20,20)\n", + "\n", + "##### START UNCOMMENT FOR TESTING\n", + "n_test = 2\n", + "g_current_limit = generate_three_orbital_nx(n_test,n_test) \n", + "g_ideal = generate_three_orbital_nx(n_test,n_test)\n", + "##### END UNCOMMENT FOR TESTING\n", + "n_qubits_current_limit = len(g_current_limit)\n", + "n_qubits_ideal = len(g_ideal)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have the Hamiltonians for both the model which constitutes the current limit of classical solvers, and the ideal capability of a solver, we can perform resource estimation for the Hamiltonians. As with the single qubit model, we need to get a decent state initialization using Hartree-Fock. As above, this has not been implemented at this time, but should have low depth in the quantum circuit since Hartree-Fock outputs a product state." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "#TODO: Incorporate Hartree-Fock into this section to prepare the initial state for QPE for GSEE.\n", + "#This should provide a low depth initialization circuit relative to the depth of the QPE, while giving access to a low-energy subspace" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Assuming that we have the output from the Hartree-Fock simulation, we may now perform QPE as above. Currently we are using a short evolution time and a second order trotterization with a single step. We will use scaling arguments to determine the final resources since generating the full circuit for a large number of trotter steps with many bits of precision is quite costly." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "ham_current_limit = nx_to_three_orbital_hamiltonian(g_current_limit,t1,t2,t3,t4,t5,t6,t7,t8,mu,delta)\n", + "ham_ideal = nx_to_three_orbital_hamiltonian(g_ideal,t1,t2,t3,t4,t5,t6,t7,t8,mu,delta)\n", + "trotter_order_current_limit = 2\n", + "trotter_steps_current_limit = 1 #Using one trotter step for a strict lower bound with this method\n", + "\n", + "trotter_order_ideal = 2\n", + "trotter_steps_ideal = 1 #Using one trotter step for a strict lower bound with this method\n", + "\n", + "bits_precision_ideal = 16\n", + "bits_precision_current_limit = 16\n", + "\n", + "E_min_ideal = -len(ham_ideal.terms)\n", + "E_max_ideal = 0\n", + "omega_ideal = E_max_ideal-E_min_ideal\n", + "t_ideal = 2*np.pi/omega_ideal\n", + "phase_offset_ideal = E_max_ideal*t_ideal\n", + "\n", + "E_min_current_limit = -len(ham_current_limit.terms)\n", + "E_max_current_limit = 0\n", + "omega_current_limit = E_max_current_limit-E_min_current_limit\n", + "t_current_limit = 2*np.pi/omega_current_limit\n", + "phase_offset_current_limit = E_max_current_limit*t_current_limit\n", + "\n", + "init_state_ideal = [0] * n_qubits_ideal\n", + "init_state_current_limit = [0] * n_qubits_current_limit\n", + "\n", + "current_limit_args = {\n", + " 'trotterize' : True,\n", + " 'mol_ham' : ham_current_limit,\n", + " 'ev_time' : t_current_limit,\n", + " 'trot_ord' : trotter_order_current_limit,\n", + " 'trot_num' : 1\n", + "}\n", + "\n", + "ideal_args = {\n", + " 'trotterize' : True,\n", + " 'mol_ham' : ham_ideal,\n", + " 'ev_time' : t_ideal,\n", + " 'trot_ord' : trotter_order_ideal,\n", + " 'trot_num' : 1\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "metadata_current_limit = EstimateMetaData(\n", + " id=3000,\n", + " name='FermiHubbard_ideal_Current_Limit`',\n", + " category='scientific',\n", + " size=f'{6}x{7}',\n", + " task='Ground State Energy Estimation',\n", + " implementations=f'GSEE, evolution_time={t_current_limit}, bits_precision={bits_precision_current_limit}, trotter_order={trotter_order_current_limit}',\n", + ")\n", + "\n", + "metadata_ideal = EstimateMetaData(\n", + " id=4000,\n", + " name='FermiHubbard_ideal_Ideal',\n", + " category='scientific',\n", + " size=f'{20}x{20}',\n", + " task='Ground State Energy Estimation',\n", + " implementations=f'GSEE, evolution_time={t_ideal}, bits_precision={bits_precision_ideal}, trotter_order={trotter_order_ideal}',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Estimating Current Limit\n", + "Time to generate circuit for GSEE: 4.704200546257198e-05 seconds\n", + " Time to decompose high level Graph: return new_graph -def generate_two_orbital_nx(Lx: int, Ly: int) -> Graph: +def generate_two_orbital_nx(Lx: int, Ly: int) -> MultiGraph: # can combine logic between loops if this is slow - g = Graph() + g = MultiGraph() for m in range(Lx): for n in range(Ly): for a in range(2): @@ -59,20 +59,107 @@ def generate_two_orbital_nx(Lx: int, Ly: int) -> Graph: if n4 in g: g.add_edge(n3, n4, label="-t3") - n1, n2 = (m, n, 0, s), (m + 1, n, 0, s) + + # +t_4 terms + n1, n2 = (m, n, 0, s), (m + 1, n + 1, 1, s) + n3, n4 = (m, n, 1, s), (m + 1, n + 1, 0, s) + if n2 in g: + g.add_edge(n1, n2, label="+t4") + if n4 in g: + g.add_edge(n3, n4, label="+t4") + + # -t4 terms + n1, n2 = (m, n, 0, s), (m + 1, n - 1, 1, s) + n3, n4 = (m, n, 1, s), (m + 1, n - 1, 0, s) + if n2 in g: + g.add_edge(n1, n2, label="-t4") + if n4 in g: + g.add_edge(n3, n4, label="-t4") + return g + + +def nx_to_two_orbital_hamiltonian( + graph: MultiGraph, + t1: float, + t2: float, + t3: float, + t4: float, + mu: float) -> FermionOperator: + g_flat = flatten_nx_graph(graph) + H = FermionOperator() + + # generating hopping terms on each edge + for i, j, d in g_flat.edges(data=True): + w = 0 + label = d['label'] + if label == "-t1": + w = -t1 + elif label == "-t2": + w = -t2 + elif label == "-t3": + w = -t3 + elif label == "-t4": + w = -t4 + elif label == "+t4": + w = t4 + else: + raise ValueError("Graph improperly labeled") + + H += FermionOperator(((i, 1), (j, 0)), w) + H += FermionOperator(((j, 1), (i, 0)), w) + + # applying number operator to each qubit + for i in g_flat.nodes: + H += FermionOperator(((i, 1), (i, 0)), -mu) + + return H + +def generate_xz_yz_hamiltonian_nx(Lx: int, Ly: int) -> Graph: + #xz is given orbital label 0, yz is given orbital label 1 + g = MultiGraph() + for m in range(Lx): + for n in range(Ly): + for a in range(2): + for s in range(2): + g.add_node((m, n, a, s), pos=( + m + a * (Lx + 1), n + s * (Ly + 1)), + delta=False) + + for m in range(Lx): + for n in range(Ly): + for s in range(2): + # t_1 terms + n1, n2 = (m, n, 0, s), (m, n + 1, 0, s) n3, n4 = (m, n, 1, s), (m + 1, n, 1, s) + if n2 in g: + g.add_edge(n1, n2, label="-t1") + if n4 in g: + g.add_edge(n3, n4, label="-t1") + + # t_2 terms + n1, n2 = (m, n, 0, s), (m + 1, n, 0, s) + n3, n4 = (m, n, 1, s), (m, n + 1, 1, s) + if n2 in g: + g.add_edge(n1, n2, label="-t2") + if n4 in g: + g.add_edge(n3, n4, label="-t2") + + # t_3 terms + n1, n2 = (m, n, 0, s), (m + 1, n + 1, 0, s) + n3, n4 = (m, n, 1, s), (m + 1, n + 1, 1, s) if n2 in g: g.add_edge(n1, n2, label="-t3") if n4 in g: g.add_edge(n3, n4, label="-t3") - n1, n2 = (m, n, 0, s), (m, n + 1, 0, s) - n3, n4 = (m, n, 1, s), (m, n + 1, 1, s) + n1, n2 = (m, n, 0, s), (m + 1, n - 1, 0, s) + n3, n4 = (m, n, 1, s), (m + 1, n - 1, 1, s) if n2 in g: g.add_edge(n1, n2, label="-t3") if n4 in g: g.add_edge(n3, n4, label="-t3") + # +t_4 terms n1, n2 = (m, n, 0, s), (m + 1, n + 1, 1, s) n3, n4 = (m, n, 1, s), (m + 1, n + 1, 0, s) @@ -90,14 +177,168 @@ def generate_two_orbital_nx(Lx: int, Ly: int) -> Graph: g.add_edge(n3, n4, label="-t4") return g +def generate_xy_hamiltonian_nx(Lx: int, Ly: int) -> Graph: + #xy is given orbital label 2 + g = MultiGraph() + for m in range(Lx): + for n in range(Ly): + for a in [2]: + for s in range(2): + #Adding nodes to graph to make edge checking easier + #Also adding node positions for drawing functionality + g.add_node((m, n, a, s), pos=( + m + a * (Lx + 1), n + s * (Ly + 1)), + delta=True) + + for m in range(Lx): + for n in range(Ly): + for s in range(2): + #t_5 terms + n1, n2 = (m, n, 2, s), (m, n + 1, 2, s) + n3, n4 = (m, n, 2, s), (m + 1, n, 2, s) + if n2 in g: + g.add_edge(n1, n2, label="+t5") + if n4 in g: + g.add_edge(n3, n4, label="+t5") -def nx_to_two_orbital_hamiltonian( - graph: Graph, + #t_6 terms + n1, n2 = (m, n, 2, s), (m + 1, n + 1, 2, s) + if n2 in g: + g.add_edge(n1, n2, label="-t6") + + n1, n2 = (m, n, 2, s), (m + 1, n - 1, 2, s) + if n2 in g: + g.add_edge(n1, n2, label="-t6") + return g + +def generate_xz_yz_xy_hamiltonian_nx(Lx: int, Ly: int) -> Graph: + #xz is orbital label 0 + #yz is orbital label 1 + #xy is orbital label 2 + g = MultiGraph() + for m in range(Lx): + for n in range(Ly): + for a in range(3): + for s in range(2): + #Adding nodes to graph to make edge checking easier + #Also adding node positions for drawing functionality + g.add_node((m, n, a, s), pos=( + m + a * (Lx + 1), n + s * (Ly + 1)), + delta=(a==2)) + + for m in range(Lx): + for n in range(Ly): + for s in range(2): + #t_7 terms + n1, n2 = (m, n, 0, s), (m + 1, n, 2, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t7") + else: + g.add_edge(n1, n2, label="+t7") + + n1, n2 = (m, n, 2, s), (m + 1, n, 0, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t7") + else: + g.add_edge(n1, n2, label="+t7") + + n1, n2 = (m, n, 1, s), (m, n + 1, 2, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t7") + else: + g.add_edge(n1, n2, label="+t7") + + n1, n2 = (m, n, 2, s), (m, n + 1, 1, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t7") + else: + g.add_edge(n1, n2, label="+t7") + + #t_8 terms + n1, n2 = (m, n, 0, s), (m + 1, n + 1, 2, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t8") + else: + g.add_edge(n1, n2, label="+t8") + + n1, n2 = (m, n, 2, s), (m + 1, n + 1, 0, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="+t8") + else: + g.add_edge(n1, n2, label="-t8") + + n1, n2 = (m, n, 0, s), (m + 1, n - 1, 2, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t8") + else: + g.add_edge(n1, n2, label="+t8") + + n1, n2 = (m, n, 2, s), (m + 1, n - 1, 0, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="+t8") + else: + g.add_edge(n1, n2, label="-t8") + + n1, n2 = (m, n, 1, s), (m + 1, n + 1, 2, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t8") + else: + g.add_edge(n1, n2, label="+t8") + + n1, n2 = (m, n, 2, s), (m + 1, n + 1, 1, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="+t8") + else: + g.add_edge(n1, n2, label="-t8") + + n1, n2 = (m, n, 1, s), (m + 1, n - 1, 2, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="+t8") + else: + g.add_edge(n1, n2, label="-t8") + + n1, n2 = (m, n, 2, s), (m + 1, n - 1, 1, s) + if n2 in g: + if (m + n) % 2 == 0: + g.add_edge(n1, n2, label="-t8") + else: + g.add_edge(n1, n2, label="+t8") + return g + +def generate_three_orbital_nx(Lx: int, Ly: int) -> MultiGraph: + # can combine logic between loops if this is slow + g_xz_yz = generate_xz_yz_hamiltonian_nx(Lx, Ly) + g_xy = generate_xy_hamiltonian_nx(Lx, Ly) + g_xz_yz_xy = generate_xz_yz_xy_hamiltonian_nx(Lx, Ly) + + g_three_band = compose(g_xz_yz, g_xy) + g_three_band = compose(g_three_band, g_xz_yz_xy) + return g_three_band + +def nx_to_three_orbital_hamiltonian( + graph: MultiGraph, t1: float, t2: float, t3: float, t4: float, - mu: float) -> FermionOperator: + t5: float, + t6: float, + t7: float, + t8: float, + mu: float, + delta: float) -> FermionOperator: + g_flat = flatten_nx_graph(graph) H = FermionOperator() @@ -115,6 +356,18 @@ def nx_to_two_orbital_hamiltonian( w = -t4 elif label == "+t4": w = t4 + elif label == "+t5": + w = t5 + elif label == "-t6": + w = -t6 + elif label == "+t7": + w = t7 + elif label == "-t7": + w = -t7 + elif label == "+t8": + w = t8 + elif label == "-t8": + w = -t8 else: raise ValueError("Graph improperly labeled") @@ -122,8 +375,10 @@ def nx_to_two_orbital_hamiltonian( H += FermionOperator(((j, 1), (i, 0)), w) # applying number operator to each qubit - for i in g_flat.nodes: + for i,d in g_flat.nodes(data=True): H += FermionOperator(((i, 1), (i, 0)), -mu) + if d['delta']: + H += FermionOperator(((i, 1), (i, 0)), delta) return H diff --git a/tests/hamiltonian_utils_test.py b/tests/hamiltonian_utils_test.py index 3e46679..5e2c4b3 100644 --- a/tests/hamiltonian_utils_test.py +++ b/tests/hamiltonian_utils_test.py @@ -17,11 +17,13 @@ def test_triangle_hamiltonian_generation(self): graph_triangle = nx_triangle_lattice(lattice_size=2) assign_directional_triangular_labels(graph_triangle, 2) edge_labels = dict([((n1, n2), d['label']) for n1, n2, d in graph_triangle.edges(data=True)]) - correct_labels = {((0,0), (1,0)): 'Z', - ((0,0), (0,1)): 'X', - ((0,0), (1, 1)): 'Y', - ((0, 1), (1, 1)): 'Z', - ((1, 0), (1, 1)): 'X'} + correct_labels = { + ((0,0), (1,0)): 'Z', + ((0,0), (0,1)): 'X', + ((0,0), (1, 1)): 'Y', + ((0, 1), (1, 1)): 'Z', + ((1, 0), (1, 1)): 'X' + } for edge, label in edge_labels.items(): self.assertTrue(edge in correct_labels.keys()) self.assertEqual(label, correct_labels[edge]) @@ -30,12 +32,14 @@ def test_hexagonal_labels(self): hexagon_graph = hexagonal_lattice_graph(1,1) assign_hexagon_labels(hexagon_graph, 'X', 'Y', 'Z') edge_labels = dict([((n1, n2), d['label']) for n1, n2, d in hexagon_graph.edges(data=True)]) - correct_labels = {((0, 0), (0,1)): 'Y', - ((0, 0), (1, 0)): 'Z', - ((0, 1), (0, 2)): 'X', - ((0, 2), (1, 2)): 'Z', - ((1, 0), (1, 1)): 'X', - ((1, 1), (1, 2)): 'Y'} + correct_labels = { + ((0, 0), (0,1)): 'Y', + ((0, 0), (1, 0)): 'Z', + ((0, 1), (0, 2)): 'X', + ((0, 2), (1, 2)): 'Z', + ((1, 0), (1, 1)): 'X', + ((1, 1), (1, 2)): 'Y' + } for edge, label in edge_labels.items(): self.assertTrue(edge in correct_labels.keys()) self.assertEqual(label, correct_labels[edge]) @@ -72,38 +76,40 @@ def test_two_orbital_gen_test(self): two_orbital = generate_two_orbital_nx(2, 2) edge_labels = dict([((n1, n2), d['label']) for n1, n2, d in two_orbital.edges(data=True)]) - correct_labels = {((0, 0, 0, 0), (0, 1, 0, 0)): '-t3', - ((0, 0, 0, 0), (1, 0, 0, 0)): '-t3', - ((0, 0, 0, 0), (1, 1, 0, 0)): '-t3', - ((0, 0, 0, 0), (1, 1, 1, 0)): '+t4', - ((0, 0, 0, 1), (0, 1, 0, 1)): '-t3', - ((0, 0, 0, 1), (1, 0, 0, 1)): '-t3', - ((0, 0, 0, 1), (1, 1, 0, 1)): '-t3', - ((0, 0, 0, 1), (1, 1, 1, 1)): '+t4', - ((0, 0, 1, 0), (1, 0, 1, 0)): '-t3', - ((0, 0, 1, 0), (0, 1, 1, 0)): '-t3', - ((0, 0, 1, 0), (1, 1, 1, 0)): '-t3', - ((0, 0, 1, 0), (1, 1, 0, 0)): '+t4', - ((0, 0, 1, 1), (1, 0, 1, 1)): '-t3', - ((0, 0, 1, 1), (0, 1, 1, 1)): '-t3', - ((0, 0, 1, 1), (1, 1, 1, 1)): '-t3', - ((0, 0, 1, 1), (1, 1, 0, 1)): '+t4', - ((0, 1, 0, 0), (1, 1, 0, 0)): '-t3', - ((0, 1, 0, 0), (1, 0, 0, 0)): '-t3', - ((0, 1, 0, 0), (1, 0, 1, 0)): '-t4', - ((0, 1, 0, 1), (1, 1, 0, 1)): '-t3', - ((0, 1, 0, 1), (1, 0, 0, 1)): '-t3', - ((0, 1, 0, 1), (1, 0, 1, 1)): '-t4', - ((0, 1, 1, 0), (1, 1, 1, 0)): '-t3', - ((0, 1, 1, 0), (1, 0, 1, 0)): '-t3', - ((0, 1, 1, 0), (1, 0, 0, 0)): '-t4', - ((0, 1, 1, 1), (1, 1, 1, 1)): '-t3', - ((0, 1, 1, 1), (1, 0, 1, 1)): '-t3', - ((0, 1, 1, 1), (1, 0, 0, 1)): '-t4', - ((1, 0, 0, 0), (1, 1, 0, 0)): '-t3', - ((1, 0, 0, 1), (1, 1, 0, 1)): '-t3', - ((1, 0, 1, 0), (1, 1, 1, 0)): '-t3', - ((1, 0, 1, 1), (1, 1, 1, 1)): '-t3'} + correct_labels = { + ((0, 0, 0, 0), (0, 1, 0, 0)): '-t1', + ((0, 0, 0, 0), (1, 0, 0, 0)): '-t2', + ((0, 0, 0, 0), (1, 1, 0, 0)): '-t3', + ((0, 0, 0, 0), (1, 1, 1, 0)): '+t4', + ((0, 0, 0, 1), (0, 1, 0, 1)): '-t1', + ((0, 0, 0, 1), (1, 0, 0, 1)): '-t2', + ((0, 0, 0, 1), (1, 1, 0, 1)): '-t3', + ((0, 0, 0, 1), (1, 1, 1, 1)): '+t4', + ((0, 0, 1, 0), (1, 0, 1, 0)): '-t1', + ((0, 0, 1, 0), (0, 1, 1, 0)): '-t2', + ((0, 0, 1, 0), (1, 1, 1, 0)): '-t3', + ((0, 0, 1, 0), (1, 1, 0, 0)): '+t4', + ((0, 0, 1, 1), (1, 0, 1, 1)): '-t1', + ((0, 0, 1, 1), (0, 1, 1, 1)): '-t2', + ((0, 0, 1, 1), (1, 1, 1, 1)): '-t3', + ((0, 0, 1, 1), (1, 1, 0, 1)): '+t4', + ((0, 1, 0, 0), (1, 1, 0, 0)): '-t2', + ((0, 1, 0, 0), (1, 0, 0, 0)): '-t3', + ((0, 1, 0, 0), (1, 0, 1, 0)): '-t4', + ((0, 1, 0, 1), (1, 1, 0, 1)): '-t2', + ((0, 1, 0, 1), (1, 0, 0, 1)): '-t3', + ((0, 1, 0, 1), (1, 0, 1, 1)): '-t4', + ((0, 1, 1, 0), (1, 1, 1, 0)): '-t1', + ((0, 1, 1, 0), (1, 0, 1, 0)): '-t3', + ((0, 1, 1, 0), (1, 0, 0, 0)): '-t4', + ((0, 1, 1, 1), (1, 1, 1, 1)): '-t1', + ((0, 1, 1, 1), (1, 0, 1, 1)): '-t3', + ((0, 1, 1, 1), (1, 0, 0, 1)): '-t4', + ((1, 0, 0, 0), (1, 1, 0, 0)): '-t1', + ((1, 0, 0, 1), (1, 1, 0, 1)): '-t1', + ((1, 0, 1, 0), (1, 1, 1, 0)): '-t2', + ((1, 0, 1, 1), (1, 1, 1, 1)): '-t2' + } for edge, label in edge_labels.items(): self.assertTrue(edge in correct_labels.keys()) self.assertEqual(label, correct_labels[edge]) @@ -118,87 +124,88 @@ def test_nx_to_two_orbital_hamiltonian(self): 0.85, 1 ) - edge_labels = dict([((n1, n2), d['label']) for n1, n2, d in two_orbital.edges(data=True)]); - correct_fermionic_terms = {((0, 1), (4, 0)): -0.85, - ((4, 1), (0, 0)): -0.85, - ((0, 1), (8, 0)): -0.85, - ((8, 1), (0, 0)): -0.85, - ((0, 1), (12, 0)): -0.85, - ((12, 1), (0, 0)): -0.85, - ((0, 1), (14, 0)): 0.85, - ((14, 1), (0, 0)): 0.85, - ((1, 1), (5, 0)): -0.85, - ((5, 1), (1, 0)): -0.85, - ((1, 1), (9, 0)): -0.85, - ((9, 1), (1, 0)): -0.85, - ((1, 1), (13, 0)): -0.85, - ((13, 1), (1, 0)): -0.85, - ((1, 1), (15, 0)): 0.85, - ((15, 1), (1, 0)): 0.85, - ((2, 1), (10, 0)): -0.85, - ((10, 1), (2, 0)): -0.85, - ((2, 1), (6, 0)): -0.85, - ((6, 1), (2, 0)): -0.85, - ((2, 1), (14, 0)): -0.85, - ((14, 1), (2, 0)): -0.85, - ((2, 1), (12, 0)): 0.85, - ((12, 1), (2, 0)): 0.85, - ((3, 1), (11, 0)): -0.85, - ((11, 1), (3, 0)): -0.85, - ((3, 1), (7, 0)): -0.85, - ((7, 1), (3, 0)): -0.85, - ((3, 1), (15, 0)): -0.85, - ((15, 1), (3, 0)): -0.85, - ((3, 1), (13, 0)): 0.85, - ((13, 1), (3, 0)): 0.85, - ((4, 1), (12, 0)): -0.85, - ((12, 1), (4, 0)): -0.85, - ((4, 1), (8, 0)): -0.85, - ((8, 1), (4, 0)): -0.85, - ((4, 1), (10, 0)): -0.85, - ((10, 1), (4, 0)): -0.85, - ((5, 1), (13, 0)): -0.85, - ((13, 1), (5, 0)): -0.85, - ((5, 1), (9, 0)): -0.85, - ((9, 1), (5, 0)): -0.85, - ((5, 1), (11, 0)): -0.85, - ((11, 1), (5, 0)): -0.85, - ((6, 1), (14, 0)): -0.85, - ((14, 1), (6, 0)): -0.85, - ((6, 1), (10, 0)): -0.85, - ((10, 1), (6, 0)): -0.85, - ((6, 1), (8, 0)): -0.85, - ((8, 1), (6, 0)): -0.85, - ((7, 1), (15, 0)): -0.85, - ((15, 1), (7, 0)): -0.85, - ((7, 1), (11, 0)): -0.85, - ((11, 1), (7, 0)): -0.85, - ((7, 1), (9, 0)): -0.85, - ((9, 1), (7, 0)): -0.85, - ((8, 1), (12, 0)): -0.85, - ((12, 1), (8, 0)): -0.85, - ((9, 1), (13, 0)): -0.85, - ((13, 1), (9, 0)): -0.85, - ((10, 1), (14, 0)): -0.85, - ((14, 1), (10, 0)): -0.85, - ((11, 1), (15, 0)): -0.85, - ((15, 1), (11, 0)): -0.85, - ((0, 1), (0, 0)): -1.0, - ((1, 1), (1, 0)): -1.0, - ((2, 1), (2, 0)): -1.0, - ((3, 1), (3, 0)): -1.0, - ((4, 1), (4, 0)): -1.0, - ((5, 1), (5, 0)): -1.0, - ((6, 1), (6, 0)): -1.0, - ((7, 1), (7, 0)): -1.0, - ((8, 1), (8, 0)): -1.0, - ((9, 1), (9, 0)): -1.0, - ((10, 1), (10, 0)): -1.0, - ((11, 1), (11, 0)): -1.0, - ((12, 1), (12, 0)): -1.0, - ((13, 1), (13, 0)): -1.0, - ((14, 1), (14, 0)): -1.0, - ((15, 1), (15, 0)): -1.0} + correct_fermionic_terms = { + ((0, 1), (4, 0)): 1.0, + ((4, 1), (0, 0)): 1.0, + ((0, 1), (8, 0)): -1.3, + ((8, 1), (0, 0)): -1.3, + ((0, 1), (12, 0)): -0.85, + ((12, 1), (0, 0)): -0.85, + ((0, 1), (14, 0)): 0.85, + ((14, 1), (0, 0)): 0.85, + ((1, 1), (5, 0)): 1.0, + ((5, 1), (1, 0)): 1.0, + ((1, 1), (9, 0)): -1.3, + ((9, 1), (1, 0)): -1.3, + ((1, 1), (13, 0)): -0.85, + ((13, 1), (1, 0)): -0.85, + ((1, 1), (15, 0)): 0.85, + ((15, 1), (1, 0)): 0.85, + ((2, 1), (10, 0)): 1.0, + ((10, 1), (2, 0)): 1.0, + ((2, 1), (6, 0)): -1.3, + ((6, 1), (2, 0)): -1.3, + ((2, 1), (14, 0)): -0.85, + ((14, 1), (2, 0)): -0.85, + ((2, 1), (12, 0)): 0.85, + ((12, 1), (2, 0)): 0.85, + ((3, 1), (11, 0)): 1.0, + ((11, 1), (3, 0)): 1.0, + ((3, 1), (7, 0)): -1.3, + ((7, 1), (3, 0)): -1.3, + ((3, 1), (15, 0)): -0.85, + ((15, 1), (3, 0)): -0.85, + ((3, 1), (13, 0)): 0.85, + ((13, 1), (3, 0)): 0.85, + ((4, 1), (12, 0)): -1.3, + ((12, 1), (4, 0)): -1.3, + ((4, 1), (8, 0)): -0.85, + ((8, 1), (4, 0)): -0.85, + ((4, 1), (10, 0)): -0.85, + ((10, 1), (4, 0)): -0.85, + ((5, 1), (13, 0)): -1.3, + ((13, 1), (5, 0)): -1.3, + ((5, 1), (9, 0)): -0.85, + ((9, 1), (5, 0)): -0.85, + ((5, 1), (11, 0)): -0.85, + ((11, 1), (5, 0)): -0.85, + ((6, 1), (14, 0)): 1.0, + ((14, 1), (6, 0)): 1.0, + ((6, 1), (10, 0)): -0.85, + ((10, 1), (6, 0)): -0.85, + ((6, 1), (8, 0)): -0.85, + ((8, 1), (6, 0)): -0.85, + ((7, 1), (15, 0)): 1.0, + ((15, 1), (7, 0)): 1.0, + ((7, 1), (11, 0)): -0.85, + ((11, 1), (7, 0)): -0.85, + ((7, 1), (9, 0)): -0.85, + ((9, 1), (7, 0)): -0.85, + ((8, 1), (12, 0)): 1.0, + ((12, 1), (8, 0)): 1.0, + ((9, 1), (13, 0)): 1.0, + ((13, 1), (9, 0)): 1.0, + ((10, 1), (14, 0)): -1.3, + ((14, 1), (10, 0)): -1.3, + ((11, 1), (15, 0)): -1.3, + ((15, 1), (11, 0)): -1.3, + ((0, 1), (0, 0)): -1.0, + ((1, 1), (1, 0)): -1.0, + ((2, 1), (2, 0)): -1.0, + ((3, 1), (3, 0)): -1.0, + ((4, 1), (4, 0)): -1.0, + ((5, 1), (5, 0)): -1.0, + ((6, 1), (6, 0)): -1.0, + ((7, 1), (7, 0)): -1.0, + ((8, 1), (8, 0)): -1.0, + ((9, 1), (9, 0)): -1.0, + ((10, 1), (10, 0)): -1.0, + ((11, 1), (11, 0)): -1.0, + ((12, 1), (12, 0)): -1.0, + ((13, 1), (13, 0)): -1.0, + ((14, 1), (14, 0)): -1.0, + ((15, 1), (15, 0)): -1.0 + } for edge, label in correct_fermionic_terms.items(): self.assertTrue(edge in fermionic_two_orbital.terms) self.assertEqual(label, fermionic_two_orbital.terms[edge])