Coverage for colour/continuous/tests/test_multi_signal.py: 100%
296 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
1"""Define the unit tests for the :mod:`colour.continuous.multi_signals` module."""
3from __future__ import annotations
5import pickle
6import textwrap
8import numpy as np
9import pytest
11from colour.algebra import CubicSplineInterpolator, Extrapolator, KernelInterpolator
12from colour.constants import DTYPE_FLOAT_DEFAULT, TOLERANCE_ABSOLUTE_TESTS
13from colour.continuous import MultiSignals, Signal
14from colour.utilities import (
15 ColourRuntimeWarning,
16 attest,
17 is_pandas_installed,
18 is_scipy_installed,
19 tsplit,
20 tstack,
21)
23__author__ = "Colour Developers"
24__copyright__ = "Copyright 2013 Colour Developers"
25__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
26__maintainer__ = "Colour Developers"
27__email__ = "colour-developers@colour-science.org"
28__status__ = "Production"
30__all__ = [
31 "TestMultiSignals",
32]
35class TestMultiSignals:
36 """
37 Define :class:`colour.continuous.multi_signals.MultiSignals` class unit
38 tests methods.
39 """
41 def setup_method(self) -> None:
42 """Initialise the common tests attributes."""
44 self._range_1 = np.linspace(10, 100, 10)
45 self._range_2 = tstack([self._range_1] * 3) + np.array([0, 10, 20])
46 self._domain_1 = np.arange(0, 10, 1)
47 self._domain_2 = np.arange(100, 1100, 100)
49 self._multi_signals = MultiSignals(self._range_2)
51 def test_required_attributes(self) -> None:
52 """Test the presence of required attributes."""
54 required_attributes = (
55 "dtype",
56 "domain",
57 "range",
58 "interpolator",
59 "interpolator_kwargs",
60 "extrapolator",
61 "extrapolator_kwargs",
62 "function",
63 "signals",
64 "labels",
65 "signal_type",
66 )
68 for attribute in required_attributes:
69 assert attribute in dir(MultiSignals)
71 def test_required_methods(self) -> None:
72 """Test the presence of required methods."""
74 required_methods = (
75 "__init__",
76 "__str__",
77 "__repr__",
78 "__hash__",
79 "__getitem__",
80 "__setitem__",
81 "__contains__",
82 "__iter__",
83 "__eq__",
84 "__ne__",
85 "arithmetical_operation",
86 "multi_signals_unpack_data",
87 "fill_nan",
88 "domain_distance",
89 "to_dataframe",
90 )
92 for method in required_methods:
93 assert method in dir(MultiSignals)
95 def test_pickling(self) -> None:
96 """
97 Test whether the :class:``colour.continuous.signal.MultiSignals` class
98 can be pickled.
99 """
101 data = pickle.dumps(self._multi_signals)
102 data = pickle.loads(data) # noqa: S301
103 assert self._multi_signals == data
105 def test_dtype(self) -> None:
106 """
107 Test :func:`colour.continuous.multi_signals.MultiSignals.dtype`
108 property.
109 """
111 assert self._multi_signals.dtype == DTYPE_FLOAT_DEFAULT
113 multi_signals = self._multi_signals.copy()
114 multi_signals.dtype = np.float32
115 assert multi_signals.dtype == np.float32
117 def test_domain(self) -> None:
118 """
119 Test :func:`colour.continuous.multi_signals.MultiSignals.domain`
120 property.
121 """
123 multi_signals = self._multi_signals.copy()
125 np.testing.assert_allclose(
126 multi_signals[np.array([0, 1, 2])],
127 np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]]),
128 atol=TOLERANCE_ABSOLUTE_TESTS,
129 )
131 multi_signals.domain = self._domain_1 * 10
133 np.testing.assert_array_equal(multi_signals.domain, self._domain_1 * 10)
135 np.testing.assert_allclose(
136 multi_signals[np.array([0, 1, 2]) * 10],
137 np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]]),
138 atol=TOLERANCE_ABSOLUTE_TESTS,
139 )
141 domain = np.linspace(0, 1, 10)
142 domain[0] = -np.inf
144 def assert_warns() -> None:
145 """Help to test the runtime warning."""
147 multi_signals.domain = domain
149 pytest.warns(ColourRuntimeWarning, assert_warns)
151 def test_range(self) -> None:
152 """
153 Test :func:`colour.continuous.multi_signals.MultiSignals.range`
154 property.
155 """
157 multi_signals = self._multi_signals.copy()
159 np.testing.assert_allclose(
160 multi_signals[np.array([0, 1, 2])],
161 np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]]),
162 atol=TOLERANCE_ABSOLUTE_TESTS,
163 )
165 multi_signals.range = self._range_1 * 10
167 np.testing.assert_array_equal(
168 multi_signals.range, tstack([self._range_1] * 3) * 10
169 )
171 np.testing.assert_allclose(
172 multi_signals[np.array([0, 1, 2])],
173 np.array([[10.0, 10.0, 10.0], [20.0, 20.0, 20.0], [30.0, 30.0, 30.0]]) * 10,
174 atol=TOLERANCE_ABSOLUTE_TESTS,
175 )
177 multi_signals.range = self._range_2 * 10
179 np.testing.assert_array_equal(multi_signals.range, self._range_2 * 10)
181 np.testing.assert_allclose(
182 multi_signals[np.array([0, 1, 2])],
183 np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]]) * 10,
184 atol=TOLERANCE_ABSOLUTE_TESTS,
185 )
187 def test_interpolator(self) -> None:
188 """
189 Test :func:`colour.continuous.multi_signals.MultiSignals.interpolator`
190 property.
191 """
193 if not is_scipy_installed(): # pragma: no cover
194 return
196 multi_signals = self._multi_signals.copy()
198 np.testing.assert_allclose(
199 multi_signals[np.linspace(0, 5, 5)],
200 np.array(
201 [
202 [10.00000000, 20.00000000, 30.00000000],
203 [22.83489024, 32.80460562, 42.77432100],
204 [34.80044921, 44.74343470, 54.68642018],
205 [47.55353925, 57.52325463, 67.49297001],
206 [60.00000000, 70.00000000, 80.00000000],
207 ]
208 ),
209 atol=TOLERANCE_ABSOLUTE_TESTS,
210 )
212 multi_signals.interpolator = CubicSplineInterpolator
214 np.testing.assert_allclose(
215 multi_signals[np.linspace(0, 5, 5)],
216 np.array(
217 [
218 [10.00000000, 20.00000000, 30.00000000],
219 [22.50000000, 32.50000000, 42.50000000],
220 [35.00000000, 45.00000000, 55.00000000],
221 [47.50000000, 57.50000000, 67.50000000],
222 [60.00000000, 70.00000000, 80.00000000],
223 ]
224 ),
225 atol=TOLERANCE_ABSOLUTE_TESTS,
226 )
228 def test_interpolator_kwargs(self) -> None:
229 """
230 Test :func:`colour.continuous.multi_signals.MultiSignals.\
231interpolator_kwargs` property.
232 """
234 multi_signals = self._multi_signals.copy()
236 np.testing.assert_allclose(
237 multi_signals[np.linspace(0, 5, 5)],
238 np.array(
239 [
240 [10.00000000, 20.00000000, 30.00000000],
241 [22.83489024, 32.80460562, 42.77432100],
242 [34.80044921, 44.74343470, 54.68642018],
243 [47.55353925, 57.52325463, 67.49297001],
244 [60.00000000, 70.00000000, 80.00000000],
245 ]
246 ),
247 atol=TOLERANCE_ABSOLUTE_TESTS,
248 )
250 multi_signals.interpolator_kwargs = {
251 "window": 1,
252 "kernel_kwargs": {"a": 1},
253 }
255 np.testing.assert_allclose(
256 multi_signals[np.linspace(0, 5, 5)],
257 np.array(
258 [
259 [10.00000000, 20.00000000, 30.00000000],
260 [18.91328761, 27.91961505, 36.92594248],
261 [28.36993142, 36.47562611, 44.58132080],
262 [44.13100443, 53.13733187, 62.14365930],
263 [60.00000000, 70.00000000, 80.00000000],
264 ]
265 ),
266 atol=TOLERANCE_ABSOLUTE_TESTS,
267 )
269 def test_extrapolator(self) -> None:
270 """
271 Test :func:`colour.continuous.multi_signals.MultiSignals.extrapolator`
272 property.
273 """
275 assert isinstance(self._multi_signals.extrapolator(), Extrapolator)
277 def test_extrapolator_kwargs(self) -> None:
278 """
279 Test :func:`colour.continuous.multi_signals.MultiSignals.\
280extrapolator_kwargs` property.
281 """
283 multi_signals = self._multi_signals.copy()
285 attest(np.all(np.isnan(multi_signals[np.array([-1000, 1000])])))
287 multi_signals.extrapolator_kwargs = {
288 "method": "Linear",
289 }
291 np.testing.assert_allclose(
292 multi_signals[np.array([-1000, 1000])],
293 np.array([[-9990.0, -9980.0, -9970.0], [10010.0, 10020.0, 10030.0]]),
294 atol=TOLERANCE_ABSOLUTE_TESTS,
295 )
297 def test_function(self) -> None:
298 """
299 Test :func:`colour.continuous.multi_signals.MultiSignals.function`
300 property.
301 """
303 attest(callable(self._multi_signals.function))
305 def test_raise_exception_function(self) -> None:
306 """
307 Test :func:`colour.continuous.signal.multi_signals.MultiSignals.\
308function` property raised exception.
309 """
311 pytest.raises((ValueError, TypeError), MultiSignals().function, 0)
313 def test_signals(self) -> None:
314 """
315 Test :func:`colour.continuous.multi_signals.MultiSignals.signals`
316 property.
317 """
319 multi_signals = self._multi_signals.copy()
321 multi_signals.signals = self._range_1
322 np.testing.assert_array_equal(multi_signals.domain, self._domain_1)
323 np.testing.assert_array_equal(multi_signals.range, self._range_1[:, None])
325 def test_labels(self) -> None:
326 """
327 Test :func:`colour.continuous.multi_signals.MultiSignals.labels`
328 property.
329 """
331 assert self._multi_signals.labels == ["0", "1", "2"]
333 multi_signals = self._multi_signals.copy()
335 multi_signals.labels = ["a", "b", "c"]
337 assert multi_signals.labels == ["a", "b", "c"]
339 def test_signal_type(self) -> None:
340 """
341 Test :func:`colour.continuous.multi_signals.MultiSignals.signal_type`
342 property.
343 """
345 multi_signals = MultiSignals(signal_type=Signal)
347 assert multi_signals.signal_type == Signal
349 def test__init__(self) -> None:
350 """
351 Test :meth:`colour.continuous.multi_signals.MultiSignals.__init__`
352 method.
353 """
355 multi_signals = MultiSignals(self._range_1)
356 np.testing.assert_array_equal(multi_signals.domain, self._domain_1)
357 np.testing.assert_array_equal(multi_signals.range, self._range_1[:, None])
359 multi_signals = MultiSignals(self._range_1, self._domain_2)
360 np.testing.assert_array_equal(multi_signals.domain, self._domain_2)
361 np.testing.assert_array_equal(multi_signals.range, self._range_1[:, None])
363 multi_signals = MultiSignals(self._range_2, self._domain_2)
364 np.testing.assert_array_equal(multi_signals.domain, self._domain_2)
365 np.testing.assert_array_equal(multi_signals.range, self._range_2)
367 multi_signals = MultiSignals(
368 dict(zip(self._domain_2, self._range_2, strict=True))
369 )
370 np.testing.assert_array_equal(multi_signals.domain, self._domain_2)
371 np.testing.assert_array_equal(multi_signals.range, self._range_2)
373 multi_signals = MultiSignals(multi_signals)
374 np.testing.assert_array_equal(multi_signals.domain, self._domain_2)
375 np.testing.assert_array_equal(multi_signals.range, self._range_2)
377 class NotSignal(Signal):
378 """Not :class:`Signal` class."""
380 multi_signals = MultiSignals(self._range_1, signal_type=NotSignal)
381 assert isinstance(multi_signals.signals["0"], NotSignal)
382 np.testing.assert_array_equal(multi_signals.domain, self._domain_1)
383 np.testing.assert_array_equal(multi_signals.range, self._range_1[:, None])
385 if is_pandas_installed():
386 from pandas import DataFrame, Series # noqa: PLC0415
388 multi_signals = MultiSignals(
389 Series(dict(zip(self._domain_2, self._range_1, strict=True)))
390 )
391 np.testing.assert_array_equal(multi_signals.domain, self._domain_2)
392 np.testing.assert_array_equal(multi_signals.range, self._range_1[:, None])
394 data = dict(zip(["a", "b", "c"], tsplit(self._range_2), strict=True))
395 multi_signals = MultiSignals(DataFrame(data, self._domain_2))
396 np.testing.assert_array_equal(multi_signals.domain, self._domain_2)
397 np.testing.assert_array_equal(multi_signals.range, self._range_2)
399 def test__hash__(self) -> None:
400 """
401 Test :meth:`colour.continuous.multi_signals.MultiSignals.__hash__`
402 method.
403 """
405 assert isinstance(hash(self._multi_signals), int)
407 def test__str__(self) -> None:
408 """
409 Test :meth:`colour.continuous.multi_signals.MultiSignals.__str__`
410 method.
411 """
413 assert (
414 str(self._multi_signals)
415 == (
416 textwrap.dedent(
417 """
418 [[ 0. 10. 20. 30.]
419 [ 1. 20. 30. 40.]
420 [ 2. 30. 40. 50.]
421 [ 3. 40. 50. 60.]
422 [ 4. 50. 60. 70.]
423 [ 5. 60. 70. 80.]
424 [ 6. 70. 80. 90.]
425 [ 7. 80. 90. 100.]
426 [ 8. 90. 100. 110.]
427 [ 9. 100. 110. 120.]]"""
428 )[1:]
429 )
430 )
432 assert isinstance(str(MultiSignals()), str)
434 def test__repr__(self) -> None:
435 """
436 Test :meth:`colour.continuous.multi_signals.MultiSignals.__repr__`
437 method.
438 """
440 assert repr(self._multi_signals) == (
441 textwrap.dedent(
442 """
443 MultiSignals([[ 0., 10., 20., 30.],
444 [ 1., 20., 30., 40.],
445 [ 2., 30., 40., 50.],
446 [ 3., 40., 50., 60.],
447 [ 4., 50., 60., 70.],
448 [ 5., 60., 70., 80.],
449 [ 6., 70., 80., 90.],
450 [ 7., 80., 90., 100.],
451 [ 8., 90., 100., 110.],
452 [ 9., 100., 110., 120.]],
453 ['0', '1', '2'],
454 KernelInterpolator,
455 {},
456 Extrapolator,
457 {'method': 'Constant', 'left': nan, 'right': nan})
458 """
459 ).strip()
460 )
462 assert isinstance(repr(MultiSignals()), str)
464 def test__getitem__(self) -> None:
465 """
466 Test :meth:`colour.continuous.multi_signals.MultiSignals.__getitem__`
467 method.
468 """
470 np.testing.assert_allclose(
471 self._multi_signals[0],
472 np.array([10.0, 20.0, 30.0]),
473 atol=TOLERANCE_ABSOLUTE_TESTS,
474 )
476 np.testing.assert_allclose(
477 self._multi_signals[np.array([0, 1, 2])],
478 np.array(
479 [
480 [10.0, 20.0, 30.0],
481 [20.0, 30.0, 40.0],
482 [30.0, 40.0, 50.0],
483 ]
484 ),
485 atol=TOLERANCE_ABSOLUTE_TESTS,
486 )
488 np.testing.assert_allclose(
489 self._multi_signals[np.linspace(0, 5, 5)],
490 np.array(
491 [
492 [10.00000000, 20.00000000, 30.00000000],
493 [22.83489024, 32.80460562, 42.77432100],
494 [34.80044921, 44.74343470, 54.68642018],
495 [47.55353925, 57.52325463, 67.49297001],
496 [60.00000000, 70.00000000, 80.00000000],
497 ]
498 ),
499 atol=TOLERANCE_ABSOLUTE_TESTS,
500 )
502 attest(np.all(np.isnan(self._multi_signals[np.array([-1000, 1000])])))
504 np.testing.assert_allclose(
505 self._multi_signals[:],
506 self._multi_signals.range,
507 atol=TOLERANCE_ABSOLUTE_TESTS,
508 )
510 np.testing.assert_allclose(
511 self._multi_signals[:, :], # pyright: ignore
512 self._multi_signals.range,
513 atol=TOLERANCE_ABSOLUTE_TESTS,
514 )
516 np.testing.assert_allclose(
517 self._multi_signals[0:3],
518 np.array(
519 [
520 [10.0, 20.0, 30.0],
521 [20.0, 30.0, 40.0],
522 [30.0, 40.0, 50.0],
523 ]
524 ),
525 atol=TOLERANCE_ABSOLUTE_TESTS,
526 )
528 np.testing.assert_allclose(
529 self._multi_signals[:, 0:2], # pyright: ignore
530 np.array(
531 [
532 [10.0, 20.0],
533 [20.0, 30.0],
534 [30.0, 40.0],
535 [40.0, 50.0],
536 [50.0, 60.0],
537 [60.0, 70.0],
538 [70.0, 80.0],
539 [80.0, 90.0],
540 [90.0, 100.0],
541 [100.0, 110.0],
542 ]
543 ),
544 atol=TOLERANCE_ABSOLUTE_TESTS,
545 )
547 multi_signals = self._multi_signals.copy()
548 multi_signals.extrapolator_kwargs = {
549 "method": "Linear",
550 }
551 np.testing.assert_array_equal(
552 multi_signals[np.array([-1000, 1000])],
553 np.array(
554 [
555 [-9990.0, -9980.0, -9970.0],
556 [10010.0, 10020.0, 10030.0],
557 ]
558 ),
559 )
561 multi_signals.extrapolator_kwargs = {
562 "method": "Constant",
563 "left": 0,
564 "right": 1,
565 }
566 np.testing.assert_array_equal(
567 multi_signals[np.array([-1000, 1000])],
568 np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]),
569 )
571 def test__setitem__(self) -> None:
572 """
573 Test :meth:`colour.continuous.multi_signals.MultiSignals.__setitem__`
574 method.
575 """
577 multi_signals = self._multi_signals.copy()
579 multi_signals[0] = 20
580 np.testing.assert_allclose(
581 multi_signals[0],
582 np.array([20.0, 20.0, 20.0]),
583 atol=TOLERANCE_ABSOLUTE_TESTS,
584 )
586 multi_signals[np.array([0, 1, 2])] = 30
587 np.testing.assert_allclose(
588 multi_signals[np.array([0, 1, 2])],
589 np.array(
590 [
591 [30.0, 30.0, 30.0],
592 [30.0, 30.0, 30.0],
593 [30.0, 30.0, 30.0],
594 ]
595 ),
596 atol=TOLERANCE_ABSOLUTE_TESTS,
597 )
599 multi_signals[np.linspace(0, 5, 5)] = 50
600 np.testing.assert_allclose(
601 multi_signals.domain,
602 np.array(
603 [
604 0.00,
605 1.00,
606 1.25,
607 2.00,
608 2.50,
609 3.00,
610 3.75,
611 4.00,
612 5.00,
613 6.00,
614 7.00,
615 8.00,
616 9.00,
617 ]
618 ),
619 atol=TOLERANCE_ABSOLUTE_TESTS,
620 )
621 np.testing.assert_allclose(
622 multi_signals.range,
623 np.array(
624 [
625 [50.0, 50.0, 50.0],
626 [30.0, 30.0, 30.0],
627 [50.0, 50.0, 50.0],
628 [30.0, 30.0, 30.0],
629 [50.0, 50.0, 50.0],
630 [40.0, 50.0, 60.0],
631 [50.0, 50.0, 50.0],
632 [50.0, 60.0, 70.0],
633 [50.0, 50.0, 50.0],
634 [70.0, 80.0, 90.0],
635 [80.0, 90.0, 100.0],
636 [90.0, 100.0, 110.0],
637 [100.0, 110.0, 120.0],
638 ]
639 ),
640 atol=TOLERANCE_ABSOLUTE_TESTS,
641 )
643 multi_signals[np.array([0, 1, 2])] = np.array([10, 20, 30])
644 np.testing.assert_allclose(
645 multi_signals.range,
646 np.array(
647 [
648 [10.0, 20.0, 30.0],
649 [10.0, 20.0, 30.0],
650 [50.0, 50.0, 50.0],
651 [10.0, 20.0, 30.0],
652 [50.0, 50.0, 50.0],
653 [40.0, 50.0, 60.0],
654 [50.0, 50.0, 50.0],
655 [50.0, 60.0, 70.0],
656 [50.0, 50.0, 50.0],
657 [70.0, 80.0, 90.0],
658 [80.0, 90.0, 100.0],
659 [90.0, 100.0, 110.0],
660 [100.0, 110.0, 120.0],
661 ]
662 ),
663 atol=TOLERANCE_ABSOLUTE_TESTS,
664 )
666 multi_signals[np.array([0, 1, 2])] = np.reshape(np.arange(1, 10, 1), (3, 3))
667 np.testing.assert_allclose(
668 multi_signals.range,
669 np.array(
670 [
671 [1.0, 2.0, 3.0],
672 [4.0, 5.0, 6.0],
673 [50.0, 50.0, 50.0],
674 [7.0, 8.0, 9.0],
675 [50.0, 50.0, 50.0],
676 [40.0, 50.0, 60.0],
677 [50.0, 50.0, 50.0],
678 [50.0, 60.0, 70.0],
679 [50.0, 50.0, 50.0],
680 [70.0, 80.0, 90.0],
681 [80.0, 90.0, 100.0],
682 [90.0, 100.0, 110.0],
683 [100.0, 110.0, 120.0],
684 ]
685 ),
686 atol=TOLERANCE_ABSOLUTE_TESTS,
687 )
689 multi_signals[:] = 40
690 np.testing.assert_allclose(
691 multi_signals.range, 40, atol=TOLERANCE_ABSOLUTE_TESTS
692 )
694 multi_signals[:, :] = 50 # pyright: ignore
695 np.testing.assert_allclose(
696 multi_signals.range, 50, atol=TOLERANCE_ABSOLUTE_TESTS
697 )
699 multi_signals = self._multi_signals.copy()
700 multi_signals[0:3] = 40
701 np.testing.assert_allclose(
702 multi_signals[0:3],
703 np.array(
704 [
705 [40.0, 40.0, 40.0],
706 [40.0, 40.0, 40.0],
707 [40.0, 40.0, 40.0],
708 ]
709 ),
710 atol=TOLERANCE_ABSOLUTE_TESTS,
711 )
713 multi_signals[:, 0:2] = 50 # pyright: ignore
714 np.testing.assert_allclose(
715 multi_signals.range,
716 np.array(
717 [
718 [50.0, 50.0, 40.0],
719 [50.0, 50.0, 40.0],
720 [50.0, 50.0, 40.0],
721 [50.0, 50.0, 60.0],
722 [50.0, 50.0, 70.0],
723 [50.0, 50.0, 80.0],
724 [50.0, 50.0, 90.0],
725 [50.0, 50.0, 100.0],
726 [50.0, 50.0, 110.0],
727 [50.0, 50.0, 120.0],
728 ]
729 ),
730 atol=TOLERANCE_ABSOLUTE_TESTS,
731 )
733 def test__contains__(self) -> None:
734 """
735 Test :meth:`colour.continuous.multi_signals.MultiSignals.__contains__`
736 method.
737 """
739 assert 0 in self._multi_signals
740 assert 0.5 in self._multi_signals
741 assert 1000 not in self._multi_signals
743 def test__iter__(self) -> None:
744 """Test :func:`colour.continuous.signal.Signal.__iter__` method."""
746 domain = np.arange(0, 10)
747 for i, domain_range_value in enumerate(self._multi_signals):
748 np.testing.assert_array_equal(domain_range_value[0], domain[i])
749 np.testing.assert_array_equal(domain_range_value[1:], self._range_2[i])
751 def test__len__(self) -> None:
752 """
753 Test :meth:`colour.continuous.multi_signals.MultiSignals.__len__`
754 method.
755 """
757 assert len(self._multi_signals) == 10
759 def test__eq__(self) -> None:
760 """
761 Test :meth:`colour.continuous.multi_signals.MultiSignals.__eq__`
762 method.
763 """
765 signal_1 = self._multi_signals.copy()
766 signal_2 = self._multi_signals.copy()
768 assert signal_1 == signal_2
770 assert signal_1 != ()
772 def test__ne__(self) -> None:
773 """
774 Test :meth:`colour.continuous.multi_signals.MultiSignals.__ne__`
775 method.
776 """
778 multi_signals_1 = self._multi_signals.copy()
779 multi_signals_2 = self._multi_signals.copy()
781 multi_signals_2[0] = 20
782 assert multi_signals_1 != multi_signals_2
784 multi_signals_2[0] = np.array([10, 20, 30])
785 assert multi_signals_1 == multi_signals_2
787 multi_signals_2.interpolator = CubicSplineInterpolator
788 assert multi_signals_1 != multi_signals_2
790 multi_signals_2.interpolator = KernelInterpolator
791 assert multi_signals_1 == multi_signals_2
793 multi_signals_2.interpolator_kwargs = {"window": 1}
794 assert multi_signals_1 != multi_signals_2
796 multi_signals_2.interpolator_kwargs = {}
797 assert multi_signals_1 == multi_signals_2
799 class NotExtrapolator(Extrapolator):
800 """Not :class:`Extrapolator` class."""
802 multi_signals_2.extrapolator = NotExtrapolator
803 assert multi_signals_1 != multi_signals_2
805 multi_signals_2.extrapolator = Extrapolator
806 assert multi_signals_1 == multi_signals_2
808 multi_signals_2.extrapolator_kwargs = {}
809 assert multi_signals_1 != multi_signals_2
811 multi_signals_2.extrapolator_kwargs = {
812 "method": "Constant",
813 "left": np.nan,
814 "right": np.nan,
815 }
816 assert multi_signals_1 == multi_signals_2
818 def test_arithmetical_operation(self) -> None:
819 """
820 Test :func:`colour.continuous.multi_signals.MultiSignals.\
821arithmetical_operation` method.
822 """
824 np.testing.assert_allclose(
825 self._multi_signals.arithmetical_operation(10, "+", False).range,
826 self._range_2 + 10,
827 atol=TOLERANCE_ABSOLUTE_TESTS,
828 )
830 np.testing.assert_allclose(
831 self._multi_signals.arithmetical_operation(10, "-", False).range,
832 self._range_2 - 10,
833 atol=TOLERANCE_ABSOLUTE_TESTS,
834 )
836 np.testing.assert_allclose(
837 self._multi_signals.arithmetical_operation(10, "*", False).range,
838 self._range_2 * 10,
839 atol=TOLERANCE_ABSOLUTE_TESTS,
840 )
842 np.testing.assert_allclose(
843 self._multi_signals.arithmetical_operation(10, "/", False).range,
844 self._range_2 / 10,
845 atol=TOLERANCE_ABSOLUTE_TESTS,
846 )
848 np.testing.assert_allclose(
849 self._multi_signals.arithmetical_operation(10, "**", False).range,
850 self._range_2**10,
851 atol=TOLERANCE_ABSOLUTE_TESTS,
852 )
854 np.testing.assert_allclose(
855 (self._multi_signals + 10).range,
856 self._range_2 + 10,
857 atol=TOLERANCE_ABSOLUTE_TESTS,
858 )
860 np.testing.assert_allclose(
861 (self._multi_signals - 10).range,
862 self._range_2 - 10,
863 atol=TOLERANCE_ABSOLUTE_TESTS,
864 )
866 np.testing.assert_allclose(
867 (self._multi_signals * 10).range,
868 self._range_2 * 10,
869 atol=TOLERANCE_ABSOLUTE_TESTS,
870 )
872 np.testing.assert_allclose(
873 (self._multi_signals / 10).range,
874 self._range_2 / 10,
875 atol=TOLERANCE_ABSOLUTE_TESTS,
876 )
878 np.testing.assert_allclose(
879 (self._multi_signals**10).range,
880 self._range_2**10,
881 atol=TOLERANCE_ABSOLUTE_TESTS,
882 )
884 multi_signals = self._multi_signals.copy()
886 np.testing.assert_allclose(
887 multi_signals.arithmetical_operation(10, "+", True).range,
888 self._range_2 + 10,
889 atol=TOLERANCE_ABSOLUTE_TESTS,
890 )
892 np.testing.assert_allclose(
893 multi_signals.arithmetical_operation(10, "-", True).range,
894 self._range_2,
895 atol=TOLERANCE_ABSOLUTE_TESTS,
896 )
898 np.testing.assert_allclose(
899 multi_signals.arithmetical_operation(10, "*", True).range,
900 self._range_2 * 10,
901 atol=TOLERANCE_ABSOLUTE_TESTS,
902 )
904 np.testing.assert_allclose(
905 multi_signals.arithmetical_operation(10, "/", True).range,
906 self._range_2,
907 atol=TOLERANCE_ABSOLUTE_TESTS,
908 )
910 np.testing.assert_allclose(
911 multi_signals.arithmetical_operation(10, "**", True).range,
912 self._range_2**10,
913 atol=TOLERANCE_ABSOLUTE_TESTS,
914 )
916 multi_signals = self._multi_signals.copy()
917 np.testing.assert_allclose(
918 multi_signals.arithmetical_operation(self._range_2, "+", False).range,
919 self._range_2 + self._range_2,
920 atol=TOLERANCE_ABSOLUTE_TESTS,
921 )
923 np.testing.assert_allclose(
924 multi_signals.arithmetical_operation(multi_signals, "+", False).range,
925 self._range_2 + self._range_2,
926 atol=TOLERANCE_ABSOLUTE_TESTS,
927 )
929 def test_is_uniform(self) -> None:
930 """
931 Test :meth:`colour.continuous.multi_signals.MultiSignals.is_uniform`
932 method.
933 """
935 assert self._multi_signals.is_uniform()
937 multi_signals = self._multi_signals.copy()
938 multi_signals[0.5] = 1.0
939 assert not multi_signals.is_uniform()
941 def test_copy(self) -> None:
942 """Test :func:`colour.continuous.multi_signals.MultiSignals.copy` method."""
944 assert self._multi_signals is not self._multi_signals.copy()
945 assert self._multi_signals == self._multi_signals.copy()
947 def test_multi_signals_unpack_data(self) -> None:
948 """
949 Test :func:`colour.continuous.multi_signals.MultiSignals.\
950multi_signals_unpack_data` method.
951 """
953 signals = MultiSignals.multi_signals_unpack_data(self._range_1)
954 assert list(signals.keys()) == ["0"]
955 np.testing.assert_array_equal(signals["0"].domain, self._domain_1)
956 np.testing.assert_array_equal(signals["0"].range, self._range_1)
958 signals = MultiSignals.multi_signals_unpack_data(self._range_1, self._domain_2)
959 assert list(signals.keys()) == ["0"]
960 np.testing.assert_array_equal(signals["0"].domain, self._domain_2)
961 np.testing.assert_array_equal(signals["0"].range, self._range_1)
963 signals = MultiSignals.multi_signals_unpack_data(
964 self._range_1, dict(zip(self._domain_2, self._range_1, strict=True)).keys()
965 )
966 np.testing.assert_array_equal(signals["0"].domain, self._domain_2)
968 signals = MultiSignals.multi_signals_unpack_data(self._range_2, self._domain_2)
969 assert list(signals.keys()) == ["0", "1", "2"]
970 np.testing.assert_array_equal(signals["0"].range, self._range_1)
971 np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10)
972 np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20)
974 signals = MultiSignals.multi_signals_unpack_data(
975 next(
976 iter(
977 MultiSignals.multi_signals_unpack_data(
978 dict(zip(self._domain_2, self._range_2, strict=True))
979 ).values()
980 )
981 )
982 )
983 np.testing.assert_array_equal(signals["0"].range, self._range_1)
985 signals = MultiSignals.multi_signals_unpack_data(
986 MultiSignals.multi_signals_unpack_data(
987 dict(zip(self._domain_2, self._range_2, strict=True))
988 ).values()
989 )
990 np.testing.assert_array_equal(signals["0"].range, self._range_1)
991 np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10)
992 np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20)
994 signals = MultiSignals.multi_signals_unpack_data(
995 dict(zip(self._domain_2, self._range_2, strict=True))
996 )
997 assert list(signals.keys()) == ["0", "1", "2"]
998 np.testing.assert_array_equal(signals["0"].range, self._range_1)
999 np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10)
1000 np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20)
1002 signals = MultiSignals.multi_signals_unpack_data(
1003 MultiSignals.multi_signals_unpack_data(
1004 dict(zip(self._domain_2, self._range_2, strict=True))
1005 )
1006 )
1007 assert list(signals.keys()) == ["0", "1", "2"]
1008 np.testing.assert_array_equal(signals["0"].range, self._range_1)
1009 np.testing.assert_array_equal(signals["1"].range, self._range_1 + 10)
1010 np.testing.assert_array_equal(signals["2"].range, self._range_1 + 20)
1012 signals = MultiSignals.multi_signals_unpack_data(
1013 dict(zip(self._domain_2, self._range_2, strict=True)),
1014 labels=["0", "0", "0"],
1015 )
1016 assert list(signals.keys()) == ["0 - 0", "0 - 1", "0 - 2"]
1018 if is_pandas_installed():
1019 from pandas import DataFrame, Series # noqa: PLC0415
1021 signals = MultiSignals.multi_signals_unpack_data(
1022 Series(dict(zip(self._domain_1, self._range_1, strict=True)))
1023 )
1024 assert list(signals.keys()) == ["0"]
1025 np.testing.assert_array_equal(signals["0"].domain, self._domain_1)
1026 np.testing.assert_array_equal(signals["0"].range, self._range_1)
1028 data = dict(zip(["a", "b", "c"], tsplit(self._range_2), strict=True))
1029 signals = MultiSignals.multi_signals_unpack_data(
1030 DataFrame(data, self._domain_1)
1031 )
1032 assert list(signals.keys()) == ["a", "b", "c"]
1033 np.testing.assert_array_equal(signals["a"].range, self._range_1)
1034 np.testing.assert_array_equal(signals["b"].range, self._range_1 + 10)
1035 np.testing.assert_array_equal(signals["c"].range, self._range_1 + 20)
1037 def test_fill_nan(self) -> None:
1038 """
1039 Test :meth:`colour.continuous.multi_signals.MultiSignals.fill_nan`
1040 method.
1041 """
1043 multi_signals = self._multi_signals.copy()
1045 multi_signals[3:7] = np.nan
1047 np.testing.assert_allclose(
1048 multi_signals.fill_nan().range,
1049 np.array(
1050 [
1051 [10.0, 20.0, 30.0],
1052 [20.0, 30.0, 40.0],
1053 [30.0, 40.0, 50.0],
1054 [40.0, 50.0, 60.0],
1055 [50.0, 60.0, 70.0],
1056 [60.0, 70.0, 80.0],
1057 [70.0, 80.0, 90.0],
1058 [80.0, 90.0, 100.0],
1059 [90.0, 100.0, 110.0],
1060 [100.0, 110.0, 120.0],
1061 ]
1062 ),
1063 atol=TOLERANCE_ABSOLUTE_TESTS,
1064 )
1066 multi_signals[3:7] = np.nan
1068 np.testing.assert_allclose(
1069 multi_signals.fill_nan(method="Constant").range,
1070 np.array(
1071 [
1072 [10.0, 20.0, 30.0],
1073 [20.0, 30.0, 40.0],
1074 [30.0, 40.0, 50.0],
1075 [0.0, 0.0, 0.0],
1076 [0.0, 0.0, 0.0],
1077 [0.0, 0.0, 0.0],
1078 [0.0, 0.0, 0.0],
1079 [80.0, 90.0, 100.0],
1080 [90.0, 100.0, 110.0],
1081 [100.0, 110.0, 120.0],
1082 ]
1083 ),
1084 atol=TOLERANCE_ABSOLUTE_TESTS,
1085 )
1087 def test_domain_distance(self) -> None:
1088 """
1089 Test :func:`colour.continuous.multi_signals.MultiSignals.\
1090domain_distance` method.
1091 """
1093 np.testing.assert_allclose(
1094 self._multi_signals.domain_distance(0.5),
1095 0.5,
1096 atol=TOLERANCE_ABSOLUTE_TESTS,
1097 )
1099 np.testing.assert_allclose(
1100 self._multi_signals.domain_distance(np.linspace(0, 9, 10) + 0.5),
1101 np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]),
1102 atol=TOLERANCE_ABSOLUTE_TESTS,
1103 )
1105 def test_to_dataframe(self) -> None:
1106 """
1107 Test :meth:`colour.continuous.multi_signals.MultiSignals.to_dataframe`
1108 method.
1109 """
1111 if is_pandas_installed():
1112 from pandas import DataFrame # noqa: PLC0415
1114 data = dict(zip(["a", "b", "c"], tsplit(self._range_2), strict=True))
1116 attest(
1117 MultiSignals(self._range_2, self._domain_2, labels=["a", "b", "c"])
1118 .to_dataframe()
1119 .equals(DataFrame(data, self._domain_2))
1120 )