Skip to content

Commit adf021a

Browse files
committed
Update colour.algebra.sdiv definition signature to support replacing with colour.constants.EPSILON constant.
1 parent 5db4b4c commit adf021a

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

colour/algebra/common.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,16 @@
2525
Tuple,
2626
)
2727

28+
from colour.constants import EPSILON
2829
from colour.hints import Literal, cast
29-
from colour.utilities import as_float, as_float_array, optional, tsplit, validate_method
30+
from colour.utilities import (
31+
as_float,
32+
as_float_array,
33+
optional,
34+
runtime_warning,
35+
tsplit,
36+
validate_method,
37+
)
3038

3139
__author__ = "Colour Developers"
3240
__copyright__ = "Copyright 2013 Colour Developers"
@@ -67,6 +75,8 @@
6775
"Warning Zero Conversion",
6876
"Ignore Limit Conversion",
6977
"Warning Limit Conversion",
78+
"Replace With Epsilon",
79+
"Warning Replace With Epsilon",
7080
] = "Ignore Zero Conversion"
7181
"""
7282
Global variable storing the current *Colour* safe division function mode.
@@ -83,6 +93,8 @@ def get_sdiv_mode() -> (
8393
"Warning Zero Conversion",
8494
"Ignore Limit Conversion",
8595
"Warning Limit Conversion",
96+
"Replace With Epsilon",
97+
"Warning Replace With Epsilon",
8698
]
8799
):
88100
"""
@@ -118,6 +130,8 @@ def set_sdiv_mode(
118130
"Warning Zero Conversion",
119131
"Ignore Limit Conversion",
120132
"Warning Limit Conversion",
133+
"Replace With Epsilon",
134+
"Warning Replace With Epsilon",
121135
]
122136
| str
123137
),
@@ -153,6 +167,8 @@ def set_sdiv_mode(
153167
"Warning Zero Conversion",
154168
"Ignore Limit Conversion",
155169
"Warning Limit Conversion",
170+
"Replace With Epsilon",
171+
"Warning Replace With Epsilon",
156172
],
157173
validate_method(
158174
mode,
@@ -165,6 +181,8 @@ def set_sdiv_mode(
165181
"Warning Zero Conversion",
166182
"Ignore Limit Conversion",
167183
"Warning Limit Conversion",
184+
"Replace With Epsilon",
185+
"Warning Replace With Epsilon",
168186
),
169187
),
170188
)
@@ -194,6 +212,8 @@ def __init__(
194212
"Warning Zero Conversion",
195213
"Ignore Limit Conversion",
196214
"Warning Limit Conversion",
215+
"Replace With Epsilon",
216+
"Warning Replace With Epsilon",
197217
]
198218
| None
199219
) = None,
@@ -254,10 +274,16 @@ def sdiv(a: ArrayLike, b: ArrayLike) -> NDArrayFloat:
254274
finite floating point values representable by the division result
255275
:class:`numpy.dtype`. See :func:`numpy.nan_to_num` definition for more
256276
details.
257-
- ``Warning Limit Conversion``: Zero-division occurs with a warning and
277+
- ``Warning Limit Conversion``: Zero-division occurs with a warning and
258278
NaNs or +/- infs values are converted to zeros or the largest +/-
259279
finite floating point values representable by the division result
260280
:class:`numpy.dtype`.
281+
- ``Replace With Epsilon``: Zero-division is avoided by replacing zero
282+
denominators with the machine epsilon value from
283+
:attr:`colour.constants.EPSILON`.
284+
- ``Warning Replace With Epsilon``: Zero-division is avoided by replacing
285+
zero denominators with the machine epsilon value from
286+
:attr:`colour.constants.EPSILON` with a warning.
261287
262288
Parameters
263289
----------
@@ -295,6 +321,12 @@ def sdiv(a: ArrayLike, b: ArrayLike) -> NDArrayFloat:
295321
>>> with sdiv_mode("Warning Limit Conversion"):
296322
... sdiv(a, b) # doctest: +SKIP
297323
array([ 0.00000000e+000, 1.00000000e+000, 1.79769313e+308])
324+
>>> with sdiv_mode("Replace With Epsilon"):
325+
... sdiv(a, b) # doctest: +ELLIPSIS
326+
array([ 0.00000000e+00, 1.00000000e+00, ...])
327+
>>> with sdiv_mode("Warning Replace With Epsilon"):
328+
... sdiv(a, b) # doctest: +ELLIPSIS
329+
array([ 0.00000000e+00, 1.00000000e+00, ...])
298330
"""
299331

300332
a = as_float_array(a)
@@ -311,6 +343,8 @@ def sdiv(a: ArrayLike, b: ArrayLike) -> NDArrayFloat:
311343
"Warning Zero Conversion",
312344
"Ignore Limit Conversion",
313345
"Warning Limit Conversion",
346+
"Replace With Epsilon",
347+
"Warning Replace With Epsilon",
314348
),
315349
)
316350

@@ -337,6 +371,14 @@ def sdiv(a: ArrayLike, b: ArrayLike) -> NDArrayFloat:
337371
elif mode == "warning limit conversion":
338372
with np.errstate(divide="warn", invalid="warn"):
339373
c = np.nan_to_num(a / b)
374+
elif mode == "replace with epsilon":
375+
b = np.where(b == 0, EPSILON, b)
376+
c = a / b
377+
elif mode == "warning replace with epsilon":
378+
if np.any(b == 0):
379+
runtime_warning("Zero(s) detected in denominator, replacing with EPSILON.")
380+
b = np.where(b == 0, EPSILON, b)
381+
c = a / b
340382

341383
return c
342384

colour/algebra/tests/test_common.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
vecmul,
2929
)
3030
from colour.constants import TOLERANCE_ABSOLUTE_TESTS
31-
from colour.utilities import ignore_numpy_errors
31+
from colour.utilities import ColourRuntimeWarning, ignore_numpy_errors
3232

3333
__author__ = "Colour Developers"
3434
__copyright__ = "Copyright 2013 Colour Developers"
@@ -199,6 +199,17 @@ def test_sdiv(self) -> None:
199199
pytest.warns(RuntimeWarning, sdiv, a, b)
200200
np.testing.assert_equal(sdiv(a, b), np.nan_to_num(np.array([0, 1, np.inf])))
201201

202+
with sdiv_mode("Replace With Epsilon"):
203+
np.testing.assert_allclose(
204+
sdiv(a, b), np.array([0, 1, 2 / np.finfo(np.double).eps])
205+
)
206+
207+
with sdiv_mode("Warning Replace With Epsilon"):
208+
pytest.warns(ColourRuntimeWarning, sdiv, a, b)
209+
np.testing.assert_allclose(
210+
sdiv(a, b), np.array([0, 1, 2 / np.finfo(np.double).eps])
211+
)
212+
202213

203214
class TestIsSpowEnabled:
204215
"""

0 commit comments

Comments
 (0)