Skip to content

Commit a041f67

Browse files
authored
Fix 2 problems in _multitensor.py (#1293)
The `dual_basis` parameter to the `MultiTensor` class constructor was defaulted to `DualBasis()`. In Python, default arguments are evaluated only once at definition time, meaning every `MultiTensor` instance created without an explicit basis shared the same global `DualBasis` object. When tests were run in parallel or repeated, data from one test would leak into others, leading to errors. In addition, `MultiTensor.add_dual_elements` used `list.extend()` instead of `list.append()` when adding `DualBasisElement` objects. Since `DualBasisElement` is iterable (yielding its internal terms), using `extend()` incorrectly decomposed the object into its constituent tuples instead of adding the object as a single unit. This caused `MultiTensor.synthesize_dual_basis()` to fail in some cases.
1 parent 97d925a commit a041f67

2 files changed

Lines changed: 32 additions & 4 deletions

File tree

src/openfermion/contrib/representability/_multitensor.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def __iter__(self):
2323

2424

2525
class MultiTensor(object):
26-
def __init__(self, tensors, dual_basis=DualBasis()):
26+
def __init__(self, tensors, dual_basis=None):
2727
"""
2828
A collection of tensor objects with maps from name to tensor
2929
@@ -46,6 +46,8 @@ def __init__(self, tensors, dual_basis=DualBasis()):
4646
self.off_set_map = self.make_offset_dict(self.tensors)
4747

4848
# An iterable object that provides access to the dual basis elements
49+
if dual_basis is None:
50+
dual_basis = DualBasis()
4951
self.dual_basis = dual_basis
5052
self.vec_dim = sum([vec.size for vec in self.tensors])
5153

@@ -81,8 +83,7 @@ def add_dual_elements(self, dual_element):
8183
if not isinstance(dual_element, DualBasisElement):
8284
raise TypeError("dual_element variable needs to be a DualBasisElement type")
8385

84-
# we should extend TMap to add
85-
self.dual_basis.elements.extend(dual_element)
86+
self.dual_basis.elements.append(dual_element)
8687

8788
def synthesize_dual_basis(self):
8889
"""
@@ -93,7 +94,7 @@ def synthesize_dual_basis(self):
9394
9495
:returns: sparse matrix
9596
"""
96-
# go throught the dual basis list and synthesize each element
97+
# go through the dual basis list and synthesize each element
9798
dual_row_indices = []
9899
dual_col_indices = []
99100
dual_data_values = []

src/openfermion/contrib/representability/_multitensor_test.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,33 @@ def test_add_dualelement():
9494
mt.add_dual_elements(dbe)
9595
assert len(mt.dual_basis) == 1
9696

97+
dbe2 = DualBasisElement()
98+
dbe2.add_element('b', (0, 1, 2), 5)
99+
mt.add_dual_elements(dbe2)
100+
assert len(mt.dual_basis) == 2
101+
102+
A, bias, scalar = mt.synthesize_dual_basis()
103+
assert A.shape[0] == 2
104+
# Verify that the elements are correctly added as DualBasisElements
105+
# and not as their internal tuples (which would cause synthesis to fail).
106+
assert isinstance(mt.dual_basis[0], DualBasisElement)
107+
assert isinstance(mt.dual_basis[1], DualBasisElement)
108+
109+
110+
def test_multitensor_init_isolation():
111+
# Test that different MultiTensor instances don't share the same dual_basis.
112+
a = np.random.random((2, 2))
113+
at = Tensor(tensor=a, name='a')
114+
mt1 = MultiTensor([at])
115+
mt2 = MultiTensor([at])
116+
117+
dbe = DualBasisElement()
118+
dbe.add_element('a', (0, 0), 1.0)
119+
mt1.add_dual_elements(dbe)
120+
121+
assert len(mt1.dual_basis) == 1
122+
assert len(mt2.dual_basis) == 0
123+
97124

98125
def test_synthesis_element():
99126
a = np.random.random((5, 5))

0 commit comments

Comments
 (0)