sigima 0.0.1.dev0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sigima/__init__.py +142 -2
- sigima/client/__init__.py +105 -0
- sigima/client/base.py +780 -0
- sigima/client/remote.py +469 -0
- sigima/client/stub.py +814 -0
- sigima/client/utils.py +90 -0
- sigima/config.py +444 -0
- sigima/data/logo/Sigima.svg +135 -0
- sigima/data/tests/annotations.json +798 -0
- sigima/data/tests/curve_fitting/exponential_fit.txt +511 -0
- sigima/data/tests/curve_fitting/gaussian_fit.txt +100 -0
- sigima/data/tests/curve_fitting/piecewiseexponential_fit.txt +1022 -0
- sigima/data/tests/curve_fitting/polynomial_fit.txt +100 -0
- sigima/data/tests/curve_fitting/twohalfgaussian_fit.txt +1000 -0
- sigima/data/tests/curve_formats/bandwidth.txt +201 -0
- sigima/data/tests/curve_formats/boxcar.npy +0 -0
- sigima/data/tests/curve_formats/datetime.txt +1001 -0
- sigima/data/tests/curve_formats/dynamic_parameters.txt +4000 -0
- sigima/data/tests/curve_formats/fw1e2.txt +301 -0
- sigima/data/tests/curve_formats/fwhm.txt +319 -0
- sigima/data/tests/curve_formats/multiple_curves.csv +29 -0
- sigima/data/tests/curve_formats/noised_saw.mat +0 -0
- sigima/data/tests/curve_formats/oscilloscope.csv +111 -0
- sigima/data/tests/curve_formats/other/other2/recursive2.txt +5 -0
- sigima/data/tests/curve_formats/other/recursive1.txt +5 -0
- sigima/data/tests/curve_formats/paracetamol.npy +0 -0
- sigima/data/tests/curve_formats/paracetamol.txt +1010 -0
- sigima/data/tests/curve_formats/paracetamol_dx_dy.csv +1000 -0
- sigima/data/tests/curve_formats/paracetamol_dy.csv +1001 -0
- sigima/data/tests/curve_formats/pulse1.npy +0 -0
- sigima/data/tests/curve_formats/pulse2.npy +0 -0
- sigima/data/tests/curve_formats/simple.txt +5 -0
- sigima/data/tests/curve_formats/spectrum.mca +2139 -0
- sigima/data/tests/curve_formats/square2.npy +0 -0
- sigima/data/tests/curve_formats/step.npy +0 -0
- sigima/data/tests/fabry-perot1.jpg +0 -0
- sigima/data/tests/fabry-perot2.jpg +0 -0
- sigima/data/tests/flower.npy +0 -0
- sigima/data/tests/image_formats/NF 180338201.scor-data +11003 -0
- sigima/data/tests/image_formats/binary_image.npy +0 -0
- sigima/data/tests/image_formats/binary_image.png +0 -0
- sigima/data/tests/image_formats/centroid_test.npy +0 -0
- sigima/data/tests/image_formats/coordinated_text/complex_image.txt +10011 -0
- sigima/data/tests/image_formats/coordinated_text/complex_ref_image.txt +10010 -0
- sigima/data/tests/image_formats/coordinated_text/image.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image2.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_no_unit_no_label.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_nan.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_unit.txt +14 -0
- sigima/data/tests/image_formats/fiber.csv +480 -0
- sigima/data/tests/image_formats/fiber.jpg +0 -0
- sigima/data/tests/image_formats/fiber.png +0 -0
- sigima/data/tests/image_formats/fiber.txt +480 -0
- sigima/data/tests/image_formats/gaussian_spot_with_noise.npy +0 -0
- sigima/data/tests/image_formats/mr-brain.dcm +0 -0
- sigima/data/tests/image_formats/noised_gaussian.mat +0 -0
- sigima/data/tests/image_formats/sif_reader/nd_lum_image_no_glue.sif +0 -0
- sigima/data/tests/image_formats/sif_reader/raman1.sif +0 -0
- sigima/data/tests/image_formats/tiling.txt +10 -0
- sigima/data/tests/image_formats/uint16.tiff +0 -0
- sigima/data/tests/image_formats/uint8.tiff +0 -0
- sigima/data/tests/laser_beam/TEM00_z_13.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_18.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_23.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_30.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_35.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_40.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_45.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_50.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_55.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_60.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_65.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_70.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_75.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_80.jpg +0 -0
- sigima/enums.py +195 -0
- sigima/io/__init__.py +123 -0
- sigima/io/base.py +311 -0
- sigima/io/common/__init__.py +5 -0
- sigima/io/common/basename.py +164 -0
- sigima/io/common/converters.py +189 -0
- sigima/io/common/objmeta.py +181 -0
- sigima/io/common/textreader.py +58 -0
- sigima/io/convenience.py +157 -0
- sigima/io/enums.py +17 -0
- sigima/io/ftlab.py +395 -0
- sigima/io/image/__init__.py +9 -0
- sigima/io/image/base.py +177 -0
- sigima/io/image/formats.py +1016 -0
- sigima/io/image/funcs.py +414 -0
- sigima/io/signal/__init__.py +9 -0
- sigima/io/signal/base.py +129 -0
- sigima/io/signal/formats.py +290 -0
- sigima/io/signal/funcs.py +723 -0
- sigima/objects/__init__.py +260 -0
- sigima/objects/base.py +937 -0
- sigima/objects/image/__init__.py +88 -0
- sigima/objects/image/creation.py +556 -0
- sigima/objects/image/object.py +524 -0
- sigima/objects/image/roi.py +904 -0
- sigima/objects/scalar/__init__.py +57 -0
- sigima/objects/scalar/common.py +215 -0
- sigima/objects/scalar/geometry.py +502 -0
- sigima/objects/scalar/table.py +784 -0
- sigima/objects/shape.py +290 -0
- sigima/objects/signal/__init__.py +133 -0
- sigima/objects/signal/constants.py +27 -0
- sigima/objects/signal/creation.py +1428 -0
- sigima/objects/signal/object.py +444 -0
- sigima/objects/signal/roi.py +274 -0
- sigima/params.py +405 -0
- sigima/proc/__init__.py +96 -0
- sigima/proc/base.py +381 -0
- sigima/proc/decorator.py +330 -0
- sigima/proc/image/__init__.py +513 -0
- sigima/proc/image/arithmetic.py +335 -0
- sigima/proc/image/base.py +260 -0
- sigima/proc/image/detection.py +519 -0
- sigima/proc/image/edges.py +329 -0
- sigima/proc/image/exposure.py +406 -0
- sigima/proc/image/extraction.py +458 -0
- sigima/proc/image/filtering.py +219 -0
- sigima/proc/image/fourier.py +147 -0
- sigima/proc/image/geometry.py +661 -0
- sigima/proc/image/mathops.py +340 -0
- sigima/proc/image/measurement.py +195 -0
- sigima/proc/image/morphology.py +155 -0
- sigima/proc/image/noise.py +107 -0
- sigima/proc/image/preprocessing.py +182 -0
- sigima/proc/image/restoration.py +235 -0
- sigima/proc/image/threshold.py +217 -0
- sigima/proc/image/transformations.py +393 -0
- sigima/proc/signal/__init__.py +376 -0
- sigima/proc/signal/analysis.py +206 -0
- sigima/proc/signal/arithmetic.py +551 -0
- sigima/proc/signal/base.py +262 -0
- sigima/proc/signal/extraction.py +60 -0
- sigima/proc/signal/features.py +310 -0
- sigima/proc/signal/filtering.py +484 -0
- sigima/proc/signal/fitting.py +276 -0
- sigima/proc/signal/fourier.py +259 -0
- sigima/proc/signal/mathops.py +420 -0
- sigima/proc/signal/processing.py +580 -0
- sigima/proc/signal/stability.py +175 -0
- sigima/proc/title_formatting.py +227 -0
- sigima/proc/validation.py +272 -0
- sigima/tests/__init__.py +7 -0
- sigima/tests/common/__init__.py +0 -0
- sigima/tests/common/arithmeticparam_unit_test.py +26 -0
- sigima/tests/common/basename_unit_test.py +126 -0
- sigima/tests/common/client_unit_test.py +412 -0
- sigima/tests/common/converters_unit_test.py +77 -0
- sigima/tests/common/decorator_unit_test.py +176 -0
- sigima/tests/common/examples_unit_test.py +104 -0
- sigima/tests/common/kernel_normalization_unit_test.py +242 -0
- sigima/tests/common/roi_basic_unit_test.py +73 -0
- sigima/tests/common/roi_geometry_unit_test.py +171 -0
- sigima/tests/common/scalar_builder_unit_test.py +142 -0
- sigima/tests/common/scalar_unit_test.py +991 -0
- sigima/tests/common/shape_unit_test.py +183 -0
- sigima/tests/common/stat_unit_test.py +138 -0
- sigima/tests/common/title_formatting_unit_test.py +338 -0
- sigima/tests/common/tools_coordinates_unit_test.py +60 -0
- sigima/tests/common/transformations_unit_test.py +178 -0
- sigima/tests/common/validation_unit_test.py +205 -0
- sigima/tests/conftest.py +129 -0
- sigima/tests/data.py +998 -0
- sigima/tests/env.py +280 -0
- sigima/tests/guiutils.py +163 -0
- sigima/tests/helpers.py +532 -0
- sigima/tests/image/__init__.py +28 -0
- sigima/tests/image/binning_unit_test.py +128 -0
- sigima/tests/image/blob_detection_unit_test.py +312 -0
- sigima/tests/image/centroid_unit_test.py +170 -0
- sigima/tests/image/check_2d_array_unit_test.py +63 -0
- sigima/tests/image/contour_unit_test.py +172 -0
- sigima/tests/image/convolution_unit_test.py +178 -0
- sigima/tests/image/datatype_unit_test.py +67 -0
- sigima/tests/image/edges_unit_test.py +155 -0
- sigima/tests/image/enclosingcircle_unit_test.py +88 -0
- sigima/tests/image/exposure_unit_test.py +223 -0
- sigima/tests/image/fft2d_unit_test.py +189 -0
- sigima/tests/image/filtering_unit_test.py +166 -0
- sigima/tests/image/geometry_unit_test.py +654 -0
- sigima/tests/image/hough_circle_unit_test.py +147 -0
- sigima/tests/image/imageobj_unit_test.py +737 -0
- sigima/tests/image/morphology_unit_test.py +71 -0
- sigima/tests/image/noise_unit_test.py +57 -0
- sigima/tests/image/offset_correction_unit_test.py +72 -0
- sigima/tests/image/operation_unit_test.py +518 -0
- sigima/tests/image/peak2d_limits_unit_test.py +41 -0
- sigima/tests/image/peak2d_unit_test.py +133 -0
- sigima/tests/image/profile_unit_test.py +159 -0
- sigima/tests/image/projections_unit_test.py +121 -0
- sigima/tests/image/restoration_unit_test.py +141 -0
- sigima/tests/image/roi2dparam_unit_test.py +53 -0
- sigima/tests/image/roi_advanced_unit_test.py +588 -0
- sigima/tests/image/roi_grid_unit_test.py +279 -0
- sigima/tests/image/spectrum2d_unit_test.py +40 -0
- sigima/tests/image/threshold_unit_test.py +91 -0
- sigima/tests/io/__init__.py +0 -0
- sigima/tests/io/addnewformat_unit_test.py +125 -0
- sigima/tests/io/convenience_funcs_unit_test.py +470 -0
- sigima/tests/io/coordinated_text_format_unit_test.py +495 -0
- sigima/tests/io/datetime_csv_unit_test.py +198 -0
- sigima/tests/io/imageio_formats_test.py +41 -0
- sigima/tests/io/ioregistry_unit_test.py +69 -0
- sigima/tests/io/objmeta_unit_test.py +87 -0
- sigima/tests/io/readobj_unit_test.py +130 -0
- sigima/tests/io/readwriteobj_unit_test.py +67 -0
- sigima/tests/signal/__init__.py +0 -0
- sigima/tests/signal/analysis_unit_test.py +135 -0
- sigima/tests/signal/check_1d_arrays_unit_test.py +169 -0
- sigima/tests/signal/convolution_unit_test.py +404 -0
- sigima/tests/signal/datetime_unit_test.py +176 -0
- sigima/tests/signal/fft1d_unit_test.py +303 -0
- sigima/tests/signal/filters_unit_test.py +403 -0
- sigima/tests/signal/fitting_unit_test.py +929 -0
- sigima/tests/signal/fwhm_unit_test.py +111 -0
- sigima/tests/signal/noise_unit_test.py +128 -0
- sigima/tests/signal/offset_correction_unit_test.py +34 -0
- sigima/tests/signal/operation_unit_test.py +489 -0
- sigima/tests/signal/peakdetection_unit_test.py +145 -0
- sigima/tests/signal/processing_unit_test.py +657 -0
- sigima/tests/signal/pulse/__init__.py +112 -0
- sigima/tests/signal/pulse/crossing_times_unit_test.py +123 -0
- sigima/tests/signal/pulse/plateau_detection_unit_test.py +102 -0
- sigima/tests/signal/pulse/pulse_unit_test.py +1824 -0
- sigima/tests/signal/roi_advanced_unit_test.py +392 -0
- sigima/tests/signal/signalobj_unit_test.py +603 -0
- sigima/tests/signal/stability_unit_test.py +431 -0
- sigima/tests/signal/uncertainty_unit_test.py +611 -0
- sigima/tests/vistools.py +1030 -0
- sigima/tools/__init__.py +59 -0
- sigima/tools/checks.py +290 -0
- sigima/tools/coordinates.py +308 -0
- sigima/tools/datatypes.py +26 -0
- sigima/tools/image/__init__.py +97 -0
- sigima/tools/image/detection.py +451 -0
- sigima/tools/image/exposure.py +77 -0
- sigima/tools/image/extraction.py +48 -0
- sigima/tools/image/fourier.py +260 -0
- sigima/tools/image/geometry.py +190 -0
- sigima/tools/image/preprocessing.py +165 -0
- sigima/tools/signal/__init__.py +86 -0
- sigima/tools/signal/dynamic.py +254 -0
- sigima/tools/signal/features.py +135 -0
- sigima/tools/signal/filtering.py +171 -0
- sigima/tools/signal/fitting.py +1171 -0
- sigima/tools/signal/fourier.py +466 -0
- sigima/tools/signal/interpolation.py +70 -0
- sigima/tools/signal/peakdetection.py +126 -0
- sigima/tools/signal/pulse.py +1626 -0
- sigima/tools/signal/scaling.py +50 -0
- sigima/tools/signal/stability.py +258 -0
- sigima/tools/signal/windowing.py +90 -0
- sigima/worker.py +79 -0
- sigima-1.0.0.dist-info/METADATA +233 -0
- sigima-1.0.0.dist-info/RECORD +262 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/licenses/LICENSE +29 -29
- sigima-0.0.1.dev0.dist-info/METADATA +0 -60
- sigima-0.0.1.dev0.dist-info/RECORD +0 -6
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/WHEEL +0 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Unit tests for uncertainty propagation in signal operations
|
|
5
|
+
|
|
6
|
+
Features from signal processing functions that include uncertainty propagation.
|
|
7
|
+
This test covers the mathematical functions (sqrt, log10, exp, clip, absolute,
|
|
8
|
+
real, imag) and arithmetic operations (addition, average, product, difference,
|
|
9
|
+
constant operations).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import warnings
|
|
17
|
+
from typing import Callable
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
import sigima.enums
|
|
22
|
+
import sigima.objects
|
|
23
|
+
import sigima.params
|
|
24
|
+
import sigima.proc.signal
|
|
25
|
+
import sigima.tests.data
|
|
26
|
+
from sigima.tests.helpers import check_array_result
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def __create_signal_with_uncertainty() -> sigima.objects.SignalObj:
|
|
30
|
+
"""Create a signal with uncertainty data for testing."""
|
|
31
|
+
obj = sigima.tests.data.create_periodic_signal(sigima.objects.SignalTypes.COSINE)
|
|
32
|
+
obj.dy = 0.1 * np.abs(obj.y) + 0.01 # 10% relative + 0.01 absolute
|
|
33
|
+
return obj
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def __create_signal_without_uncertainty() -> sigima.objects.SignalObj:
|
|
37
|
+
"""Create a signal without uncertainty data for testing."""
|
|
38
|
+
obj = sigima.tests.data.create_periodic_signal(sigima.objects.SignalTypes.COSINE)
|
|
39
|
+
obj.dy = None
|
|
40
|
+
return obj
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def __verify_uncertainty_propagation(
|
|
44
|
+
func: Callable[[sigima.objects.SignalObj], sigima.objects.SignalObj],
|
|
45
|
+
param: sigima.params.GaussianParam
|
|
46
|
+
| sigima.params.MovingAverageParam
|
|
47
|
+
| sigima.params.MovingMedianParam
|
|
48
|
+
| None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Test uncertainty propagation for a given signal processing function."""
|
|
51
|
+
src = __create_signal_with_uncertainty()
|
|
52
|
+
if param is None:
|
|
53
|
+
result = func(src)
|
|
54
|
+
else:
|
|
55
|
+
result = func(src, param)
|
|
56
|
+
|
|
57
|
+
# Check that uncertainties are propagated (should be unchanged for filters)
|
|
58
|
+
assert result.dy is not None, "Uncertainty should be propagated"
|
|
59
|
+
check_array_result("Uncertainty propagation", result.dy, src.dy)
|
|
60
|
+
|
|
61
|
+
# Test without uncertainty
|
|
62
|
+
src = __create_signal_without_uncertainty()
|
|
63
|
+
result_no_unc = func(src)
|
|
64
|
+
assert result_no_unc.dy is None, (
|
|
65
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_exp_uncertainty_propagation() -> None:
|
|
70
|
+
"""Test uncertainty propagation for exponential function."""
|
|
71
|
+
# Test with uncertainty
|
|
72
|
+
src = __create_signal_with_uncertainty()
|
|
73
|
+
result = sigima.proc.signal.exp(src)
|
|
74
|
+
|
|
75
|
+
# Check result values
|
|
76
|
+
check_array_result("Exponential values", result.y, np.exp(src.y))
|
|
77
|
+
|
|
78
|
+
# Check uncertainty propagation: σ(eʸ) = eʸ * σ(y) = dst.y * σ(y)
|
|
79
|
+
expected_dy = np.abs(result.y) * src.dy
|
|
80
|
+
check_array_result("Exponential uncertainty propagation", result.dy, expected_dy)
|
|
81
|
+
|
|
82
|
+
# Test without uncertainty
|
|
83
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
84
|
+
result_no_unc = sigima.proc.signal.exp(src_no_unc)
|
|
85
|
+
assert result_no_unc.dy is None, (
|
|
86
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_sqrt_uncertainty_propagation() -> None:
|
|
91
|
+
"""Test uncertainty propagation for square root function."""
|
|
92
|
+
# Test with uncertainty
|
|
93
|
+
src = __create_signal_with_uncertainty()
|
|
94
|
+
|
|
95
|
+
# Suppress warnings for sqrt of negative values in test data
|
|
96
|
+
with warnings.catch_warnings():
|
|
97
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
98
|
+
result = sigima.proc.signal.sqrt(src)
|
|
99
|
+
|
|
100
|
+
# Check result values
|
|
101
|
+
with warnings.catch_warnings():
|
|
102
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
103
|
+
check_array_result("Square root values", result.y, np.sqrt(src.y))
|
|
104
|
+
|
|
105
|
+
# Check uncertainty propagation: σ(√y) = σ(y) / (2√y)
|
|
106
|
+
with warnings.catch_warnings():
|
|
107
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
108
|
+
expected_dy = src.dy / (2 * np.sqrt(src.y))
|
|
109
|
+
expected_dy[np.isinf(expected_dy) | np.isnan(expected_dy)] = np.nan
|
|
110
|
+
|
|
111
|
+
check_array_result("Square root uncertainty propagation", result.dy, expected_dy)
|
|
112
|
+
|
|
113
|
+
# Test without uncertainty
|
|
114
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
115
|
+
with warnings.catch_warnings():
|
|
116
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
117
|
+
result_no_unc = sigima.proc.signal.sqrt(src_no_unc)
|
|
118
|
+
assert result_no_unc.dy is None, (
|
|
119
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_power_uncertainty_propagation() -> None:
|
|
124
|
+
"""Test uncertainty propagation for power function."""
|
|
125
|
+
# Test with uncertainty
|
|
126
|
+
src = __create_signal_with_uncertainty()
|
|
127
|
+
p = 3.0
|
|
128
|
+
param = sigima.params.PowerParam.create(power=p)
|
|
129
|
+
result = sigima.proc.signal.power(src, param)
|
|
130
|
+
|
|
131
|
+
# Check result values
|
|
132
|
+
check_array_result("Power values", result.y, src.y**p)
|
|
133
|
+
|
|
134
|
+
# Check uncertainty propagation: σ(yᵖ) = |p * y^(p-1)| * σ(y)
|
|
135
|
+
with warnings.catch_warnings():
|
|
136
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
137
|
+
expected_dy = np.abs(p * src.y ** (p - 1)) * src.dy
|
|
138
|
+
expected_dy[np.isinf(expected_dy) | np.isnan(expected_dy)] = np.nan
|
|
139
|
+
|
|
140
|
+
check_array_result("Power uncertainty propagation", result.dy, expected_dy)
|
|
141
|
+
|
|
142
|
+
# Test without uncertainty
|
|
143
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
144
|
+
result_no_unc = sigima.proc.signal.power(src_no_unc, param)
|
|
145
|
+
assert result_no_unc.dy is None, (
|
|
146
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_log10_uncertainty_propagation() -> None:
|
|
151
|
+
"""Test uncertainty propagation for log10 function."""
|
|
152
|
+
# Test with uncertainty - use positive values to avoid log domain issues
|
|
153
|
+
src = __create_signal_with_uncertainty()
|
|
154
|
+
src.y = np.abs(src.y) + 1.0 # Ensure positive values
|
|
155
|
+
result = sigima.proc.signal.log10(src)
|
|
156
|
+
|
|
157
|
+
# Check result values
|
|
158
|
+
check_array_result("Log10 values", result.y, np.log10(src.y))
|
|
159
|
+
|
|
160
|
+
# Check uncertainty propagation: σ(log₁₀(y)) = σ(y) / (y * ln(10))
|
|
161
|
+
with warnings.catch_warnings():
|
|
162
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
163
|
+
expected_dy = src.dy / (src.y * np.log(10))
|
|
164
|
+
expected_dy[np.isinf(expected_dy) | np.isnan(expected_dy)] = np.nan
|
|
165
|
+
|
|
166
|
+
check_array_result("Log10 uncertainty propagation", result.dy, expected_dy)
|
|
167
|
+
|
|
168
|
+
# Test without uncertainty
|
|
169
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
170
|
+
src_no_unc.y = np.abs(src_no_unc.y) + 1.0 # Ensure positive values
|
|
171
|
+
result_no_unc = sigima.proc.signal.log10(src_no_unc)
|
|
172
|
+
assert result_no_unc.dy is None, (
|
|
173
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_clip_uncertainty_propagation() -> None:
|
|
178
|
+
"""Test uncertainty propagation for clipping function."""
|
|
179
|
+
# Test with uncertainty
|
|
180
|
+
src = __create_signal_with_uncertainty()
|
|
181
|
+
|
|
182
|
+
# Test clipping with both limits
|
|
183
|
+
param = sigima.params.ClipParam.create(lower=-0.5, upper=0.5)
|
|
184
|
+
result = sigima.proc.signal.clip(src, param)
|
|
185
|
+
|
|
186
|
+
# Check result values
|
|
187
|
+
expected_y = np.clip(src.y, param.lower, param.upper)
|
|
188
|
+
check_array_result("Clip values", result.y, expected_y)
|
|
189
|
+
|
|
190
|
+
# Check uncertainty propagation: σ(clip(y)) = σ(y) where not clipped,
|
|
191
|
+
# 0 where clipped
|
|
192
|
+
expected_dy = src.dy.copy()
|
|
193
|
+
expected_dy[src.y <= param.lower] = 0
|
|
194
|
+
expected_dy[src.y >= param.upper] = 0
|
|
195
|
+
check_array_result("Clip uncertainty propagation", result.dy, expected_dy)
|
|
196
|
+
|
|
197
|
+
# Test without uncertainty
|
|
198
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
199
|
+
result_no_unc = sigima.proc.signal.clip(src_no_unc, param)
|
|
200
|
+
assert result_no_unc.dy is None, (
|
|
201
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_normalize_uncertainty_propagation() -> None:
|
|
206
|
+
"""Test uncertainty propagation for normalization function."""
|
|
207
|
+
# Test different normalization methods
|
|
208
|
+
for method in sigima.enums.NormalizationMethod:
|
|
209
|
+
# Test with uncertainty
|
|
210
|
+
src = __create_signal_with_uncertainty()
|
|
211
|
+
param = sigima.params.NormalizeParam()
|
|
212
|
+
param.method = method
|
|
213
|
+
result = sigima.proc.signal.normalize(src, param)
|
|
214
|
+
|
|
215
|
+
# Check that uncertainties are propagated appropriately for each method
|
|
216
|
+
assert result.dy is not None, f"Uncertainty should be propagated for {method}"
|
|
217
|
+
|
|
218
|
+
# For most methods, uncertainty should be non-zero where input uncertainty
|
|
219
|
+
# exists
|
|
220
|
+
if method != sigima.enums.NormalizationMethod.AMPLITUDE:
|
|
221
|
+
# For non-amplitude methods, check that uncertainties exist and are finite
|
|
222
|
+
assert np.any(np.isfinite(result.dy)), (
|
|
223
|
+
f"Some uncertainties should be finite for {method}"
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
# For amplitude normalization, check the specific uncertainty formula
|
|
227
|
+
# σ(amplitude_norm(y)) = σ(y) / (max(y) - min(y))
|
|
228
|
+
denom = np.max(src.y) - np.min(src.y)
|
|
229
|
+
if denom != 0:
|
|
230
|
+
expected_dy = src.dy / denom
|
|
231
|
+
check_array_result(
|
|
232
|
+
"Amplitude normalize uncertainty propagation",
|
|
233
|
+
result.dy,
|
|
234
|
+
expected_dy,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Test without uncertainty
|
|
238
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
239
|
+
result_no_unc = sigima.proc.signal.normalize(src_no_unc, param)
|
|
240
|
+
assert result_no_unc.dy is None, (
|
|
241
|
+
f"Uncertainty should be None when input has no uncertainty for {method}"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_derivative_uncertainty_propagation() -> None:
|
|
246
|
+
"""Test uncertainty propagation for derivative function."""
|
|
247
|
+
# Test with uncertainty
|
|
248
|
+
src = __create_signal_with_uncertainty()
|
|
249
|
+
result = sigima.proc.signal.derivative(src)
|
|
250
|
+
|
|
251
|
+
# Check that uncertainties are propagated
|
|
252
|
+
assert result.dy is not None, "Uncertainty should be propagated"
|
|
253
|
+
|
|
254
|
+
# For numerical derivatives, the uncertainty depends on the finite difference scheme
|
|
255
|
+
# numpy.gradient uses central differences for interior points:
|
|
256
|
+
# dy/dx ≈ (y[i+1] - y[i-1]) / (x[i+1] - x[i-1])
|
|
257
|
+
# So σ(dy/dx) ≈ sqrt(σ(y[i+1])² + σ(y[i-1])²) / (x[i+1] - x[i-1])
|
|
258
|
+
|
|
259
|
+
# For a more general test, we verify that:
|
|
260
|
+
# 1. Uncertainties exist and are finite where input uncertainties exist
|
|
261
|
+
# 2. The uncertainty scaling is reasonable compared to input uncertainties
|
|
262
|
+
assert np.any(np.isfinite(result.dy)), "Some uncertainties should be finite"
|
|
263
|
+
|
|
264
|
+
# The derivative uncertainty should generally be larger than input uncertainty
|
|
265
|
+
# due to the division by dx (assuming dx < 1 for typical signals)
|
|
266
|
+
x = src.x
|
|
267
|
+
typical_dx = np.median(np.diff(x))
|
|
268
|
+
if typical_dx > 0:
|
|
269
|
+
# Expected rough scaling: derivative uncertainty ~ input uncertainty / dx
|
|
270
|
+
expected_scale = 1.0 / typical_dx
|
|
271
|
+
# Allow for significant variation due to the complexity of gradient calculation
|
|
272
|
+
max_ratio = np.nanmax(result.dy / src.dy)
|
|
273
|
+
assert max_ratio > 0.1 * expected_scale, (
|
|
274
|
+
f"Derivative uncertainty scaling seems too small: {max_ratio} vs "
|
|
275
|
+
f"expected ~{expected_scale}"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Test without uncertainty
|
|
279
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
280
|
+
result_no_unc = sigima.proc.signal.derivative(src_no_unc)
|
|
281
|
+
assert result_no_unc.dy is None, (
|
|
282
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_integral_uncertainty_propagation() -> None:
|
|
287
|
+
"""Test uncertainty propagation for integral function."""
|
|
288
|
+
# Test with uncertainty
|
|
289
|
+
src = __create_signal_with_uncertainty()
|
|
290
|
+
result = sigima.proc.signal.integral(src)
|
|
291
|
+
|
|
292
|
+
# Check that uncertainties are propagated
|
|
293
|
+
assert result.dy is not None, "Uncertainty should be propagated"
|
|
294
|
+
|
|
295
|
+
# For cumulative integration, uncertainties should accumulate
|
|
296
|
+
# The first point should have zero uncertainty (initial value)
|
|
297
|
+
assert result.dy[0] == 0.0, "Initial integral value should have zero uncertainty"
|
|
298
|
+
|
|
299
|
+
# For cumulative trapezoidal integration, uncertainties should generally increase
|
|
300
|
+
# as we accumulate more measurements
|
|
301
|
+
# Check that uncertainties are non-decreasing (allowing for numerical precision)
|
|
302
|
+
diff_dy = np.diff(result.dy)
|
|
303
|
+
# Allow small negative differences due to numerical precision
|
|
304
|
+
assert np.all(diff_dy >= -1e-10), "Integral uncertainties should generally increase"
|
|
305
|
+
|
|
306
|
+
# The integral uncertainties should be finite and non-negative
|
|
307
|
+
assert np.all(np.isfinite(result.dy)), "All integral uncertainties should be finite"
|
|
308
|
+
assert np.all(result.dy >= 0), "All integral uncertainties should be non-negative"
|
|
309
|
+
|
|
310
|
+
# Integral uncertainty should be positive (assuming we have some integration range)
|
|
311
|
+
max_integral_uncertainty = np.max(result.dy)
|
|
312
|
+
assert max_integral_uncertainty > 0, (
|
|
313
|
+
"Maximum integral uncertainty should be positive"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Validate the uncertainty propagation formula implementation
|
|
317
|
+
# The integral function uses: σ(∫y dx) ≈ √(Σ(σ(y_i) * Δx_i)²) for trapezoidal rule
|
|
318
|
+
# Specifically: dy_squared = src.dy[:-1]² + src.dy[1:]²
|
|
319
|
+
# and dst.dy[1:] = √(cumsum(dy_squared * dx² / 4))
|
|
320
|
+
dx = np.diff(src.x)
|
|
321
|
+
dy_squared = src.dy[:-1] ** 2 + src.dy[1:] ** 2
|
|
322
|
+
expected_uncertainties = np.zeros_like(result.dy)
|
|
323
|
+
expected_uncertainties[0] = 0.0 # Initial value
|
|
324
|
+
expected_uncertainties[1:] = np.sqrt(np.cumsum(dy_squared * (dx**2) / 4))
|
|
325
|
+
|
|
326
|
+
# The computed uncertainties should match the expected formula
|
|
327
|
+
check_array_result(
|
|
328
|
+
"Integral uncertainty propagation", result.dy, expected_uncertainties
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Test without uncertainty
|
|
332
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
333
|
+
result_no_unc = sigima.proc.signal.integral(src_no_unc)
|
|
334
|
+
assert result_no_unc.dy is None, (
|
|
335
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def test_calibration_uncertainty_propagation() -> None:
|
|
340
|
+
"""Test uncertainty propagation for calibration function."""
|
|
341
|
+
# Test with uncertainty
|
|
342
|
+
src = __create_signal_with_uncertainty()
|
|
343
|
+
# Add X uncertainty for testing X-axis calibration
|
|
344
|
+
src.dx = 0.05 * np.abs(src.x) + 0.001 # 5% relative + 0.001 absolute
|
|
345
|
+
|
|
346
|
+
# Test Y-axis calibration: y' = a*y + b
|
|
347
|
+
a, b = 2.5, 0.3
|
|
348
|
+
param = sigima.params.XYCalibrateParam.create(axis="y", a=a, b=b)
|
|
349
|
+
result = sigima.proc.signal.calibration(src, param)
|
|
350
|
+
|
|
351
|
+
# Check uncertainty propagation: σ(a*y + b) = |a| * σ(y)
|
|
352
|
+
expected_dy = np.abs(a) * src.dy
|
|
353
|
+
check_array_result(
|
|
354
|
+
"Y-axis calibration uncertainty propagation", result.dy, expected_dy
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# Test X-axis calibration: x' = a*x + b
|
|
358
|
+
param = sigima.params.XYCalibrateParam.create(axis="x", a=a, b=b)
|
|
359
|
+
result = sigima.proc.signal.calibration(src, param)
|
|
360
|
+
|
|
361
|
+
# Check X uncertainty propagation: σ(a*x + b) = |a| * σ(x)
|
|
362
|
+
if src.dx is not None:
|
|
363
|
+
expected_dx = np.abs(a) * src.dx
|
|
364
|
+
check_array_result(
|
|
365
|
+
"X-axis calibration uncertainty propagation", result.dx, expected_dx
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Y uncertainties should remain the same for x-axis calibration
|
|
369
|
+
check_array_result("X-axis calibration dy unchanged", result.dy, src.dy)
|
|
370
|
+
|
|
371
|
+
# Test with negative scaling factor to check absolute value
|
|
372
|
+
a_neg = -1.5
|
|
373
|
+
param_neg = sigima.params.XYCalibrateParam.create(axis="y", a=a_neg, b=b)
|
|
374
|
+
result_neg = sigima.proc.signal.calibration(src, param_neg)
|
|
375
|
+
expected_dy_neg = np.abs(a_neg) * src.dy
|
|
376
|
+
check_array_result(
|
|
377
|
+
"Y-axis calibration negative scaling uncertainty",
|
|
378
|
+
result_neg.dy,
|
|
379
|
+
expected_dy_neg,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# Test without uncertainty
|
|
383
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
384
|
+
result_no_unc = sigima.proc.signal.calibration(src_no_unc, param)
|
|
385
|
+
assert result_no_unc.dy is None, (
|
|
386
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def test_absolute_uncertainty_propagation() -> None:
|
|
391
|
+
"""Test uncertainty propagation for absolute value function."""
|
|
392
|
+
__verify_uncertainty_propagation(sigima.proc.signal.absolute)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def test_real_uncertainty_propagation() -> None:
|
|
396
|
+
"""Test uncertainty propagation for real part function."""
|
|
397
|
+
__verify_uncertainty_propagation(sigima.proc.signal.real)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def test_imag_uncertainty_propagation() -> None:
|
|
401
|
+
"""Test uncertainty propagation for imaginary part function."""
|
|
402
|
+
# Test with uncertainty
|
|
403
|
+
src = __create_signal_with_uncertainty()
|
|
404
|
+
result = sigima.proc.signal.imag(src)
|
|
405
|
+
|
|
406
|
+
# Check result values
|
|
407
|
+
check_array_result("Imaginary part values", result.y, np.imag(src.y))
|
|
408
|
+
|
|
409
|
+
# Check uncertainty propagation: uncertainties unchanged for imaginary part
|
|
410
|
+
check_array_result("Imaginary part uncertainty propagation", result.dy, src.dy)
|
|
411
|
+
|
|
412
|
+
# Test without uncertainty
|
|
413
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
414
|
+
result_no_unc = sigima.proc.signal.imag(src_no_unc)
|
|
415
|
+
assert result_no_unc.dy is None, (
|
|
416
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def test_is_uncertainty_data_available() -> None:
|
|
421
|
+
"""Test the is_uncertainty_data_available helper function."""
|
|
422
|
+
# Single signal with uncertainty
|
|
423
|
+
src_with = __create_signal_with_uncertainty()
|
|
424
|
+
assert sigima.proc.signal.is_uncertainty_data_available(src_with), (
|
|
425
|
+
"Should return True for signal with uncertainty"
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Single signal without uncertainty
|
|
429
|
+
src_without = __create_signal_without_uncertainty()
|
|
430
|
+
assert not sigima.proc.signal.is_uncertainty_data_available(src_without), (
|
|
431
|
+
"Should return False for signal without uncertainty"
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# List of signals - all with uncertainty
|
|
435
|
+
src_list_with = [__create_signal_with_uncertainty() for _ in range(3)]
|
|
436
|
+
assert sigima.proc.signal.is_uncertainty_data_available(src_list_with), (
|
|
437
|
+
"Should return True for list where all signals have uncertainty"
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
# List of signals - mixed
|
|
441
|
+
src_list_mixed = [
|
|
442
|
+
__create_signal_with_uncertainty(),
|
|
443
|
+
__create_signal_without_uncertainty(),
|
|
444
|
+
]
|
|
445
|
+
assert not sigima.proc.signal.is_uncertainty_data_available(src_list_mixed), (
|
|
446
|
+
"Should return False for list with mixed uncertainty availability"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# List of signals - all without uncertainty
|
|
450
|
+
src_list_without = [__create_signal_without_uncertainty() for _ in range(3)]
|
|
451
|
+
assert not sigima.proc.signal.is_uncertainty_data_available(src_list_without), (
|
|
452
|
+
"Should return False for list where no signals have uncertainty"
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def test_inverse_uncertainty_propagation() -> None:
|
|
457
|
+
"""Test uncertainty propagation for signal inversion."""
|
|
458
|
+
# Test with uncertainty
|
|
459
|
+
src = __create_signal_with_uncertainty()
|
|
460
|
+
# Ensure values are not too close to zero to avoid division issues
|
|
461
|
+
src.y = src.y + 2.0 # Shift away from zero
|
|
462
|
+
result = sigima.proc.signal.inverse(src)
|
|
463
|
+
|
|
464
|
+
# Check result values
|
|
465
|
+
with warnings.catch_warnings():
|
|
466
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
467
|
+
expected_y = 1.0 / src.y
|
|
468
|
+
expected_y[np.isinf(expected_y)] = np.nan
|
|
469
|
+
check_array_result("Inverse values", result.y, expected_y)
|
|
470
|
+
|
|
471
|
+
# Check uncertainty propagation: σ(1/y) = |1/y| * σ(y) / |y| = σ(y) / |y|²
|
|
472
|
+
with warnings.catch_warnings():
|
|
473
|
+
warnings.simplefilter("ignore", category=RuntimeWarning)
|
|
474
|
+
expected_dy = np.abs(result.y) * src.dy / np.abs(src.y)
|
|
475
|
+
expected_dy[np.isinf(expected_dy)] = np.nan
|
|
476
|
+
check_array_result("Inverse uncertainty propagation", result.dy, expected_dy)
|
|
477
|
+
|
|
478
|
+
# Test without uncertainty
|
|
479
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
480
|
+
src_no_unc.y = src_no_unc.y + 2.0 # Shift away from zero
|
|
481
|
+
result_no_unc = sigima.proc.signal.inverse(src_no_unc)
|
|
482
|
+
assert result_no_unc.dy is None, (
|
|
483
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def test_gaussian_filter_uncertainty_propagation() -> None:
|
|
488
|
+
"""Test uncertainty propagation for Gaussian filter."""
|
|
489
|
+
param = sigima.params.GaussianParam.create(sigma=2.0)
|
|
490
|
+
__verify_uncertainty_propagation(sigima.proc.signal.gaussian_filter, param)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def test_wiener_filter_uncertainty_propagation() -> None:
|
|
494
|
+
"""Test uncertainty propagation for Wiener filter."""
|
|
495
|
+
__verify_uncertainty_propagation(sigima.proc.signal.wiener)
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def test_moving_average_uncertainty_propagation() -> None:
|
|
499
|
+
"""Test uncertainty propagation for moving average filter."""
|
|
500
|
+
param = sigima.params.MovingAverageParam.create(n=5)
|
|
501
|
+
__verify_uncertainty_propagation(sigima.proc.signal.moving_average, param)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def test_moving_median_uncertainty_propagation() -> None:
|
|
505
|
+
"""Test uncertainty propagation for moving median filter."""
|
|
506
|
+
param = sigima.params.MovingMedianParam.create(n=5)
|
|
507
|
+
__verify_uncertainty_propagation(sigima.proc.signal.moving_median, param)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def test_wrap1to1func_basic_behavior() -> None:
|
|
511
|
+
"""Test basic Wrap1to1Func behavior with uncertainty propagation.
|
|
512
|
+
|
|
513
|
+
Wrap1to1Func should preserve uncertainty unchanged for any wrapped function.
|
|
514
|
+
"""
|
|
515
|
+
# Test with a mathematical function (np.sin)
|
|
516
|
+
# Note: This tests the wrapper behavior, not the direct sin function
|
|
517
|
+
compute_sin_wrapped = sigima.proc.signal.Wrap1to1Func(np.sin)
|
|
518
|
+
|
|
519
|
+
# Test with uncertainty
|
|
520
|
+
src = __create_signal_with_uncertainty()
|
|
521
|
+
result = compute_sin_wrapped(src)
|
|
522
|
+
|
|
523
|
+
# Check result values
|
|
524
|
+
check_array_result("Wrapped sin values", result.y, np.sin(src.y))
|
|
525
|
+
|
|
526
|
+
# Check uncertainty propagation (should be unchanged when using Wrap1to1Func)
|
|
527
|
+
check_array_result("Wrapped sin uncertainty propagation", result.dy, src.dy)
|
|
528
|
+
|
|
529
|
+
# Test with a custom function
|
|
530
|
+
def custom_multiply(y):
|
|
531
|
+
"""Custom function: multiply by 3."""
|
|
532
|
+
return 3 * y
|
|
533
|
+
|
|
534
|
+
compute_custom = sigima.proc.signal.Wrap1to1Func(custom_multiply)
|
|
535
|
+
|
|
536
|
+
result_custom = compute_custom(src)
|
|
537
|
+
|
|
538
|
+
# Check result values
|
|
539
|
+
check_array_result("Custom multiply values", result_custom.y, 3 * src.y)
|
|
540
|
+
|
|
541
|
+
# Check uncertainty propagation (should be unchanged for any wrapped function)
|
|
542
|
+
check_array_result(
|
|
543
|
+
"Custom multiply uncertainty propagation", result_custom.dy, src.dy
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
# Test without uncertainty
|
|
547
|
+
src_no_unc = __create_signal_without_uncertainty()
|
|
548
|
+
result_no_unc = compute_custom(src_no_unc)
|
|
549
|
+
assert result_no_unc.dy is None, (
|
|
550
|
+
"Uncertainty should be None when input has no uncertainty"
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def test_wrap1to1func_with_args_kwargs() -> None:
|
|
555
|
+
"""Test Wrap1to1Func with additional args and kwargs."""
|
|
556
|
+
|
|
557
|
+
def power_func(y, power=2):
|
|
558
|
+
"""Raise y to a power."""
|
|
559
|
+
return y**power
|
|
560
|
+
|
|
561
|
+
# Test with power=3 using kwargs
|
|
562
|
+
compute_power = sigima.proc.signal.Wrap1to1Func(power_func, power=3)
|
|
563
|
+
|
|
564
|
+
src = __create_signal_with_uncertainty()
|
|
565
|
+
result = compute_power(src)
|
|
566
|
+
|
|
567
|
+
# Check result values
|
|
568
|
+
check_array_result("Power 3 values", result.y, src.y**3)
|
|
569
|
+
|
|
570
|
+
# Check uncertainty propagation (should be unchanged when using Wrap1to1Func)
|
|
571
|
+
# Note: This is different from the mathematical uncertainty propagation
|
|
572
|
+
# which would be σ(y³) = 3 * y² * σ(y)
|
|
573
|
+
check_array_result("Power 3 uncertainty propagation", result.dy, src.dy)
|
|
574
|
+
|
|
575
|
+
# Test with positional arguments
|
|
576
|
+
def multiply_add(y, multiplier, addend):
|
|
577
|
+
"""Custom function: y * multiplier + addend."""
|
|
578
|
+
return y * multiplier + addend
|
|
579
|
+
|
|
580
|
+
compute_multiply_add = sigima.proc.signal.Wrap1to1Func(multiply_add, 2, addend=5)
|
|
581
|
+
|
|
582
|
+
result_multiply_add = compute_multiply_add(src)
|
|
583
|
+
|
|
584
|
+
# Check result values
|
|
585
|
+
expected_y = src.y * 2 + 5
|
|
586
|
+
check_array_result("Multiply-add values", result_multiply_add.y, expected_y)
|
|
587
|
+
|
|
588
|
+
# Check uncertainty propagation (preserved unchanged)
|
|
589
|
+
check_array_result(
|
|
590
|
+
"Multiply-add uncertainty propagation", result_multiply_add.dy, src.dy
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
if __name__ == "__main__":
|
|
595
|
+
test_sqrt_uncertainty_propagation()
|
|
596
|
+
test_log10_uncertainty_propagation()
|
|
597
|
+
test_exp_uncertainty_propagation()
|
|
598
|
+
test_clip_uncertainty_propagation()
|
|
599
|
+
test_derivative_uncertainty_propagation()
|
|
600
|
+
test_integral_uncertainty_propagation()
|
|
601
|
+
test_absolute_uncertainty_propagation()
|
|
602
|
+
test_real_uncertainty_propagation()
|
|
603
|
+
test_imag_uncertainty_propagation()
|
|
604
|
+
test_is_uncertainty_data_available()
|
|
605
|
+
test_inverse_uncertainty_propagation()
|
|
606
|
+
test_gaussian_filter_uncertainty_propagation()
|
|
607
|
+
test_wiener_filter_uncertainty_propagation()
|
|
608
|
+
test_moving_average_uncertainty_propagation()
|
|
609
|
+
test_moving_median_uncertainty_propagation()
|
|
610
|
+
test_wrap1to1func_basic_behavior()
|
|
611
|
+
test_wrap1to1func_with_args_kwargs()
|