Repository: quantumlib/OpenFermion
Pull request #526 (2019-06-25 02:44:45)

Add is_contextual
Dear OpenFermion developers,

I've added a function is_contextual that takes as an argument a Hamiltonian (QubitOperator) and returns a boolean indicating whether the Hamiltonian is contextual, as described in https://arxiv.org/abs/1904.02260.  Contextuality is a criterion for nonclassicality (see reference above for details), and its absence has been connected with classical simulability (see for example https://arxiv.org/abs/1802.07744 and https://arxiv.org/abs/1905.05374).  I hope you think the added function is worth including; please let me know if I should make any changes.

Cheers,
Will

ncrubin: Thanks @wmkirby1 !  We'll take a look ASAP to see if any changes that need to be made.  Thank you for the PR!
wmkirby1: @babbush thanks! I've added myself the authors lists, but it doesn't look like I can merge it myself now.

Patch:
diff --git a/NOTICE b/NOTICE
index 5ee8c23..d5728be 100644
--- a/NOTICE
+++ b/NOTICE
@@ -20,6 +20,7 @@ Oscar Higgott (University College London)
 Cupjin Huang (University of Michigan)
 Josh Izaac (Xanadu)
 Zhang Jiang (NASA)
+William Kirby (Tufts University)
 Xinle Liu (Google)
 Sam McArdle (Oxford)
 Matthew Neeley (Google)
diff --git a/README.rst b/README.rst
index d299a9b..b27c9cf 100644
--- a/README.rst
+++ b/README.rst
@@ -145,6 +145,7 @@ Authors
 `Cupjin Huang <https://github.com/pertoX4726>`__ (University of Michigan),
 `Josh Izaac <https://github.com/josh146>`__ (Xanadu),
 `Zhang Jiang <https://ti.arc.nasa.gov/profile/zjiang3>`__ (NASA),
+`William Kirby <https://williammkirby.com>`__ (Tufts University),
 `Xinle Liu <https://github.com/sheilaliuxl>`__ (Google),
 `Sam McArdle <https://github.com/sammcardle30>`__ (Oxford),
 `Matthew Neeley <https://github.com/maffoo>`__ (Google),
@@ -173,7 +174,7 @@ When using OpenFermion for research projects, please cite:
     Jarrod R. McClean, Kevin J. Sung, Ian D. Kivlichan, Yudong Cao,
     Chengyu Dai, E. Schuyler Fried, Craig Gidney, Brendan Gimby,
     Pranav Gokhale, Thomas Häner, Tarini Hardikar, Vojtĕch Havlíček,
-    Oscar Higgott, Cupjin Huang, Josh Izaac, Zhang Jiang, Xinle Liu,
+    Oscar Higgott, Cupjin Huang, Josh Izaac, Zhang Jiang, William Kirby, Xinle Liu,
     Sam McArdle, Matthew Neeley, Thomas O'Brien, Bryan O'Gorman, Isil Ozfidan,
     Maxwell D. Radin, Jhonathan Romero, Nicholas Rubin, Nicolas P. D. Sawaya,
     Kanav Setia, Sukin Sim, Damian S. Steiger, Mark Steudtner, Qiming Sun,
diff --git a/src/openfermion/utils/_operator_utils.py b/src/openfermion/utils/_operator_utils.py
index bb8b212..dc7af40 100644
--- a/src/openfermion/utils/_operator_utils.py
+++ b/src/openfermion/utils/_operator_utils.py
@@ -960,3 +960,36 @@ def group_into_tensor_product_basis_sets(operator, seed=None):
             sub_operators[basis] = sub_operator
 
     return sub_operators
+
+  
+def _commutes(operator1,operator2):
+    return operator1*operator2==operator2*operator1
+
+def _non_fully_commuting_terms(hamiltonian):
+    terms = list([QubitOperator(key) for key in hamiltonian.terms.keys()])
+    T=[] # will contain the subset of terms that do not commute universally in terms                                                                                                 
+    for i in range(len(terms)):
+        if any(not _commutes(terms[i],terms[j]) for j in range(len(terms))):
+            T.append(terms[i])
+    return T
+
+def is_contextual(hamiltonian):
+    """                                                                                                                                                                              
+    Determine whether a hamiltonian (instance of QubitOperator) is contextual,                                                                                                       
+    in the sense of https://arxiv.org/abs/1904.02260.                                                                                                                                
+                                                                                                                                                                                     
+    Args:                                                                                                                                                                            
+        hamiltonian (QubitOperator): the hamiltonian whose                                                                                                                           
+            contextuality is to be evaluated.                                                                                                                                        
+                                                                                                                                                                                     
+    Returns:                                                                                                                                                                         
+        boolean indicating whether hamiltonian is contextual or not.                                                                                                                 
+    """
+    T = _non_fully_commuting_terms(hamiltonian)
+    # Search in T for triples in which exactly one pair anticommutes; if any exist, hamiltonian is contextual.                                                                       
+    for i in range(len(T)): # WLOG, i indexes the operator that (putatively) commutes with both others.                                                                              
+        for j in range(len(T)):
+            for k in range(j+1,len(T)): # Ordering of j, k does not matter.                                                                                                          
+                if i!=j and i!=k and _commutes(T[i],T[j]) and _commutes(T[i],T[k]) and not _commutes(T[j],T[k]):
+                    return True
+    return False  


Test patch:
diff --git a/src/openfermion/utils/_operator_utils_test.py b/src/openfermion/utils/_operator_utils_test.py
index dfcca0b..612467b 100644
--- a/src/openfermion/utils/_operator_utils_test.py
+++ b/src/openfermion/utils/_operator_utils_test.py
@@ -938,3 +938,37 @@ class GroupTensorProductBasisTest(unittest.TestCase):
     def test_none_bad_type(self):
         with self.assertRaises(TypeError):
             _ = group_into_tensor_product_basis_sets(None)
+
+
+class IsContextualTest(unittest.TestCase):
+
+    def setUp(self):
+        self.x1 = QubitOperator('X1',1.)
+        self.x2 = QubitOperator('X2',1.)
+        self.x3 = QubitOperator('X3',1.)
+        self.x4 = QubitOperator('X4',1.)
+        self.z1 = QubitOperator('Z1',1.)
+        self.z2 = QubitOperator('Z2',1.)
+        self.x1x2 = QubitOperator('X1 X2',1.)
+        self.y1y2 = QubitOperator('Y1 Y2',1.)
+
+    def test_empty_qubit_operator(self):
+        self.assertFalse(is_contextual(QubitOperator()))
+
+    def test_noncontextual_two_qubit_hamiltonians(self):
+        self.assertFalse(is_contextual(self.x1 + self.x2))
+        self.assertFalse(is_contextual(self.x1 + self.x2 + self.z2))
+        self.assertFalse(is_contextual(self.x1 + self.x2 + self.y1y2))
+
+    def test_contextual_two_qubit_hamiltonians(self):
+        self.assertTrue(is_contextual(self.x1 + self.x2 + self.z1 + self.z2))
+        self.assertTrue(is_contextual(self.x1 + self.x1x2 + self.z1 + self.z2))
+        self.assertTrue(is_contextual(self.x1 + self.y1y2 + self.z1 + self.z2))
+
+    def test_contextual_hamiltonians_with_extra_terms(self):
+        self.assertTrue(is_contextual(self.x1 + self.x2 + self.z1 + self.z2 + self.x3 + self.x4))
+        self.assertTrue(is_contextual(self.x1 + self.x1x2 + self.z1 + self.z2 + self.x3 + self.x4))
+        self.assertTrue(is_contextual(self.x1 + self.y1y2 + self.z1 + self.z2 + self.x3 + self.x4))
+
+    def test_commuting_hamiltonian(self):
+        self.assertFalse(is_contextual(self.x1 + self.x2 + self.x3 + self.x4))