Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected side effect using numpy trig functions #399

Closed
josiahslack opened this issue Jul 9, 2016 · 5 comments
Closed

Unexpected side effect using numpy trig functions #399

josiahslack opened this issue Jul 9, 2016 · 5 comments
Labels
numpy Numpy related bug/enhancement

Comments

@josiahslack
Copy link

I ran across this yesterday.

I type the following into a python REPL:

`
import numpy as np
import pint

ureg = pint.UnitRegistry()
Q_ = ureg.Quantity
theta = Q_(30, ureg.degrees)
type(theta.m) # returns int
s_theta = np.sin(theta)
type(theta.m) # returns numpy.ndarray
`
Printing theta, using comparisons, etc. doesn't show that anything has changed about it. I ran into trouble when I was saving an angle's magnitude to a JSON file, and json.dump complained that it didn't know how to write 'array(0.0)'. I worked backwards, and eventually isolated the point where the type of the angle's magnitude changed.

@hgrecco
Copy link
Owner

hgrecco commented Jul 20, 2016

Thanks for reporting. We should not transform scalars to arrays.
And it is not numpy's fault

>>> type(np.sin(.3))
<class 'numpy.float64'>
>>> type(.3)
<class 'float'>

@CD3
Copy link
Contributor

CD3 commented Jan 11, 2017

I ran into this too.

import pint
import numpy

ureg = pint.UnitRegistry()

Angle = ureg.Quantity(30,'degree')

Cos = ureg.Quantity(numpy.sqrt(3.)/2.)
print Angle, type(Angle), Angle.magnitude, type(Angle.magnitude)
print Cos, type(Cos), Cos.magnitude, type(Cos.magnitude)
print "cos of {Angle:.1f} is {Cos:.1e}".format(Angle=Angle, Cos=Cos)

Cos = numpy.cos(Angle)
print Angle, type(Angle), Angle.magnitude, type(Angle.magnitude)
print Cos, type(Cos), Cos.magnitude, type(Cos.magnitude)
print "cos of {Angle:.1f} is {Cos:.1e}".format(Angle=Angle, Cos=Cos)

This throws an error on the second format() call.

30 degree <class 'pint.unit.Quantity'> 30 <type 'int'>
0.866025403784 dimensionless <class 'pint.unit.Quantity'> 0.866025403784 <type 'numpy.float64'>
cos of 30.0 degree is 8.7e-01 dimensionless
30 degree <class 'pint.unit.Quantity'> 30 <type 'numpy.ndarray'>
0.866025403784 dimensionless <class 'pint.unit.Quantity'> 0.866025403784 <type 'numpy.float64'>
Traceback (most recent call last):
  File "./test.py", line 18, in <module>
    print "cos of {Angle:.1f} is {Cos:.1e}".format(Angle=Angle, Cos=Cos)
  File "/usr/lib64/python2.7/site-packages/pint/quantity.py", line 141, in __format__
    format(obj.magnitude, remove_custom_flags(spec)),
ValueError: Unknown format code 'f' for object of type 'unicode'

It took me a while to figure out what was going on. The call to numpy.cos changes the type of Angle. It isn't just trig functions either, numpy.abs will also change the type of its argument.

I would expect any quantities that get passed into the numpy functions are left unmodified. Is pint modifying the argument intentionally for some reason? If not, could the numpy hooks just make a copy of the arguments to pass to the numpy functions?

@CD3
Copy link
Contributor

CD3 commented Jan 11, 2017

A workaround is to pass copies of the quantity to the numpy functions using the copy module

import pint
import numpy
from copy import *

ureg = pint.UnitRegistry()

Angle = ureg.Quantity(30,'degree')

Cos = ureg.Quantity(numpy.sqrt(3.)/2.)
print Angle, type(Angle), Angle.magnitude, type(Angle.magnitude)
print Cos, type(Cos), Cos.magnitude, type(Cos.magnitude)
print "cos of {Angle:.1f} is {Cos:.1e}".format(Angle=Angle, Cos=Cos)

Cos = numpy.cos(copy(Angle)) # pass copy of quantity so it won't be modified.
print Angle, type(Angle), Angle.magnitude, type(Angle.magnitude)
print Cos, type(Cos), Cos.magnitude, type(Cos.magnitude)
print "cos of {Angle:.1f} is {Cos:.1e}".format(Angle=Angle, Cos=Cos)

works as expected

30 degree <class 'pint.unit.Quantity'> 30 <type 'int'>
0.866025403784 dimensionless <class 'pint.unit.Quantity'> 0.866025403784 <type 'numpy.float64'>
cos of 30.0 degree is 8.7e-01 dimensionless
30 degree <class 'pint.unit.Quantity'> 30 <type 'int'>
0.866025403784 dimensionless <class 'pint.unit.Quantity'> 0.866025403784 <type 'numpy.float64'>
cos of 30.0 degree is 8.7e-01 dimensionless

@hgrecco
Copy link
Owner

hgrecco commented Dec 3, 2019

Review after #905

@hgrecco
Copy link
Owner

hgrecco commented Dec 11, 2019

Solved by #905

@hgrecco hgrecco closed this as completed Dec 11, 2019
bors bot added a commit that referenced this issue Dec 27, 2019
957: Add parameterized test for type immutability r=hgrecco a=jthielen

As discussed in #925, this adds a parameterized test to verify that the internal type is not mutated under common operations (as encountered in #399, #481, #509, #622, #678).

- [x] Closes #925, Closes #481
- [x] Executed ``black -t py36 . && isort -rc . && flake8`` with no errors
- [x] The change is fully covered by automated unit tests
- ~~Documented in docs/ as appropriate~~
- [x] Added an entry to the CHANGES file


Co-authored-by: Jon Thielen <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
numpy Numpy related bug/enhancement
Projects
None yet
Development

No branches or pull requests

3 participants