Skip to content

Don't allow *dop objects to be mutated, and correct the behavior of zero Dop / Sdop#151

Merged
utensil merged 2 commits intopygae:masterfrom
eric-wieser:fix-dop-mutable-state
Dec 17, 2019
Merged

Don't allow *dop objects to be mutated, and correct the behavior of zero Dop / Sdop#151
utensil merged 2 commits intopygae:masterfrom
eric-wieser:fix-dop-mutable-state

Conversation

@eric-wieser
Copy link
Member

@eric-wieser eric-wieser commented Dec 16, 2019

As discovered in gh-150, printing out a laplacian changes the effect of the operator!
In order to ensure immutability, this:

  • Changes the following from list to tuple:
  • Sdop.terms
  • Pdop.terms
  • Changes the following functions to return a new copy, rather than mutating in place and returning themselves:
    • Sdop.simplify
    • Pdop.simplify
    • Pdop.factor
    • Dop.consolidate_coefs
  • Changes the following undocumented and unused functions to return a new copy, rather than mutating in place and returning None:
    • Sdop.TSimplify
    • Pdop.TSimplify
    • Sdop.sort_terms, which has been made private and renamed to _with_sorted_terms

Crucially, this removes the use of copy.deepcopy which was duplicating sympy expressions in weird ways.


This needs a second commit to make tests pass, as it exposes another bug:


Correct the meaning of Dop([]) and Sdop([]), which is zero, not one

Dop(terms) is defined as the sum over the terms - the sum over zero terms is zero.

To allow self.terms == [], we need to not use zip(*terms), which fails when terms == [].


xref gh-141, which was also a mutability issue

@eric-wieser
Copy link
Member Author

Hmm, can't work out how to restart CI here

@eric-wieser eric-wieser reopened this Dec 16, 2019
@eric-wieser eric-wieser force-pushed the fix-dop-mutable-state branch 2 times, most recently from 661fcbb to c843d2b Compare December 17, 2019 00:00
As discovered in pygaegh-150, printing out a laplacian changes the effect of the operator!
In order to ensure immutability, this:

* Changes the following from `list` to `tuple`:
 * `Sdop.terms`
 * `Pdop.terms`
* Changes the following functions to return a new copy, rather than mutating in place _and_ returning themselves:
  * `Sdop.simplify`
  * `Pdop.simplify`
  * `Pdop.factor`
  * `Dop.consolidate_coefs`
* Changes the following undocumented and unused functions to return a new copy, rather than mutating in place and returning `None`:
  * `Sdop.TSimplify`
  * `Pdop.TSimplify`
  * `Sdop.sort_terms`, which has been made private and renamed to `_with_sorted_terms`

Crucially, this removes the use of `copy.deepcopy` which was duplicating sympy expressions in weird ways.
`Dop(terms)` is defined as the sum over the terms - the sum over zero terms is zero.

To allow `self.terms == []`, we need to not use `zip(*terms)`, which fails when `terms == []`.
@eric-wieser eric-wieser force-pushed the fix-dop-mutable-state branch from c843d2b to 79eebfd Compare December 17, 2019 00:23
Comment on lines -2027 to +2039
if len(args[0]) == 0: # identity Dop
self.terms = [(S(1),self.Ga.Pdop_identity)]
else:
if len(args) == 2:
if len(args[0]) != len(args[1]):
raise ValueError('In Dop.__init__ coefficent list and Pdop list must be same length.')
self.terms = list(zip(args[0],args[1]))
elif len(args) == 1:
if isinstance(args[0][0][0], Mv): # Mv expansion [(Mv, Pdop)]
self.terms = args[0]
elif isinstance(args[0][0][0], Sdop): # Sdop expansion [(Sdop, Mv)]
coefs = []
pdiffs = []
for (sdop, mv) in args[0]:
for (coef, pdiff) in sdop.terms:
if pdiff in pdiffs:
index = pdiffs.index(pdiff)
coefs[index] += coef * mv
else:
pdiffs.append(pdiff)
coefs.append(coef * mv)
self.terms = list(zip(coefs, pdiffs))
else:
raise ValueError('In Dop.__init__ args[0] form not allowed. args = ' + str(args))
if len(args) == 2:
if len(args[0]) != len(args[1]):
raise ValueError('In Dop.__init__ coefficent list and Pdop list must be same length.')
self.terms = tuple(zip(args[0], args[1]))
elif len(args) == 1:
if isinstance(args[0][0][0], Mv): # Mv expansion [(Mv, Pdop)]
self.terms = tuple(args[0])
elif isinstance(args[0][0][0], Sdop): # Sdop expansion [(Sdop, Mv)]
coefs = []
pdiffs = []
for (sdop, mv) in args[0]:
for (coef, pdiff) in sdop.terms:
if pdiff in pdiffs:
index = pdiffs.index(pdiff)
coefs[index] += coef * mv
else:
pdiffs.append(pdiff)
coefs.append(coef * mv)
self.terms = tuple(zip(coefs, pdiffs))
else:
raise ValueError('In Dop.__init__ length of args must be 1 or 2.')
raise ValueError('In Dop.__init__ args[0] form not allowed. args = ' + str(args))
else:
raise ValueError('In Dop.__init__ length of args must be 1 or 2.')
Copy link
Member Author

@eric-wieser eric-wieser Dec 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is just unindented, hide whitespace for clarity. This biases the coverage report a fair bit - if desirable, I can put this unindent in its own PR to make that effect clearer.

@eric-wieser eric-wieser marked this pull request as ready for review December 17, 2019 00:30
@eric-wieser eric-wieser changed the title Don't allow *dop objects to be mutated Don't allow *dop objects to be mutated, and correct the behavior of zero Dop / Sdop Dec 17, 2019
@codecov
Copy link

codecov bot commented Dec 17, 2019

Codecov Report

Merging #151 into master will increase coverage by 0.06%.
The diff coverage is 48.93%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #151      +/-   ##
==========================================
+ Coverage   67.17%   67.23%   +0.06%     
==========================================
  Files           8        8              
  Lines        4761     4740      -21     
==========================================
- Hits         3198     3187      -11     
+ Misses       1563     1553      -10
Impacted Files Coverage Δ
galgebra/mv.py 63.06% <48.93%> (+0.13%) ⬆️
galgebra/utils.py 71.87% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4740ea2...79eebfd. Read the comment docs.

@eric-wieser eric-wieser requested a review from utensil December 17, 2019 00:31
@eric-wieser eric-wieser added the ✓ soft CI failure PRs which fail quality but not test CIs label Dec 17, 2019
Copy link
Member

@utensil utensil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug component: differential operators Dop, Pdop, Sdop, grad, rgrad, etc ✓ soft CI failure PRs which fail quality but not test CIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants