Fix 2 problems in _multitensor.py#1293
Conversation
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.
There was a problem hiding this comment.
Code Review
This pull request addresses a bug in the MultiTensor class where a mutable default argument for dual_basis caused shared state between instances. The constructor now correctly initializes a new DualBasis if none is provided. Additionally, the add_dual_elements method was updated to use append instead of extend, and a typo was corrected in the documentation. New test cases verify that MultiTensor instances are isolated and that dual elements are correctly added and synthesized. I have no feedback to provide.
|
|
||
| # we should extend TMap to add | ||
| self.dual_basis.elements.extend(dual_element) | ||
| self.dual_basis.elements.append(dual_element) |
There was a problem hiding this comment.
can you update or remove the comment above this.
Are we sure add is the intended operation here?
There was a problem hiding this comment.
The function name is plural but the argument name is singular. The docstring says "add" but the inline comment says "extend". maybe this was originally an extend-type operation and was changed to an append-type?
There was a problem hiding this comment.
can you update or remove the comment above this.
Ah, yes, good point. Thanks for catching that. Fixed in the next push.
The function name is plural but the argument name is singular. The docstring says "add" but the inline comment says "extend". maybe this was originally an extend-type operation and was changed to an append-type?
Yeah, something probably happened there. I also think the bug was never caught before now because the tests in _multitensor_test.py were incomplete. (I only ran into the problem because I was working on getting parallel Pytest working, which revealed the problem of initializing MultiTensor with a mutable object, which led to more poking around.)
Here's what's happening:
-
DualBasisElementobjects are iterable because they implement the__iter__()magic method (c.f._dualbasis.py). Each call to that method returns a tuple of 3 items. -
In Python, doing
somelist.extend(someobject)invokes the__iter__method onsomeobject. On aDualBasisElementobject, doing that ends up adding a tuple of 3 items tosomelist. That's in contrast with the operationsomelist.append(someobject), which puts theDualBasisElementobject itself onsomelist. -
The previous
_multitensor_test.pyonly tested adding an item to the listdual_basis.elements, and never tested using the results. The methodsynthesize_dual_basis()eventually calls thesynthesize_element()method on each item on the list, and therein lies the problem:synthesize_element()does a loop like this:for tlabel, velement, coeff in element:
That works as expected if the values on the
elementslist inDualBasisare actualDualBasisElementobjects because their__iter__()method will get called in theforloop (yielding the 3 items above), but that won't happen if the values on theelementslist are tuples.
The
dual_basisparameter to theMultiTensorclass constructor was defaulted toDualBasis(). In Python, default arguments are evaluated only once at definition time, meaning everyMultiTensorinstance created without an explicit basis shared the same globalDualBasisobject. When tests were run in parallel or repeated, data from one test would leak into others, leading to errors.In addition,
MultiTensor.add_dual_elementsusedlist.extend()instead oflist.append()when addingDualBasisElementobjects. SinceDualBasisElementis iterable (yielding its internal terms), usingextend()incorrectly decomposed the object into its constituent tuples instead of adding the object as a single unit. This causedMultiTensor.synthesize_dual_basis()to fail in some cases.