We currently allow, and validate via testing in test_Data_BINARY_AND_UNARY_OPERATORS, some behaviour relating to input and output data types for augmented arithmetic assignment operators that is not allowed by NumPy, and we should consider whether this is suitable or not. I am inclined to say we should make appropriate changes to adopt the NumPy behaviour.
Specifics
Namely, when an augmented assignment is performed using inputs with data types which lead to a change in data type for the output, e.g. for a simplified scalar case something like a = 1; a += 1.0, we permit an in-place change of array dtype. As a minimal example, note how NumPy raises a type casting error for the equivalent operation below, whereas we go ahead and produce an output with a changed data type, the same type that the operation not in-place would produce:
>>> import cf
>>> import numpy as np
>>>
>>> # Setup equivalent arrays
>>> i_np = np.array([1, 2, 3])
>>> i_cf = cf.Data(i_np)
>>>
>>> # NumPy raises a type casting error:
>>> i_np + 1.0 # operation not in-place is fine
array([2., 3., 4.])
>>> i_np += 1.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
>>>
>>> # ... whereas cf performs the operation to give the same result data type
>>> # as the non in-place operation would:
>>> i_cf + 1.0
<CF Data(3): [2.0, 3.0, 4.0]>
>>> i_cf += 1.0
>>> i_cf
<CF Data(3): [2.0, 3.0, 4.0]>
and the equivalent behaviour occurs for the various __i<operator>__ operators.
Relevant cases in test suite
For reference, the tests in test_Data_BINARY_AND_UNARY_OPERATORS which were checking for this (dubious) behaviour, which remain as such from before the LAMA to Dask migration, are:
|
a = a0.copy() |
|
try: |
|
a += x |
|
except TypeError: |
|
pass |
|
else: |
|
e = d.copy() |
|
e += x |
|
message = "Failed in {!r}+={}".format(d, x) |
|
self.assertTrue( |
|
e.equals(cf.Data(a, "m"), verbose=1), message |
|
) |
|
|
|
a = a0.copy() |
|
try: |
|
a *= x |
|
except TypeError: |
|
pass |
|
else: |
|
e = d.copy() |
|
e *= x |
|
message = "Failed in {!r}*={}".format(d, x) |
|
self.assertTrue( |
|
e.equals(cf.Data(a, "m"), verbose=1), message |
|
) |
|
|
|
a = a0.copy() |
|
try: |
|
a /= x |
|
except TypeError: |
|
pass |
|
else: |
|
e = d.copy() |
|
e /= x |
|
message = "Failed in {!r}/={}".format(d, x) |
|
self.assertTrue( |
|
e.equals(cf.Data(a, "m"), verbose=1), message |
|
) |
|
|
|
a = a0.copy() |
|
try: |
|
a -= x |
|
except TypeError: |
|
pass |
|
else: |
|
e = d.copy() |
|
e -= x |
|
message = "Failed in {!r}-={}".format(d, x) |
|
self.assertTrue( |
|
e.equals(cf.Data(a, "m"), verbose=1), message |
|
) |
|
|
|
a = a0.copy() |
|
try: |
|
a //= x |
|
except TypeError: |
|
pass |
|
else: |
|
e = d.copy() |
|
e //= x |
|
message = "Failed in {!r}//={}".format(d, x) |
|
self.assertTrue( |
|
e.equals(cf.Data(a, "m"), verbose=1), message |
|
) |
|
|
|
# TODODASK SB: re-instate this once _combined_units is sorted, |
|
# presently fails with error, as with __pow__: |
|
# AttributeError: 'Data' object has no attribute '_size' |
|
# a = a0.copy() |
|
# try: |
|
# a **= x |
|
# except TypeError: |
|
# pass |
|
# else: |
|
# e = d.copy() |
|
# e **= x |
|
# message = "Failed in {!r}**={}".format(d, x) |
|
# self.assertTrue( |
|
# e.equals(cf.Data(a, "m2"), verbose=1), message |
|
# ) |
|
|
|
a = a0.copy() |
|
try: |
|
a.__itruediv__(x) |
|
except TypeError: |
|
pass |
|
else: |
|
e = d.copy() |
|
e.__itruediv__(x) |
|
message = "Failed in {!r}.__itruediv__({})".format(d, x) |
|
self.assertTrue( |
|
e.equals(cf.Data(a, "m"), verbose=1), message |
|
) |
We currently allow, and validate via testing in
test_Data_BINARY_AND_UNARY_OPERATORS, some behaviour relating to input and output data types for augmented arithmetic assignment operators that is not allowed by NumPy, and we should consider whether this is suitable or not. I am inclined to say we should make appropriate changes to adopt the NumPy behaviour.Specifics
Namely, when an augmented assignment is performed using inputs with data types which lead to a change in data type for the output, e.g. for a simplified scalar case something like
a = 1; a += 1.0, we permit an in-place change of arraydtype. As a minimal example, note how NumPy raises a type casting error for the equivalent operation below, whereas we go ahead and produce an output with a changed data type, the same type that the operation not in-place would produce:and the equivalent behaviour occurs for the various
__i<operator>__operators.Relevant cases in test suite
For reference, the tests in
test_Data_BINARY_AND_UNARY_OPERATORSwhich were checking for this (dubious) behaviour, which remain as such from before the LAMA to Dask migration, are:cf-python/cf/test/test_Data.py
Lines 2062 to 2154 in 0033743