Repository: quantumlib/OpenFermion
Pull request #29 (2017-10-12 13:34:52)

Meanfield dwave
It's useful to me to have modules for constructing Hamiltonian models other than the Hubbard. This one contains one for the BCS meanfield dwave model, and my intention is for it to contain other meanfield models. I'm new to the terminology so maybe some names of things need to be changed. I can also add more tests if necessary. A lot of this code is adapted from _hubbard.py and _hubbard_test.py.

babbush: This module looks great! I am in Korea right now (where it is midnight) and so I'll need to take a closer look tomorrow. Things look good to go for the most part but it seems that test coverage is decreasing slightly. Our standards for test coverage are admittedly obnoxiously high but it looks like you'll need to test about one additional line of code to pass the threshold we set in coveralls (I believe right now the max you can decrease coverage by is 0.01% and this one decreases by 0.02%). Using the pytest-cov module (which needs be pip installed separately from pytest) you can figure out which lines of code are being missed by your tests. Note that if you wanted to (for some reason) you could also bring up test coverage on some other part of the library to correct the discrepancy.
googlebot: So there's good news and bad news.

:thumbsup: The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so.  Everything is all good there.

:confused: The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter.  We need to confirm that they're okay with their commits being contributed to this project.  Please have them confirm that here in the pull request.

*Note to project maintainer: This is a terminal state, meaning the `cla/google` commit status will not change from this State. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.*

<!-- need_author_consent -->
babbush: Errr. So the CLA bot has gone wild. Kevin's CLAs were approved before I commented on this pull request but now that I have, CLA bot has decided to block the PR. @dabacon can you help us with this? There is definitely something wrong. The CLA bot has also made a similarly illogical decision on Kevin's other pull request.
kevinsung: Thank you for the comments. This is my first time contributing to an open source project and I appreciate the nitpicking!

Patch:
diff --git a/README.rst b/README.rst
index a19acbd..a18c3af 100644
--- a/README.rst
+++ b/README.rst
@@ -85,7 +85,8 @@ Wei Sun (Google),
 Craig Gidney (Google),
 Thomas Haner (ETH Zurich),
 Hannah Sim (Harvard),
-Yudong Cao (Harvard)
+Yudong Cao (Harvard),
+Kevin Sung (University of Michigan),
 Vojtech Havlicek (Oxford),
 Kanav Setia (Dartmouth),
 Nicholas Rubin (Rigetti),
diff --git a/src/openfermion/utils/__init__.py b/src/openfermion/utils/__init__.py
index 3d89954..df67604 100644
--- a/src/openfermion/utils/__init__.py
+++ b/src/openfermion/utils/__init__.py
@@ -26,6 +26,8 @@ from ._jellium import (dual_basis_kinetic,
                        plane_wave_kinetic,
                        plane_wave_potential)
 
+from ._meanfield import meanfield_dwave
+
 from ._dual_basis_trotter_error import (dual_basis_error_bound,
                                         dual_basis_error_operator)
 
