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

dask: Data.mask_fpe #380

Merged
merged 7 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 0 additions & 76 deletions cf/data/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ def wrapper(*args, **kwargs):
_seterr_raise_to_ignore[key] = "ignore"
# --- End: for

# --------------------------------------------------------------------
# _mask_fpe[0] = Whether or not to automatically set
# FloatingPointError exceptions to masked values in
# arimthmetic.
# --------------------------------------------------------------------
_mask_fpe = [False]

_empty_set = set()

_units_None = Units()
Expand Down Expand Up @@ -5754,75 +5747,6 @@ def mask(self):

return mask_data_obj

@staticmethod
def mask_fpe(*arg):
"""Masking of floating-point errors in the results of arithmetic
operations.

If masking is allowed then only floating-point errors which would
otherwise be raised as `FloatingPointError` exceptions are
masked. Whether `FloatingPointError` exceptions may be raised is
determined by `cf.Data.seterr`.

If called without an argument then the current behaviour is
returned.

Note that if the raising of `FloatingPointError` exceptions has
suppressed then invalid values in the results of arithmetic
operations may be subsequently converted to masked values with the
`mask_invalid` method.

.. seealso:: `cf.Data.seterr`, `mask_invalid`

:Parameters:

arg: `bool`, optional
The new behaviour. True means that `FloatingPointError`
exceptions are suppressed and replaced with masked
values. False means that `FloatingPointError` exceptions
are raised. The default is not to change the current
behaviour.

:Returns:

`bool`
The behaviour prior to the change, or the current
behaviour if no new value was specified.

**Examples**

>>> d = cf.Data([0., 1])
>>> e = cf.Data([1., 2])

>>> old = cf.Data.mask_fpe(False)
>>> old = cf.Data.seterr('raise')
>>> e/d
FloatingPointError: divide by zero encountered in divide
>>> e**123456
FloatingPointError: overflow encountered in power

>>> old = cf.Data.mask_fpe(True)
>>> old = cf.Data.seterr('raise')
>>> e/d
<CF Data: [--, 2.0] >
>>> e**123456
<CF Data: [1.0, --] >

>>> old = cf.Data.mask_fpe(True)
>>> old = cf.Data.seterr('ignore')
>>> e/d
<CF Data: [inf, 2.0] >
>>> e**123456
<CF Data: [1.0, inf] >

"""
old = _mask_fpe[0]

if arg:
_mask_fpe[0] = bool(arg[0])

return old

@staticmethod
def seterr(all=None, divide=None, over=None, under=None, invalid=None):
"""Set how floating-point errors in the results of arithmetic
Expand Down
78 changes: 78 additions & 0 deletions cf/data/mixin/deprecations.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ...functions import (
_DEPRECATION_ERROR_ATTRIBUTE,
_DEPRECATION_ERROR_METHOD,
DeprecationError,
)


Expand Down Expand Up @@ -372,6 +373,83 @@ def add_partitions(self, extra_boundaries, pdim):
"TODODASK Consider using rechunk instead"
) # pragma: no cover

@staticmethod
def mask_fpe(*arg):
"""Masking of floating-point errors in the results of arithmetic
operations.

Deprecated at version TODODASK. It is currently not possible
to control how floating-point errors are handled, due to the
use of `dask` for handling all array manipulations. This may
change in the future (see
https://github.com/dask/dask/issues/3245 for more details).

If masking is allowed then only floating-point errors which would
otherwise be raised as `FloatingPointError` exceptions are
masked. Whether `FloatingPointError` exceptions may be raised is
determined by `cf.Data.seterr`.

If called without an argument then the current behaviour is
returned.

Note that if the raising of `FloatingPointError` exceptions has
been suppressed then invalid values in the results of arithmetic
operations may be subsequently converted to masked values with the
`mask_invalid` method.

.. seealso:: `cf.Data.seterr`, `mask_invalid`

:Parameters:

arg: `bool`, optional
The new behaviour. True means that `FloatingPointError`
exceptions are suppressed and replaced with masked
values. False means that `FloatingPointError` exceptions
are raised. The default is not to change the current
behaviour.

:Returns:

`bool`
The behaviour prior to the change, or the current
behaviour if no new value was specified.

**Examples:**

>>> d = cf.Data([0., 1])
>>> e = cf.Data([1., 2])

>>> old = cf.Data.mask_fpe(False)
>>> old = cf.Data.seterr('raise')
>>> e/d
FloatingPointError: divide by zero encountered in divide
>>> e**123456
FloatingPointError: overflow encountered in power

>>> old = cf.Data.mask_fpe(True)
>>> old = cf.Data.seterr('raise')
>>> e/d
<CF Data: [--, 2.0] >
>>> e**123456
<CF Data: [1.0, --] >

>>> old = cf.Data.mask_fpe(True)
>>> old = cf.Data.seterr('ignore')
>>> e/d
<CF Data: [inf, 2.0] >
>>> e**123456
<CF Data: [1.0, inf] >

"""
raise DeprecationError(
"Data method 'mask_fpe' has been deprecated at version TODODASK "
"and is not available.\n\n"
"It is currently not possible to control how floating-point errors "
"are handled, due to the use of `dask` for handling all array "
"manipulations. This may change in the future (see "
"https://github.com/dask/dask/issues/3245 for more details)."
)

def partition_boundaries(self):
"""Return the partition boundaries for each partition matrix
dimension.
Expand Down
39 changes: 0 additions & 39 deletions cf/test/test_Data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2244,45 +2244,6 @@ def test_Data_BROADCASTING(self):
self.assertEqual(de.shape, ab.shape)
self.assertTrue((de.array == ab).all())

def test_Data_ERROR(self):
if self.test_only and inspect.stack()[0][3] not in self.test_only:
return

return # !!!!!!

d = cf.Data([0.0, 1])
e = cf.Data([1.0, 2])

oldm = cf.Data.mask_fpe(False)
olds = cf.Data.seterr("raise")

with self.assertRaises(FloatingPointError):
_ = e / d

with self.assertRaises(FloatingPointError):
_ = e ** 123456

cf.Data.mask_fpe(True)
cf.Data.seterr(all="raise")

g = cf.Data([-99, 2.0])
g[0] = cf.masked
f = e / d
self.assertTrue(f.equals(g, verbose=2))

g = cf.Data([1.0, -99])
g[1] = cf.masked
f = e ** 123456
self.assertTrue(f.equals(g, verbose=2))

cf.Data.mask_fpe(True)
cf.Data.seterr(all="ignore")
f = e / d
f = e ** 123456

cf.Data.mask_fpe(oldm)
cf.Data.seterr(**olds)

def test_Data__len__(self):
if self.test_only and inspect.stack()[0][3] not in self.test_only:
return
Expand Down