diff --git a/src/openfermion/utils/_meanfield.py b/src/openfermion/utils/_meanfield.py
new file mode 100644
index 0000000..0fce480
--- /dev/null
+++ b/src/openfermion/utils/_meanfield.py
@@ -0,0 +1,118 @@
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""This module contains constructions for mean-field Hamiltonian models.
+
+The mean-field d-wave model has the form
+
+H = - tunneling sum_{<i,j>} sum_sigma (a^dagger_{i, sigma} a_{j, sigma}
+  + a^dagger_{j, sigma} a_{i, sigma})
+  - sum_{<i,j>} Delta_{ij}
+  (a^dagger_{i, up} a^dagger_{j, down} - a^dagger_{i, down} a^dagger_{j, up}
+  + a_{j, down} a_{i, up} - a_{j, up} a_{i, down})
+
+where Delta_{ij} = +sc_gap/2 for horizontal edges and -sc_gap/2 for vertical
+edges.
+
+There are N sites and 2*N spin-orbitals. The operators a^dagger_i and a_i are
+fermionic creation and annihilation operators. One can transform these
+operators to qubit operator using the Jordan-Wigner transmation:
+
+a^dagger_j = 0.5 (X - i Y) prod_{k = 1}^{j - 1} Z_k
+a_j = 0.5 (X + i Y) prod_{k = 1}^{j - 1} Z_k
+
+These Hamiltonians live on a square lattice which has dimensions of
+x_dimension by y_dimension. They can have periodic boundary conditions or not.
+"""
+from __future__ import absolute_import
+
+from openfermion.ops import (FermionOperator,
+                             hermitian_conjugated)
+from openfermion.utils._hubbard import up, down
+
+
+def meanfield_dwave(x_dimension, y_dimension, tunneling, sc_gap,
+                    periodic=True, verbose=False):
+    """Return symbolic representation of a BCS mean-field d-wave Hamiltonian.
+
+    Args:
+        x_dimension: An integer giving the number of sites in width.
+        y_dimension: An integer giving the number of sites in height.
+        tunneling: A float giving the tunneling amplitude.
+        sc_gap: A float giving the magnitude of the superconducting gap.
+        periodic: If True, add periodic boundary conditions.
+        verbose: An optional Boolean. If True, print all second quantized
+            terms.
+
+    Returns:
+        meanfield_dwave_model: An instance of the FermionOperator class.
+    """
+    # Initialize fermion operator class.
+    n_sites = x_dimension * y_dimension
+    n_spin_orbitals = 2 * n_sites
+    meanfield_dwave_model = FermionOperator()
+
+    # Loop through sites and add terms.
+    for site in range(n_sites):
+        # Index coupled orbitals.
+        right_neighbor = site + 1
+        bottom_neighbor = site + x_dimension
+        # Account for periodic boundaries.
+        if periodic:
+            if (x_dimension > 2) and ((site + 1) % x_dimension == 0):
+                right_neighbor -= x_dimension
+            if (y_dimension > 2) and (site + x_dimension + 1 > n_sites):
+                bottom_neighbor -= x_dimension * y_dimension
+
+        # Add transition to neighbor on right
+        if (site + 1) % x_dimension or (periodic and x_dimension > 2):
+            # Add spin-up hopping term.
+            operators = ((up(site), 1.), (up(right_neighbor), 0.))
+            hopping_term = FermionOperator(operators, -tunneling)
+            meanfield_dwave_model += hopping_term
+            meanfield_dwave_model += hermitian_conjugated(hopping_term)
+            # Add spin-down hopping term
+            operators = ((down(site), 1.), (down(right_neighbor), 0.))
+            hopping_term = FermionOperator(operators, -tunneling)
+            meanfield_dwave_model += hopping_term
+            meanfield_dwave_model += hermitian_conjugated(hopping_term)
+
+            # Add pairing term
+            operators = ((up(site), 1.), (down(right_neighbor), 1.))
+            pairing_term = FermionOperator(operators, sc_gap / 2.)
+            operators = ((down(site), 1.), (up(right_neighbor), 1.))
+            pairing_term += FermionOperator(operators, -sc_gap / 2.)
+            meanfield_dwave_model -= pairing_term
+            meanfield_dwave_model -= hermitian_conjugated(pairing_term)
+
+        # Add transition to neighbor below.
+        if site + x_dimension + 1 <= n_sites or (periodic and y_dimension > 2):
+            # Add spin-up hopping term.
+            operators = ((up(site), 1.), (up(bottom_neighbor), 0.))
+            hopping_term = FermionOperator(operators, -tunneling)
+            meanfield_dwave_model += hopping_term
+            meanfield_dwave_model += hermitian_conjugated(hopping_term)
+            # Add spin-down hopping term
+            operators = ((down(site), 1.), (down(bottom_neighbor), 0.))
+            hopping_term = FermionOperator(operators, -tunneling)
+            meanfield_dwave_model += hopping_term
+            meanfield_dwave_model += hermitian_conjugated(hopping_term)
+
+            # Add pairing term
+            operators = ((up(site), 1.), (down(bottom_neighbor), 1.))
+            pairing_term = FermionOperator(operators, -sc_gap / 2.)
+            operators = ((down(site), 1.), (up(bottom_neighbor), 1.))
+            pairing_term += FermionOperator(operators, sc_gap / 2.)
+            meanfield_dwave_model -= pairing_term
+            meanfield_dwave_model -= hermitian_conjugated(pairing_term)
+    # Return.
+    return meanfield_dwave_model


Test patch:
diff --git a/src/openfermion/utils/_meanfield_test.py b/src/openfermion/utils/_meanfield_test.py
new file mode 100644
index 0000000..ab0b6fd
--- /dev/null
+++ b/src/openfermion/utils/_meanfield_test.py
@@ -0,0 +1,123 @@
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""Tests for mean-field d-wave model module."""
+from __future__ import absolute_import
+
+import unittest
+
+from openfermion.utils import meanfield_dwave
+
+
+class MeanfieldDwaveTest(unittest.TestCase):
+
+    def setUp(self):
+        self.tunneling = 2.
+        self.sc_gap = 2.
+
+    def test_two_by_two(self):
+
+        # Initialize the Hamiltonian.
+        meanfield_dwave_model = meanfield_dwave(
+            2, 2, self.tunneling, self.sc_gap)
+
+        # Check up right/left hopping terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((0, 1), (2, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((2, 1), (0, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((4, 1), (6, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((6, 1), (4, 0))],
+                               -self.tunneling)
+
+        # Check up top/bottom hopping terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((0, 1), (4, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((4, 1), (0, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((2, 1), (6, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((6, 1), (2, 0))],
+                               -self.tunneling)
+
+        # Check down right/left hopping terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((1, 1), (3, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((3, 1), (1, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((5, 1), (7, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((7, 1), (5, 0))],
+                               -self.tunneling)
+
+        # Check down top/bottom hopping terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((1, 1), (5, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((5, 1), (1, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((3, 1), (7, 0))],
+                               -self.tunneling)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((7, 1), (3, 0))],
+                               -self.tunneling)
+
+        # Check right/left pairing terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((0, 1), (3, 1))],
+                               -self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((1, 1), (2, 1))],
+                               self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((3, 0), (0, 0))],
+                               -self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((2, 0), (1, 0))],
+                               self.sc_gap / 2)
+
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((4, 1), (7, 1))],
+                               -self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((5, 1), (6, 1))],
+                               self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((7, 0), (4, 0))],
+                               -self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((6, 0), (5, 0))],
+                               self.sc_gap / 2)
+
+        # Check top/bottom pairing terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((0, 1), (5, 1))],
+                               self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((1, 1), (4, 1))],
+                               -self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((5, 0), (0, 0))],
+                               self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((4, 0), (1, 0))],
+                               -self.sc_gap / 2)
+
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((2, 1), (7, 1))],
+                               self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((3, 1), (6, 1))],
+                               -self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((7, 0), (2, 0))],
+                               self.sc_gap / 2)
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((6, 0), (3, 0))],
+                               -self.sc_gap / 2)
+
+    def test_two_by_three_spinless_periodic_rudimentary(self):
+        meanfield_dwave_model = meanfield_dwave(2, 3,
+                                                self.tunneling, self.sc_gap)
+        # Check top/bottom hopping terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((8, 1), (1, 1))],
+                               self.sc_gap / 2)
+
+    def test_three_by_two_spinless_periodic_rudimentary(self):
+        meanfield_dwave_model = meanfield_dwave(3, 2,
+                                                self.tunneling, self.sc_gap)
+        # Check right/left hopping terms.
+        self.assertAlmostEqual(meanfield_dwave_model.terms[((4, 1), (1, 1))],
+                               -self.sc_gap / 2)