summaryrefslogtreecommitdiff
path: root/silx/math
diff options
context:
space:
mode:
Diffstat (limited to 'silx/math')
-rw-r--r--silx/math/__init__.py39
-rw-r--r--silx/math/calibration.py180
-rw-r--r--silx/math/chistogramnd.pyx1251
-rw-r--r--silx/math/chistogramnd_lut.pyx435
-rw-r--r--silx/math/colormap.pyx559
-rw-r--r--silx/math/combo.pyx329
-rw-r--r--silx/math/fft/__init__.py8
-rw-r--r--silx/math/fft/basefft.py146
-rw-r--r--silx/math/fft/clfft.py286
-rw-r--r--silx/math/fft/cufft.py253
-rw-r--r--silx/math/fft/fft.py96
-rw-r--r--silx/math/fft/fftw.py214
-rw-r--r--silx/math/fft/npfft.py124
-rw-r--r--silx/math/fft/setup.py41
-rw-r--r--silx/math/fft/test/__init__.py25
-rw-r--r--silx/math/fft/test/test_fft.py270
-rw-r--r--silx/math/fit/__init__.py39
-rw-r--r--silx/math/fit/bgtheories.py440
-rw-r--r--silx/math/fit/filters.pyx416
-rw-r--r--silx/math/fit/filters/include/filters.h45
-rw-r--r--silx/math/fit/filters/src/smoothnd.c317
-rw-r--r--silx/math/fit/filters/src/snip1d.c149
-rw-r--r--silx/math/fit/filters/src/snip2d.c96
-rw-r--r--silx/math/fit/filters/src/snip3d.c186
-rw-r--r--silx/math/fit/filters/src/strip.c118
-rw-r--r--silx/math/fit/filters_wrapper.pxd71
-rw-r--r--silx/math/fit/fitmanager.py1087
-rw-r--r--silx/math/fit/fittheories.py1374
-rw-r--r--silx/math/fit/fittheory.py161
-rw-r--r--silx/math/fit/functions.pyx985
-rw-r--r--silx/math/fit/functions/include/functions.h68
-rw-r--r--silx/math/fit/functions/src/funs.c1265
-rw-r--r--silx/math/fit/functions_wrapper.pxd170
-rw-r--r--silx/math/fit/leastsq.py901
-rw-r--r--silx/math/fit/peaks.pyx175
-rw-r--r--silx/math/fit/peaks/include/peaks.h32
-rw-r--r--silx/math/fit/peaks/src/peaks.c255
-rw-r--r--silx/math/fit/peaks_wrapper.pxd41
-rw-r--r--silx/math/fit/setup.py85
-rw-r--r--silx/math/fit/test/__init__.py46
-rw-r--r--silx/math/fit/test/test_bgtheories.py169
-rw-r--r--silx/math/fit/test/test_filters.py137
-rw-r--r--silx/math/fit/test/test_fit.py387
-rw-r--r--silx/math/fit/test/test_fitmanager.py513
-rw-r--r--silx/math/fit/test/test_functions.py272
-rw-r--r--silx/math/fit/test/test_peaks.py146
-rw-r--r--silx/math/histogram.py593
-rw-r--r--silx/math/histogramnd/include/histogramnd_c.h313
-rw-r--r--silx/math/histogramnd/include/msvc/stdint.h247
-rw-r--r--silx/math/histogramnd/include/templates.h30
-rw-r--r--silx/math/histogramnd/src/histogramnd_c.c301
-rw-r--r--silx/math/histogramnd/src/histogramnd_template.c260
-rw-r--r--silx/math/histogramnd_c.pxd299
-rw-r--r--silx/math/include/math_compatibility.h53
-rw-r--r--silx/math/interpolate.pyx165
-rw-r--r--silx/math/marchingcubes.pyx246
-rw-r--r--silx/math/marchingcubes/mc.hpp724
-rw-r--r--silx/math/marchingcubes/mc_lut.cpp316
-rw-r--r--silx/math/math_compatibility.pxd35
-rw-r--r--silx/math/mc.pxd51
-rw-r--r--silx/math/medianfilter/__init__.py30
-rw-r--r--silx/math/medianfilter/include/median_filter.hpp284
-rw-r--r--silx/math/medianfilter/median_filter.pxd42
-rw-r--r--silx/math/medianfilter/medianfilter.pyx496
-rw-r--r--silx/math/medianfilter/setup.py59
-rw-r--r--silx/math/medianfilter/test/__init__.py36
-rw-r--r--silx/math/medianfilter/test/benchmark.py122
-rw-r--r--silx/math/medianfilter/test/test_medianfilter.py740
-rw-r--r--silx/math/setup.py99
-rw-r--r--silx/math/test/__init__.py58
-rw-r--r--silx/math/test/benchmark_combo.py203
-rw-r--r--silx/math/test/histo_benchmarks.py269
-rw-r--r--silx/math/test/test_HistogramndLut_nominal.py587
-rw-r--r--silx/math/test/test_calibration.py158
-rw-r--r--silx/math/test/test_colormap.py266
-rw-r--r--silx/math/test/test_combo.py218
-rw-r--r--silx/math/test/test_histogramnd_error.py535
-rw-r--r--silx/math/test/test_histogramnd_nominal.py949
-rw-r--r--silx/math/test/test_histogramnd_vs_np.py848
-rw-r--r--silx/math/test/test_interpolate.py136
-rw-r--r--silx/math/test/test_marchingcubes.py188
81 files changed, 0 insertions, 24358 deletions
diff --git a/silx/math/__init__.py b/silx/math/__init__.py
deleted file mode 100644
index d8b7d81..0000000
--- a/silx/math/__init__.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""This package provides some processing functions for 1D, 2D, 3D or nD arrays.
-
-For additional processing functions dedicated to 2D images,
-see the silx.image package.
-For OpenCL-based processing functions see the silx.opencl package.
-
-See silx documentation: http://www.silx.org/doc/silx/latest/
-"""
-
-__authors__ = ["D. Naudet", "V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "11/05/2017"
-
-from .histogram import Histogramnd # noqa
-from .histogram import HistogramndLut # noqa
-from .medianfilter import medfilt, medfilt1d, medfilt2d
diff --git a/silx/math/calibration.py b/silx/math/calibration.py
deleted file mode 100644
index 658e2dc..0000000
--- a/silx/math/calibration.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-This module provides classes to calibrate data.
-
-Classes
--------
-
-- :class:`NoCalibration`
-- :class:`LinearCalibration`
-- :class:`ArrayCalibration`
-
-"""
-import numpy
-
-
-class AbstractCalibration(object):
- """A calibration is a transformation to be applied to an axis (i.e. a 1D array).
-
- """
- def __init__(self):
- super(AbstractCalibration, self).__init__()
-
- def __call__(self, x):
- """Apply calibration to an axis or to a value.
-
- :param x: Axis (1-D array), or value"""
- raise NotImplementedError(
- "AbstractCalibration can not be used directly. " +
- "You must subclass it and implement __call__")
-
- def is_affine(self):
- """Returns True for an affine calibration of the form
- :math:`x \\mapsto a + b * x`, or else False.
- """
- return False
-
- def get_slope(self):
- raise NotImplementedError(
- "get_slope is implemented only for affine calibrations")
-
-
-class NoCalibration(AbstractCalibration):
- """No calibration :math:`x \\mapsto x`
- """
- def __init__(self):
- super(NoCalibration, self).__init__()
-
- def __call__(self, x):
- return x
-
- def is_affine(self):
- return True
-
- def get_slope(self):
- return 1.
-
-
-class LinearCalibration(AbstractCalibration):
- """Linear calibration :math:`x \\mapsto a + b x`,
- where *a* is the y-intercept and *b* is the slope.
-
- :param y_intercept: y-intercept
- :param slope: Slope of the affine transformation
- """
- def __init__(self, y_intercept, slope):
- super(LinearCalibration, self).__init__()
- self.constant = y_intercept
- self.slope = slope
-
- def __call__(self, x):
- return self.constant + self.slope * x
-
- def is_affine(self):
- return True
-
- def get_slope(self):
- return self.slope
-
-
-class ArrayCalibration(AbstractCalibration):
- """One-to-one mapping calibration, defined by an array *x'*,
- such as :math:`x \\mapsto x'`.
-
- This calibration can only be applied to x arrays of the same length as the
- calibration array *x'*.
- It is typically applied to an axis of indices or
- channels (:math:`0, 1, ..., n-1`).
-
- :param x1: Calibration array"""
- def __init__(self, x1):
- super(ArrayCalibration, self).__init__()
- if not isinstance(x1, (list, tuple)) and not hasattr(x1, "shape"):
- raise TypeError(
- "The calibration array must be a sequence (list, dataset, array)")
- self.calibration_array = numpy.array(x1)
- self._is_affine = None
-
- def __call__(self, x):
- # calibrate the entire axis
- if isinstance(x, (list, tuple, numpy.ndarray)) and \
- len(self.calibration_array) == len(x):
- return self.calibration_array
- # calibrate one value, by index
- if isinstance(x, int) and x < len(self.calibration_array):
- return self.calibration_array[x]
- raise ValueError("ArrayCalibration must be applied to array of same size "
- "or to index.")
-
- def is_affine(self):
- """If all values in the calibration array are regularly spaced,
- return True."""
- if self._is_affine is None:
- delta_x = self.calibration_array[1:] - self.calibration_array[:-1]
- # use a less strict relative tolerance to account for rounding errors
- # e.g. when using float64 into float32 (see #1823)
- if not numpy.isclose(delta_x, delta_x[0], rtol=1e-4).all():
- self._is_affine = False
- else:
- self._is_affine = True
- return self._is_affine
-
- def get_slope(self):
- """If the calibration array is regularly spaced, return the spacing."""
- if not self.is_affine():
- raise AttributeError(
- "get_slope only makes sense for affine transformations"
- )
- return self.calibration_array[1] - self.calibration_array[0]
-
-
-class FunctionCalibration(AbstractCalibration):
- """Calibration defined by a function *f*, such as :math:`x \\mapsto f(x)`*.
-
- :param function: Calibration function"""
- def __init__(self, function, is_affine=False):
- super(FunctionCalibration, self).__init__()
- if not hasattr(function, "__call__"):
- raise TypeError("The calibration function must be a callable")
- self.function = function
- self._is_affine = is_affine
-
- def __call__(self, x):
- return self.function(x)
-
- def is_affine(self):
- """Return True if calibration is affine.
- This is False by default, unless the object is instantiated with
- ``is_affine=True``."""
- return self._is_affine
-
- def get_slope(self):
- """If the calibration array is regularly spaced, return the spacing."""
- if not self.is_affine():
- raise AttributeError(
- "get_slope only makes sense for affine transformations"
- )
- # fixme: what if function is not defined at x=1 or x=2?
- return self.function(2) - self.function(1)
diff --git a/silx/math/chistogramnd.pyx b/silx/math/chistogramnd.pyx
deleted file mode 100644
index 8484f35..0000000
--- a/silx/math/chistogramnd.pyx
+++ /dev/null
@@ -1,1251 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "02/10/2017"
-
-cimport numpy as cnumpy # noqa
-cimport cython
-import numpy as np
-
-cimport silx.math.histogramnd_c as histogramnd_c
-
-
-def chistogramnd(sample,
- histo_range,
- n_bins,
- weights=None,
- weight_min=None,
- weight_max=None,
- last_bin_closed=False,
- histo=None,
- weighted_histo=None,
- wh_dtype=None):
- """Computes the multidimensional histogram of some data.
-
- :param sample:
- The data to be histogrammed.
- Its shape must be either
- (N,) if it contains one dimensional coordinates,
- or an (N,D) array where the rows are the
- coordinates of points in a D dimensional space.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
-
- .. warning:: if sample is not a C_CONTIGUOUS ndarray (e.g : a non
- contiguous slice) then histogramnd will have to do make an internal
- copy.
- :type sample: :class:`numpy.array`
-
- :param histo_range:
- A (N, 2) array containing the histogram range along each dimension,
- where N is the sample's number of dimensions.
- :type histo_range: array_like
-
- :param n_bins:
- The number of bins :
- * a scalar (same number of bins for all dimensions)
- * a D elements array (number of bins for each dimensions)
- :type n_bins: scalar or array_like
-
- :param weights:
- A N elements numpy array of values associated with
- each sample.
- The values of the *weighted_histo* array
- returned by the function are equal to the sum of
- the weights associated with the samples falling
- into each bin.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
-
- .. note:: If None, the weighted histogram returned will be None.
- :type weights: *optional*, :class:`numpy.array`
-
- :param weight_min:
- Use this parameter to filter out all samples whose
- weights are lower than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_min: *optional*, scalar
-
- :param weight_max:
- Use this parameter to filter out all samples whose
- weights are higher than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
-
- :type weight_max: *optional*, scalar
-
- :param last_bin_closed:
- By default the last bin is half
- open (i.e.: [x,y) ; x included, y
- excluded), like all the other bins.
- Set this parameter to true if you want
- the LAST bin to be closed.
- :type last_bin_closed: *optional*, :class:`python.boolean`
-
- :param histo:
- Use this parameter if you want to pass your
- own histogram array instead of the one
- created by this function. New values
- will be added to this array. The returned array
- will then be this one (same reference).
-
- .. warning:: If the histo array was created by a previous
- call to histogramnd then the user is
- responsible for providing the same parameters
- (*n_bins*, *histo_range*, ...).
- :type histo: *optional*, :class:`numpy.array`
-
- :param weighted_histo:
- Use this parameter if you want to pass your
- own weighted histogram array instead of
- the created by this function. New
- values will be added to this array. The returned array
- will then be this one (same reference).
-
- .. warning:: If the weighted_histo array was created by a previous
- call to histogramnd then the user is
- responsible for providing the same parameters
- (*n_bins*, *histo_range*, ...).
-
- .. warning:: if weighted_histo is not a C_CONTIGUOUS ndarray (e.g : a
- non contiguous slice) then histogramnd will have to do make an
- internal copy.
- :type weighted_histo: *optional*, :class:`numpy.array`
-
- :param wh_dtype: type of the weighted histogram array. This parameter is
- ignored if *weighted_histo* is provided. If not provided, the
- weighted histogram array will contain values of the same type as
- *weights*. Allowed values are : `numpu.double` and `numpy.float32`.
- :type wh_dtype: *optional*, numpy data type
-
- :return: Histogram (bin counts, always returned), weighted histogram of
- the sample (or *None* if weights is *None*) and bin edges for each
- dimension.
- :rtype: *tuple* (:class:`numpy.array`, :class:`numpy.array`, `tuple`) or
- (:class:`numpy.array`, None, `tuple`)
- """
-
- if wh_dtype is None:
- wh_dtype = np.double
- elif wh_dtype not in (np.double, np.float32):
- raise ValueError('<wh_dtype> type not supported : {0}.'.format(wh_dtype))
-
- if (weighted_histo is not None and
- weighted_histo.flags['C_CONTIGUOUS'] is False):
- raise ValueError('<weighted_histo> must be a C_CONTIGUOUS numpy array.')
-
- if histo is not None and histo.flags['C_CONTIGUOUS'] is False:
- raise ValueError('<histo> must be a C_CONTIGUOUS numpy array.')
-
- s_shape = sample.shape
-
- n_dims = 1 if len(s_shape) == 1 else s_shape[1]
-
- if weights is not None:
- w_shape = weights.shape
-
- # making sure the sample and weights sizes are coherent
- # 2 different cases : 2D sample (N,M) and 1D (N)
- if len(w_shape) != 1 or w_shape[0] != s_shape[0]:
- raise ValueError('<weights> must be an array whose length '
- 'is equal to the number of samples.')
-
- weights_type = weights.dtype
- else:
- weights_type = None
-
- # just in case those arent numpy arrays
- # (this allows the user to provide native python lists,
- # => easier for testing)
- i_histo_range = histo_range
- histo_range = np.array(histo_range)
- err_histo_range = False
-
- if n_dims == 1:
- if histo_range.shape == (2,):
- pass
- elif histo_range.shape == (1, 2):
- histo_range.shape = -1
- else:
- err_histo_range = True
- elif n_dims != 1 and histo_range.shape != (n_dims, 2):
- err_histo_range = True
-
- if err_histo_range:
- raise ValueError('<histo_range> error : expected {n_dims} sets of '
- 'lower and upper bin edges, '
- 'got the following instead : {histo_range}. '
- '(provided <sample> contains '
- '{n_dims}D values)'
- ''.format(histo_range=i_histo_range,
- n_dims=n_dims))
-
- # check range value
- if np.inf in histo_range:
- raise ValueError('Range parameter should be finite value')
- if np.nan in histo_range:
- raise ValueError('Range value can\'t be nan')
-
- # checking n_bins size
- n_bins = np.array(n_bins, ndmin=1)
- if len(n_bins) == 1:
- n_bins = np.tile(n_bins, n_dims)
- elif n_bins.shape != (n_dims,):
- raise ValueError('n_bins must be either a scalar (same number '
- 'of bins for all dimensions) or '
- 'an array (number of bins for each '
- 'dimension).')
-
- # checking if None is in n_bins, otherwise a rather cryptic
- # exception is thrown when calling np.zeros
- # also testing for negative/null values
- if np.any(np.equal(n_bins, None)) or np.any(n_bins <= 0):
- raise ValueError('<n_bins> : only positive values allowed.')
-
- output_shape = tuple(n_bins)
-
- # checking the histo array, if provided
- if histo is None:
- histo = np.zeros(output_shape, dtype=np.uint32)
- else:
- if histo.shape != output_shape:
- raise ValueError('Provided <histo> array doesn\'t have '
- 'a shape compatible with <n_bins> '
- ': should be {0} instead of {1}.'
- ''.format(output_shape, histo.shape))
- if histo.dtype != np.uint32:
- raise ValueError('Provided <histo> array doesn\'t have '
- 'the expected type '
- ': should be {0} instead of {1}.'
- ''.format(np.uint32, histo.dtype))
-
- # checking the weighted_histo array, if provided
- if weights_type is None:
- # no weights provided, not creating the weighted_histo array
- weighted_histo = None
- elif weighted_histo is None:
- # weights provided, but no weighted_histo, creating it
- if wh_dtype is None:
- wh_dtype = weights_type
- weighted_histo = np.zeros(output_shape, dtype=wh_dtype)
- else:
- # weighted_histo provided, checking shape/dtype
- if weighted_histo.shape != output_shape:
- raise ValueError('Provided <weighted_histo> array doesn\'t have '
- 'a shape compatible with <n_bins> '
- ': should be {0} instead of {1}.'
- ''.format(output_shape, weighted_histo.shape))
- if (weighted_histo.dtype != np.float64 and
- weighted_histo.dtype != np.float32):
- raise ValueError('Provided <weighted_histo> array doesn\'t have '
- 'the expected type '
- ': should be {0} or {1} instead of {2}.'
- ''.format(np.double,
- np.float32,
- weighted_histo.dtype))
-
- option_flags = 0
-
- if weight_min is not None:
- option_flags |= histogramnd_c.HISTO_WEIGHT_MIN
- else:
- weight_min = 0
-
- if weight_max is not None:
- option_flags |= histogramnd_c.HISTO_WEIGHT_MAX
- else:
- weight_max = 0
-
- if last_bin_closed is not None and last_bin_closed:
- option_flags |= histogramnd_c.HISTO_LAST_BIN_CLOSED
-
- sample_type = sample.dtype
- sample_type = sample_type.newbyteorder('N')
-
- n_elem = sample.size // n_dims
-
- bin_edges = np.zeros(n_bins.sum() + n_bins.size, dtype=np.double)
-
- # wanted to store the functions in a dict (with the supported types
- # as keys, but I couldn't find a way to make it work with cdef
- # functions. so I have to explicitly list them all...
-
- def raise_unsupported_type():
- raise TypeError('Case not supported - sample:{0} '
- 'and weights:{1}.'
- ''.format(sample_type, weights_type))
-
- sample_c = np.ascontiguousarray(sample.reshape((sample.size,)),
- dtype=sample_type)
-
- weights_c = (np.ascontiguousarray(weights.reshape((weights.size,)),
- dtype=weights.dtype.newbyteorder('N'))
- if weights is not None else None)
-
- histo_range_c = np.ascontiguousarray(histo_range.reshape((histo_range.size,)),
- dtype=np.double)
-
- n_bins_c = np.ascontiguousarray(n_bins.reshape((n_bins.size,)),
- dtype=np.int32)
-
- histo_c = histo.reshape((histo.size,))
-
- if weighted_histo is not None:
- cumul_c = weighted_histo.reshape((weighted_histo.size,))
- else:
- cumul_c = None
-
- bin_edges_c = np.ascontiguousarray(bin_edges.reshape((bin_edges.size,)),
- dtype=bin_edges.dtype.newbyteorder('N'))
-
- rc = 0
-
- if weighted_histo is None or weighted_histo.dtype == np.double:
-
- if sample_type == np.float64:
-
- if weights_type == np.float64 or weights_type is None:
-
- rc = _histogramnd_double_double_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.float32:
-
- rc = _histogramnd_double_float_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.int32:
-
- rc = _histogramnd_double_int32_t_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- else:
- raise_unsupported_type()
-
- # endif sample_type == np.float64
- elif sample_type == np.float32:
-
- if weights_type == np.float64 or weights_type is None:
-
- rc = _histogramnd_float_double_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.float32:
-
- rc = _histogramnd_float_float_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.int32:
-
- rc = _histogramnd_float_int32_t_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- else:
- raise_unsupported_type()
-
- # endif sample_type == np.float32
- elif sample_type == np.int32:
-
- if weights_type == np.float64 or weights_type is None:
-
- rc = _histogramnd_int32_t_double_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.float32:
-
- rc = _histogramnd_int32_t_float_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.int32:
-
- rc = _histogramnd_int32_t_int32_t_double(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- else:
- raise_unsupported_type()
-
- # endif sample_type == np.int32:
- else:
- raise_unsupported_type()
-
- # endif weighted_histo is None or weighted_histo.dtype == np.double:
- elif weighted_histo.dtype == np.float32:
-
- if sample_type == np.float64:
-
- if weights_type == np.float64 or weights_type is None:
-
- rc = _histogramnd_double_double_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.float32:
-
- rc = _histogramnd_double_float_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.int32:
-
- rc = _histogramnd_double_int32_t_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- else:
- raise_unsupported_type()
-
- # endif sample_type == np.float64
- elif sample_type == np.float32:
-
- if weights_type == np.float64 or weights_type is None:
-
- rc = _histogramnd_float_double_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.float32:
-
- rc = _histogramnd_float_float_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.int32:
-
- rc = _histogramnd_float_int32_t_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- else:
- raise_unsupported_type()
-
- # endif sample_type == np.float32
- elif sample_type == np.int32:
-
- if weights_type == np.float64 or weights_type is None:
-
- rc = _histogramnd_int32_t_double_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.float32:
-
- rc = _histogramnd_int32_t_float_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- elif weights_type == np.int32:
-
- rc = _histogramnd_int32_t_int32_t_float(sample_c,
- weights_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- histo_c,
- cumul_c,
- bin_edges_c,
- option_flags,
- weight_min=weight_min,
- weight_max=weight_max)
-
- else:
- raise_unsupported_type()
-
- # endif sample_type == np.int32:
- else:
- raise_unsupported_type()
-
- # end elseif weighted_histo.dtype == np.float32:
- else:
- # this isnt supposed to happen since weighted_histo type was checked earlier
- raise_unsupported_type()
-
- if rc != histogramnd_c.HISTO_OK:
- if rc == histogramnd_c.HISTO_ERR_ALLOC:
- raise MemoryError('histogramnd failed to allocate memory.')
- else:
- raise Exception('histogramnd returned an error : {0}'
- ''.format(rc))
-
- edges = []
- offset = 0
- for i_dim in range(n_dims):
- edges.append(bin_edges[offset:offset + n_bins[i_dim] + 1])
- offset += n_bins[i_dim] + 1
-
- return histo, weighted_histo, tuple(edges)
-
-# =====================
-# double sample, double cumul
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_double_double_double(double[:] sample,
- double[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- double weight_min,
- double weight_max) nogil:
-
- return histogramnd_c.histogramnd_double_double_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_double_float_double(double[:] sample,
- float[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- float weight_min,
- float weight_max) nogil:
-
- return histogramnd_c.histogramnd_double_float_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_double_int32_t_double(double[:] sample,
- cnumpy.int32_t[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- cnumpy.int32_t weight_min,
- cnumpy.int32_t weight_max) nogil:
-
- return histogramnd_c.histogramnd_double_int32_t_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-# =====================
-# float sample, double cumul
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_float_double_double(float[:] sample,
- double[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- double weight_min,
- double weight_max) nogil:
-
- return histogramnd_c.histogramnd_float_double_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_float_float_double(float[:] sample,
- float[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- float weight_min,
- float weight_max) nogil:
-
- return histogramnd_c.histogramnd_float_float_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_float_int32_t_double(float[:] sample,
- cnumpy.int32_t[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- cnumpy.int32_t weight_min,
- cnumpy.int32_t weight_max) nogil:
-
- return histogramnd_c.histogramnd_float_int32_t_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-# =====================
-# numpy.int32_t sample, double cumul
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_int32_t_double_double(cnumpy.int32_t[:] sample,
- double[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- double weight_min,
- double weight_max) nogil:
-
- return histogramnd_c.histogramnd_int32_t_double_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_int32_t_float_double(cnumpy.int32_t[:] sample,
- float[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- float weight_min,
- float weight_max) nogil:
-
- return histogramnd_c.histogramnd_int32_t_float_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_int32_t_int32_t_double(cnumpy.int32_t[:] sample,
- cnumpy.int32_t[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- double[:] cumul,
- double[:] bin_edges,
- int option_flags,
- cnumpy.int32_t weight_min,
- cnumpy.int32_t weight_max) nogil:
-
- return histogramnd_c.histogramnd_int32_t_int32_t_double(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-# =====================
-# double sample, float cumul
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_double_double_float(double[:] sample,
- double[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- double weight_min,
- double weight_max) nogil:
-
- return histogramnd_c.histogramnd_double_double_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_double_float_float(double[:] sample,
- float[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- float weight_min,
- float weight_max) nogil:
-
- return histogramnd_c.histogramnd_double_float_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_double_int32_t_float(double[:] sample,
- cnumpy.int32_t[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- cnumpy.int32_t weight_min,
- cnumpy.int32_t weight_max) nogil:
-
- return histogramnd_c.histogramnd_double_int32_t_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-# =====================
-# float sample, float cumul
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_float_double_float(float[:] sample,
- double[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- double weight_min,
- double weight_max) nogil:
-
- return histogramnd_c.histogramnd_float_double_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_float_float_float(float[:] sample,
- float[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- float weight_min,
- float weight_max) nogil:
-
- return histogramnd_c.histogramnd_float_float_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_float_int32_t_float(float[:] sample,
- cnumpy.int32_t[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- cnumpy.int32_t weight_min,
- cnumpy.int32_t weight_max) nogil:
-
- return histogramnd_c.histogramnd_float_int32_t_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-# =====================
-# numpy.int32_t sample, float cumul
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_int32_t_double_float(cnumpy.int32_t[:] sample,
- double[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- double weight_min,
- double weight_max) nogil:
-
- return histogramnd_c.histogramnd_int32_t_double_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_int32_t_float_float(cnumpy.int32_t[:] sample,
- float[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- float weight_min,
- float weight_max) nogil:
-
- return histogramnd_c.histogramnd_int32_t_float_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-cdef int _histogramnd_int32_t_int32_t_float(cnumpy.int32_t[:] sample,
- cnumpy.int32_t[:] weights,
- int n_dims,
- int n_elem,
- double[:] histo_range,
- int[:] n_bins,
- cnumpy.uint32_t[:] histo,
- float[:] cumul,
- double[:] bin_edges,
- int option_flags,
- cnumpy.int32_t weight_min,
- cnumpy.int32_t weight_max) nogil:
-
- return histogramnd_c.histogramnd_int32_t_int32_t_float(&sample[0],
- &weights[0],
- n_dims,
- n_elem,
- &histo_range[0],
- &n_bins[0],
- &histo[0],
- &cumul[0],
- &bin_edges[0],
- option_flags,
- weight_min,
- weight_max)
diff --git a/silx/math/chistogramnd_lut.pyx b/silx/math/chistogramnd_lut.pyx
deleted file mode 100644
index 3a3f05e..0000000
--- a/silx/math/chistogramnd_lut.pyx
+++ /dev/null
@@ -1,435 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "15/05/2016"
-
-
-cimport numpy as cnumpy # noqa
-cimport cython
-import numpy as np
-
-ctypedef fused sample_t:
- cnumpy.float64_t
- cnumpy.float32_t
- cnumpy.int32_t
- cnumpy.int64_t
-
-ctypedef fused cumul_t:
- cnumpy.float64_t
- cnumpy.float32_t
- cnumpy.int32_t
- cnumpy.int64_t
-
-ctypedef fused weights_t:
- cnumpy.float64_t
- cnumpy.float32_t
- cnumpy.int32_t
- cnumpy.int64_t
-
-ctypedef fused lut_t:
- cnumpy.int64_t
- cnumpy.int32_t
- cnumpy.int16_t
-
-
-def histogramnd_get_lut(sample,
- histo_range,
- n_bins,
- last_bin_closed=False):
- """TBD
-
- :param sample:
- The data to be histogrammed.
- Its shape must be either (N,) if it contains one dimensional
- coordinates, or an (N, D) array where the rows are the
- coordinates of points in a D dimensional space.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
- :type sample: :class:`numpy.array`
-
- :param histo_range:
- A (N, 2) array containing the histogram range along each dimension,
- where N is the sample's number of dimensions.
- :type histo_range: array_like
-
- :param n_bins:
- The number of bins :
- * a scalar (same number of bins for all dimensions)
- * a D elements array (number of bins for each dimensions)
- :type n_bins: scalar or array_like
-
- :param last_bin_closed:
- By default the last bin is half
- open (i.e.: [x,y) ; x included, y
- excluded), like all the other bins.
- Set this parameter to true if you want
- the LAST bin to be closed.
- :type last_bin_closed: *optional*, :class:`python.boolean`
-
- :return: The indices for each sample and the histogram (bin counts).
- :rtype: tuple : (:class:`numpy.array`, :class:`numpy.array`)
- """
-
- s_shape = sample.shape
-
- n_dims = 1 if len(s_shape) == 1 else s_shape[1]
-
- # just in case those arent numpy arrays
- # (this allows the user to provide native python lists,
- # => easier for testing)
- i_histo_range = histo_range
- histo_range = np.array(histo_range)
- err_histo_range = False
-
- if n_dims == 1:
- if histo_range.shape == (2,):
- pass
- elif histo_range.shape == (1, 2):
- histo_range.reshape(-1)
- else:
- err_histo_range = True
- elif n_dims != 1 and histo_range.shape != (n_dims, 2):
- err_histo_range = True
-
- if err_histo_range:
- raise ValueError('<histo_range> error : expected {n_dims} sets of '
- 'lower and upper bin edges, '
- 'got the following instead : {histo_range}. '
- '(provided <sample> contains '
- '{n_dims}D values)'
- ''.format(histo_range=i_histo_range,
- n_dims=n_dims))
-
- histo_range = np.double(histo_range)
-
- # checking n_bins size
- n_bins = np.array(n_bins, ndmin=1)
- if len(n_bins) == 1:
- n_bins = np.tile(n_bins, n_dims)
- elif n_bins.shape != (n_dims,):
- raise ValueError('n_bins must be either a scalar (same number '
- 'of bins for all dimensions) or '
- 'an array (number of bins for each '
- 'dimension).')
-
- # checking if None is in n_bins, otherwise a rather cryptic
- # exception is thrown when calling np.zeros
- # also testing for negative/null values
- if np.any(np.equal(n_bins, None)) or np.any(n_bins <= 0):
- raise ValueError('<n_bins> : only positive values allowed.')
-
- sample_type = sample.dtype
-
- n_elem = sample.size // n_dims
-
- if n_bins.prod(dtype=np.uint64) < 2**15:
- lut_dtype = np.int16
- elif n_bins.prod(dtype=np.uint64) < 2**31:
- lut_dtype = np.int32
-
- else:
- lut_dtype = np.int64
-
- # allocating the output arrays
- lut = np.zeros(n_elem, dtype=lut_dtype)
- histo = np.zeros(n_bins, dtype=np.uint32)
-
- dtype = sample.dtype.newbyteorder("N")
- sample_c = np.ascontiguousarray(sample.reshape((sample.size,)),
- dtype=dtype)
-
- histo_range_c = np.ascontiguousarray(histo_range.reshape((histo_range.size,)),
- dtype=histo_range.dtype.newbyteorder("N"))
-
- n_bins_c = np.ascontiguousarray(n_bins.reshape((n_bins.size,)),
- dtype=np.int32)
-
- lut_c = np.ascontiguousarray(lut.reshape((lut.size,)))
- histo_c = np.ascontiguousarray(histo.reshape((histo.size,)),
- dtype=histo.dtype.newbyteorder('N'))
-
- rc = 0
-
- try:
- rc = _histogramnd_get_lut_fused(sample_c,
- n_dims,
- n_elem,
- histo_range_c,
- n_bins_c,
- lut_c,
- histo_c,
- last_bin_closed)
- except TypeError as ex:
- raise TypeError('Type not supported - sample : {0}'
- ''.format(sample_type))
-
- if rc != 0:
- raise Exception('histogramnd returned an error : {0}'
- ''.format(rc))
-
- edges = []
- histo_range = histo_range.reshape(-1)
- for i_dim in range(n_dims):
- dim_edges = np.zeros(n_bins[i_dim] + 1)
- rng_min = histo_range[2 * i_dim]
- rng_max = histo_range[2 * i_dim + 1]
- dim_edges[:-1] = (rng_min + np.arange(n_bins[i_dim]) *
- ((rng_max - rng_min) / n_bins[i_dim]))
- dim_edges[-1] = rng_max
- edges.append(dim_edges)
-
- return lut, histo, tuple(edges)
-
-
-# =====================
-# =====================
-
-
-def histogramnd_from_lut(weights,
- histo_lut,
- histo=None,
- weighted_histo=None,
- shape=None,
- dtype=None,
- weight_min=None,
- weight_max=None):
- """
- dtype ignored if weighted_histo provided
- """
-
- if histo is None and weighted_histo is None:
- if shape is None:
- raise ValueError('At least one of the following parameters has to '
- 'be provided : <shape> or <histo> or '
- '<weighted_histo>')
-
- if shape is not None:
- if histo is not None and list(histo.shape) != list(shape):
- raise ValueError('The <shape> value does not match'
- 'the <histo> shape.')
-
- if(weighted_histo is not None and
- list(weighted_histo.shape) != list(shape)):
- raise ValueError('The <shape> value does not match'
- 'the <weighted_histo> shape.')
- else:
- if histo is not None:
- shape = histo.shape
- else:
- shape = weighted_histo.shape
-
- if histo is not None:
- if histo.dtype != np.uint32:
- raise ValueError('Provided <histo> array doesn\'t have '
- 'the expected type '
- ': should be {0} instead of {1}.'
- ''.format(np.uint32, histo.dtype))
-
- if weighted_histo is not None:
- if histo.shape != weighted_histo.shape:
- raise ValueError('The <histo> shape does not match'
- 'the <weighted_histo> shape.')
- else:
- histo = np.zeros(shape, dtype=np.uint32)
-
- w_dtype = weights.dtype
-
- if dtype is None:
- if weighted_histo is None:
- dtype = w_dtype
- else:
- dtype = weighted_histo.dtype
- elif weighted_histo is not None:
- if weighted_histo.dtype != dtype:
- raise ValueError('Provided <dtype> and <weighted_histo>\'s dtype'
- ' do not match.')
- dtype = weighted_histo.dtype
-
- if weighted_histo is None:
- weighted_histo = np.zeros(shape, dtype=dtype)
-
- if histo_lut.size != weights.size:
- raise ValueError('The LUT and weights arrays must have the same '
- 'number of elements.')
-
- w_c = np.ascontiguousarray(weights.reshape((weights.size,)),
- dtype=weights.dtype.newbyteorder('N'))
-
- h_c = np.ascontiguousarray(histo.reshape((histo.size,)),
- dtype=histo.dtype.newbyteorder('N'))
-
- w_h_c = np.ascontiguousarray(weighted_histo.reshape((weighted_histo.size,)),
- dtype=weighted_histo.dtype.newbyteorder('N')) # noqa
-
- h_lut_c = np.ascontiguousarray(histo_lut.reshape((histo_lut.size,)),
- histo_lut.dtype.newbyteorder('N'))
-
- rc = 0
-
- if weight_min is None:
- weight_min = 0
- filt_min_weights = False
- else:
- filt_min_weights = True
-
- if weight_max is None:
- weight_max = 0
- filt_max_weights = False
- else:
- filt_max_weights = True
-
- try:
- _histogramnd_from_lut_fused(w_c,
- h_lut_c,
- h_c,
- w_h_c,
- weights.size,
- filt_min_weights,
- w_dtype.type(weight_min),
- filt_max_weights,
- w_dtype.type(weight_max))
- except TypeError as ex:
- print(ex)
- raise TypeError('Case not supported - weights:{0} '
- 'and histo:{1}.'
- ''.format(weights.dtype, histo.dtype))
-
- return histo, weighted_histo
-
-
-# =====================
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-@cython.cdivision(True)
-def _histogramnd_from_lut_fused(weights_t[:] i_weights,
- lut_t[:] i_lut,
- cnumpy.uint32_t[:] o_histo,
- cumul_t[:] o_weighted_histo,
- int i_n_elems,
- bint i_filt_min_weights,
- weights_t i_weight_min,
- bint i_filt_max_weights,
- weights_t i_weight_max):
- with nogil:
- for i in range(i_n_elems):
- if (i_lut[i] >= 0):
- if i_filt_min_weights and i_weights[i] < i_weight_min:
- continue
- if i_filt_max_weights and i_weights[i] > i_weight_max:
- continue
- o_histo[i_lut[i]] += 1
- o_weighted_histo[i_lut[i]] += <cumul_t>i_weights[i] # noqa
-
-
-# =====================
-# =====================
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.initializedcheck(False)
-@cython.nonecheck(False)
-@cython.cdivision(True)
-def _histogramnd_get_lut_fused(sample_t[:] i_sample,
- int i_n_dims,
- int i_n_elems,
- double[:] i_histo_range,
- int[:] i_n_bins,
- lut_t[:] o_lut,
- cnumpy.uint32_t[:] o_histo,
- bint last_bin_closed):
-
- cdef:
- int i = 0
- long elem_idx = 0
- long max_idx = 0
- long lut_idx = -1
-
- # computed bin index (i_sample -> grid)
- long bin_idx = 0
-
- sample_t elem_coord = 0
-
- double[50] g_min
- double[50] g_max
- double[50] bins_range
-
- for i in range(i_n_dims):
- g_min[i] = i_histo_range[2*i]
- g_max[i] = i_histo_range[2*i+1]
- bins_range[i] = g_max[i] - g_min[i]
-
- elem_idx = 0 - i_n_dims
- max_idx = i_n_elems * i_n_dims - i_n_dims
-
- with nogil:
- while elem_idx < max_idx:
- elem_idx += i_n_dims
- lut_idx += 1
-
- bin_idx = 0
-
- for i in range(i_n_dims):
- elem_coord = i_sample[elem_idx+i]
- # =====================
- # Element is rejected if any of the following is NOT true :
- # 1. coordinate is >= than the minimum value
- # 2. coordinate is <= than the maximum value
- # 3. coordinate==maximum value and last_bin_closed is True
- # =====================
- if elem_coord < g_min[i]:
- bin_idx = -1
- break
-
- # Here we make the assumption that most of the time
- # there will be more coordinates inside the grid interval
- # (one test)
- # than coordinates higher or equal to the max
- # (two tests)
- if elem_coord < g_max[i]:
- bin_idx = <long>(bin_idx * i_n_bins[i] + # noqa
- (((elem_coord - g_min[i]) * i_n_bins[i]) /
- bins_range[i]))
- else:
- # if equal and the last bin is closed :
- # put it in the last bin
- # else : discard
- if last_bin_closed and elem_coord == g_max[i]:
- bin_idx = (bin_idx + 1) * i_n_bins[i] - 1
- else:
- bin_idx = -1
- break
-
- o_lut[lut_idx] = bin_idx
- if bin_idx >= 0:
- o_histo[bin_idx] += 1
-
- return 0
diff --git a/silx/math/colormap.pyx b/silx/math/colormap.pyx
deleted file mode 100644
index 2cefe04..0000000
--- a/silx/math/colormap.pyx
+++ /dev/null
@@ -1,559 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""This module provides :func:`cmap` which applies a colormap to a dataset.
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "16/05/2018"
-
-
-import os
-cimport cython
-from cython.parallel import prange
-cimport numpy as cnumpy
-from libc.math cimport frexp, sinh, sqrt
-from .math_compatibility cimport asinh, isnan, isfinite, lrint, INFINITY, NAN
-
-import logging
-import numbers
-
-import numpy
-
-__all__ = ['cmap']
-
-_logger = logging.getLogger(__name__)
-
-
-cdef int DEFAULT_NUM_THREADS
-if hasattr(os, 'sched_getaffinity'):
- DEFAULT_NUM_THREADS = min(4, len(os.sched_getaffinity(0)))
-elif os.cpu_count() is not None:
- DEFAULT_NUM_THREADS = min(4, os.cpu_count())
-else: # Fallback
- DEFAULT_NUM_THREADS = 1
-# Number of threads to use for the computation (initialized to up to 4)
-
-cdef int USE_OPENMP_THRESHOLD = 1000
-"""OpenMP is not used for arrays with less elements than this threshold"""
-
-# Supported data types
-ctypedef fused data_types:
- cnumpy.uint8_t
- cnumpy.int8_t
- cnumpy.uint16_t
- cnumpy.int16_t
- cnumpy.uint32_t
- cnumpy.int32_t
- cnumpy.uint64_t
- cnumpy.int64_t
- float
- double
- long double
-
-
-# Data types using a LUT to apply the colormap
-ctypedef fused lut_types:
- cnumpy.uint8_t
- cnumpy.int8_t
- cnumpy.uint16_t
- cnumpy.int16_t
-
-
-# Data types using default colormap implementation
-ctypedef fused default_types:
- cnumpy.uint32_t
- cnumpy.int32_t
- cnumpy.uint64_t
- cnumpy.int64_t
- float
- double
- long double
-
-
-# Supported colors/output types
-ctypedef fused image_types:
- cnumpy.uint8_t
- float
-
-
-# Normalization
-
-ctypedef double (*NormalizationFunction)(double) nogil
-
-
-cdef class Normalization:
- """Base class for colormap normalization"""
-
- def apply(self, data, double vmin, double vmax):
- """Apply normalization.
-
- :param Union[float,numpy.ndarray] data:
- :param float vmin: Lower bound of the range
- :param float vmax: Upper bound of the range
- :rtype: Union[float,numpy.ndarray]
- """
- cdef int length
- cdef double[:] result
-
- if isinstance(data, numbers.Real):
- return self.apply_double(<double> data, vmin, vmax)
- else:
- data = numpy.array(data, copy=False)
- length = <int> data.size
- result = numpy.empty(length, dtype=numpy.float64)
- data1d = numpy.ravel(data)
- for index in range(length):
- result[index] = self.apply_double(
- <double> data1d[index], vmin, vmax)
- return numpy.array(result).reshape(data.shape)
-
- def revert(self, data, double vmin, double vmax):
- """Revert normalization.
-
- :param Union[float,numpy.ndarray] data:
- :param float vmin: Lower bound of the range
- :param float vmax: Upper bound of the range
- :rtype: Union[float,numpy.ndarray]
- """
- cdef int length
- cdef double[:] result
-
- if isinstance(data, numbers.Real):
- return self.revert_double(<double> data, vmin, vmax)
- else:
- data = numpy.array(data, copy=False)
- length = <int> data.size
- result = numpy.empty(length, dtype=numpy.float64)
- data1d = numpy.ravel(data)
- for index in range(length):
- result[index] = self.revert_double(
- <double> data1d[index], vmin, vmax)
- return numpy.array(result).reshape(data.shape)
-
- cdef double apply_double(self, double value, double vmin, double vmax) nogil:
- """Apply normalization to a floating point value
-
- Override in subclass
-
- :param float value:
- :param float vmin: Lower bound of the range
- :param float vmax: Upper bound of the range
- """
- return value
-
- cdef double revert_double(self, double value, double vmin, double vmax) nogil:
- """Apply inverse of normalization to a floating point value
-
- Override in subclass
-
- :param float value:
- :param float vmin: Lower bound of the range
- :param float vmax: Upper bound of the range
- """
- return value
-
-
-cdef class LinearNormalization(Normalization):
- """Linear normalization"""
-
- cdef double apply_double(self, double value, double vmin, double vmax) nogil:
- return value
-
- cdef double revert_double(self, double value, double vmin, double vmax) nogil:
- return value
-
-
-cdef class LogarithmicNormalization(Normalization):
- """Logarithmic normalization using a fast log approximation"""
- cdef:
- readonly int lutsize
- readonly double[::1] lut # LUT used for fast log approximation
-
- def __cinit__(self, int lutsize=4096):
- # Initialize log approximation LUT
- self.lutsize = lutsize
- self.lut = numpy.log2(
- numpy.linspace(0.5, 1., lutsize + 1,
- endpoint=True).astype(numpy.float64))
- # index_lut can overflow of 1
- self.lut[lutsize] = self.lut[lutsize - 1]
-
- def __dealloc__(self):
- self.lut = None
-
- @cython.wraparound(False)
- @cython.boundscheck(False)
- @cython.nonecheck(False)
- @cython.cdivision(True)
- cdef double apply_double(self, double value, double vmin, double vmax) nogil:
- """Return log10(value) fast approximation based on LUT"""
- cdef double result = NAN # if value < 0.0 or value == NAN
- cdef int exponent, index_lut
- cdef double mantissa # in [0.5, 1) unless value == 0 NaN or +/-inf
-
- if value <= 0.0 or not isfinite(value):
- if value == 0.0:
- result = - INFINITY
- elif value > 0.0: # i.e., value = +INFINITY
- result = value # i.e. +INFINITY
- else:
- mantissa = frexp(value, &exponent)
- index_lut = lrint(self.lutsize * 2 * (mantissa - 0.5))
- # 1/log2(10) = 0.30102999566398114
- result = 0.30102999566398114 * (<double> exponent +
- self.lut[index_lut])
- return result
-
- cdef double revert_double(self, double value, double vmin, double vmax) nogil:
- return 10**value
-
-
-cdef class ArcsinhNormalization(Normalization):
- """Inverse hyperbolic sine normalization"""
-
- cdef double apply_double(self, double value, double vmin, double vmax) nogil:
- return asinh(value)
-
- cdef double revert_double(self, double value, double vmin, double vmax) nogil:
- return sinh(value)
-
-
-cdef class SqrtNormalization(Normalization):
- """Square root normalization"""
-
- cdef double apply_double(self, double value, double vmin, double vmax) nogil:
- return sqrt(value)
-
- cdef double revert_double(self, double value, double vmin, double vmax) nogil:
- return value**2
-
-
-cdef class PowerNormalization(Normalization):
- """Gamma correction:
-
- Linear normalization to [0, 1] followed by power normalization.
-
- :param gamma: Gamma correction factor
- """
-
- cdef:
- readonly double gamma
-
- def __cinit__(self, double gamma):
- self.gamma = gamma
-
- def __init__(self, gamma):
- # Needed for multiple inheritance to work
- pass
-
- cdef double apply_double(self, double value, double vmin, double vmax) nogil:
- if vmin == vmax:
- return 0.
- elif value <= vmin:
- return 0.
- elif value >= vmax:
- return 1.
- else:
- return ((value - vmin) / (vmax - vmin))**self.gamma
-
- cdef double revert_double(self, double value, double vmin, double vmax) nogil:
- if value <= 0.:
- return vmin
- elif value >= 1.:
- return vmax
- else:
- return vmin + (vmax - vmin) * value**(1.0/self.gamma)
-
-
-# Colormap
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.nonecheck(False)
-@cython.cdivision(True)
-cdef image_types[:, ::1] compute_cmap(
- default_types[:] data,
- image_types[:, ::1] colors,
- Normalization normalization,
- double vmin,
- double vmax,
- image_types[::1] nan_color):
- """Apply colormap to data.
-
- :param data: Input data
- :param colors: Colors look-up-table
- :param vmin: Lower bound of the colormap range
- :param vmax: Upper bound of the colormap range
- :param nan_color: Color to use for NaN value
- :param normalization: Normalization to apply
- :return: Data converted to colors
- """
- cdef image_types[:, ::1] output
- cdef double scale, value, normalized_vmin, normalized_vmax
- cdef int length, nb_channels, nb_colors
- cdef int channel, index, lut_index, num_threads
-
- nb_colors = <int> colors.shape[0]
- nb_channels = <int> colors.shape[1]
- length = <int> data.size
-
- output = numpy.empty((length, nb_channels),
- dtype=numpy.array(colors, copy=False).dtype)
-
- normalized_vmin = normalization.apply_double(vmin, vmin, vmax)
- normalized_vmax = normalization.apply_double(vmax, vmin, vmax)
-
- if not isfinite(normalized_vmin) or not isfinite(normalized_vmax):
- raise ValueError('Colormap range is not valid')
-
- if normalized_vmin == normalized_vmax:
- scale = 0.
- else:
- scale = nb_colors / (normalized_vmax - normalized_vmin)
-
- if length < USE_OPENMP_THRESHOLD:
- num_threads = 1
- else:
- num_threads = min(
- DEFAULT_NUM_THREADS,
- int(os.environ.get("OMP_NUM_THREADS", DEFAULT_NUM_THREADS)))
-
- with nogil:
- for index in prange(length, num_threads=num_threads):
- value = normalization.apply_double(
- <double> data[index], vmin, vmax)
-
- # Handle NaN
- if isnan(value):
- for channel in range(nb_channels):
- output[index, channel] = nan_color[channel]
- continue
-
- if value <= normalized_vmin:
- lut_index = 0
- elif value >= normalized_vmax:
- lut_index = nb_colors - 1
- else:
- lut_index = <int>((value - normalized_vmin) * scale)
- # Index can overflow of 1
- if lut_index >= nb_colors:
- lut_index = nb_colors - 1
-
- for channel in range(nb_channels):
- output[index, channel] = colors[lut_index, channel]
-
- return output
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.nonecheck(False)
-@cython.cdivision(True)
-cdef image_types[:, ::1] compute_cmap_with_lut(
- lut_types[:] data,
- image_types[:, ::1] colors,
- Normalization normalization,
- double vmin,
- double vmax,
- image_types[::1] nan_color):
- """Convert data to colors using look-up table to speed the process.
-
- Only supports data of types: uint8, uint16, int8, int16.
-
- :param data: Input data
- :param colors: Colors look-up-table
- :param vmin: Lower bound of the colormap range
- :param vmax: Upper bound of the colormap range
- :param nan_color: Color to use for NaN values
- :param normalization: Normalization to apply
- :return: The generated image
- """
- cdef image_types[:, ::1] output
- cdef double[:] values
- cdef image_types[:, ::1] lut
- cdef int type_min, type_max
- cdef int nb_channels, length
- cdef int channel, index, lut_index, num_threads
-
- length = <int> data.size
- nb_channels = <int> colors.shape[1]
-
- if lut_types is cnumpy.int8_t:
- type_min = -128
- type_max = 127
- elif lut_types is cnumpy.uint8_t:
- type_min = 0
- type_max = 255
- elif lut_types is cnumpy.int16_t:
- type_min = -32768
- type_max = 32767
- else: # uint16_t
- type_min = 0
- type_max = 65535
-
- colors_dtype = numpy.array(colors).dtype
-
- values = numpy.arange(type_min, type_max + 1, dtype=numpy.float64)
- lut = compute_cmap(
- values, colors, normalization, vmin, vmax, nan_color)
-
- output = numpy.empty((length, nb_channels), dtype=colors_dtype)
-
- if length < USE_OPENMP_THRESHOLD:
- num_threads = 1
- else:
- num_threads = min(
- DEFAULT_NUM_THREADS,
- int(os.environ.get("OMP_NUM_THREADS", DEFAULT_NUM_THREADS)))
-
- with nogil:
- # Apply LUT
- for index in prange(length, num_threads=num_threads):
- lut_index = data[index] - type_min
- for channel in range(nb_channels):
- output[index, channel] = lut[lut_index, channel]
-
- return output
-
-
-# Normalizations without parameters
-_BASIC_NORMALIZATIONS = {
- 'linear': LinearNormalization(),
- 'log': LogarithmicNormalization(),
- 'arcsinh': ArcsinhNormalization(),
- 'sqrt': SqrtNormalization(),
- }
-
-
-@cython.wraparound(False)
-@cython.boundscheck(False)
-@cython.nonecheck(False)
-@cython.cdivision(True)
-def _cmap(data_types[:] data,
- image_types[:, ::1] colors,
- Normalization normalization,
- double vmin,
- double vmax,
- image_types[::1] nan_color):
- """Implementation of colormap.
-
- Use :func:`cmap`.
-
- :param data: Input data
- :param colors: Colors look-up-table
- :param normalization: Normalization object to apply
- :param vmin: Lower bound of the colormap range
- :param vmax: Upper bound of the colormap range
- :param nan_color: Color to use for NaN value.
- :return: The generated image
- """
- cdef image_types[:, ::1] output
-
- # Proxy for calling the right implementation depending on data type
- if data_types in lut_types: # Use LUT implementation
- output = compute_cmap_with_lut(
- data, colors, normalization, vmin, vmax, nan_color)
-
- elif data_types in default_types: # Use default implementation
- output = compute_cmap(
- data, colors, normalization, vmin, vmax, nan_color)
-
- else:
- raise ValueError('Unsupported data type')
-
- return numpy.array(output, copy=False)
-
-
-def cmap(data,
- colors,
- double vmin,
- double vmax,
- normalization='linear',
- nan_color=None):
- """Convert data to colors with provided colors look-up table.
-
- :param numpy.ndarray data: The input data
- :param numpy.ndarray colors: Color look-up table as a 2D array.
- It MUST be of type uint8 or float32
- :param vmin: Data value to map to the beginning of colormap.
- :param vmax: Data value to map to the end of the colormap.
- :param Union[str,Normalization] normalization:
- Either a :class:`Normalization` instance or a str in:
-
- - 'linear' (default)
- - 'log'
- - 'arcsinh'
- - 'sqrt'
- - 'gamma'
-
- :param nan_color: Color to use for NaN value.
- Default: A color with all channels set to 0
- :return: Array of colors. The shape of the
- returned array is that of data array + the last dimension of colors.
- The dtype of the returned array is that of the colors array.
- :rtype: numpy.ndarray
- """
- cdef int nb_channels
- cdef Normalization norm
-
- # Make data a numpy array of native endian type (no need for contiguity)
- data = numpy.array(data, copy=False)
- native_endian_dtype = data.dtype.newbyteorder('N')
- if native_endian_dtype.kind == 'f' and native_endian_dtype.itemsize == 2:
- native_endian_dtype = "=f4" # Use native float32 instead of float16
- data = numpy.array(data, copy=False, dtype=native_endian_dtype)
-
- # Make colors a contiguous array of native endian type
- colors = numpy.array(colors, copy=False)
- nb_channels = colors.shape[colors.ndim - 1]
- colors = numpy.ascontiguousarray(colors,
- dtype=colors.dtype.newbyteorder('N'))
-
- # Make normalization a Normalization object
- if isinstance(normalization, str):
- norm = _BASIC_NORMALIZATIONS.get(normalization, None)
- if norm is None:
- raise ValueError('Unsupported normalization %s' % normalization)
- else:
- norm = normalization
-
- # Check nan_color
- if nan_color is None:
- nan_color = numpy.zeros((nb_channels,), dtype=colors.dtype)
- else:
- nan_color = numpy.ascontiguousarray(
- nan_color, dtype=colors.dtype).reshape(-1)
- assert nan_color.shape == (nb_channels,)
-
- image = _cmap(
- data.reshape(-1),
- colors.reshape(-1, nb_channels),
- norm,
- vmin,
- vmax,
- nan_color)
- image.shape = data.shape + (nb_channels,)
-
- return image
diff --git a/silx/math/combo.pyx b/silx/math/combo.pyx
deleted file mode 100644
index e24edda..0000000
--- a/silx/math/combo.pyx
+++ /dev/null
@@ -1,329 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides combination of statistics as single operation.
-
-For now it provides min/max (and optionally positive min) and indices
-of first occurrences (i.e., argmin/argmax) in a single pass.
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "24/04/2018"
-
-cimport cython
-from .math_compatibility cimport isnan, isfinite, INFINITY
-
-
-import numpy
-
-
-# All supported types
-ctypedef fused _number:
- float
- double
- long double
- signed char
- signed short
- signed int
- signed long
- signed long long
- unsigned char
- unsigned short
- unsigned int
- unsigned long
- unsigned long long
-
-# All supported floating types:
-# cython.floating + long double
-ctypedef fused _floating:
- float
- double
- long double
-
-
-class _MinMaxResult(object):
- """Object storing result from :func:`min_max`"""
-
- def __init__(self, minimum, min_pos, maximum,
- argmin, argmin_pos, argmax):
- self._minimum = minimum
- self._min_positive = min_pos
- self._maximum = maximum
-
- self._argmin = argmin
- self._argmin_positive = argmin_pos
- self._argmax = argmax
-
- minimum = property(
- lambda self: self._minimum,
- doc="Minimum value of the array")
- maximum = property(
- lambda self: self._maximum,
- doc="Maximum value of the array")
-
- argmin = property(
- lambda self: self._argmin,
- doc="Index of the first occurrence of the minimum value")
- argmax = property(
- lambda self: self._argmax,
- doc="Index of the first occurrence of the maximum value")
-
- min_positive = property(
- lambda self: self._min_positive,
- doc="""Strictly positive minimum value
-
- It is None if no value is strictly positive.
- """)
- argmin_positive = property(
- lambda self: self._argmin_positive,
- doc="""Index of the strictly positive minimum value.
-
- It is None if no value is strictly positive.
- It is the index of the first occurrence.""")
-
- def __getitem__(self, key):
- if key == 0:
- return self.minimum
- elif key == 1:
- return self.maximum
- else:
- raise IndexError("Index out of range")
-
-
-@cython.initializedcheck(False)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def _min_max(_number[::1] data, bint min_positive=False):
- """:func:`min_max` implementation including infinite values
-
- See :func:`min_max` for documentation.
- """
- cdef:
- _number value, minimum, min_pos, maximum
- unsigned int length
- unsigned int index = 0
- unsigned int min_index = 0
- unsigned int min_pos_index = 0
- unsigned int max_index = 0
-
- length = len(data)
-
- if length == 0:
- raise ValueError('Zero-size array')
-
- with nogil:
- # Init starting values
- value = data[0]
- minimum = value
- maximum = value
- if min_positive and value > 0:
- min_pos = value
- else:
- min_pos = 0
-
- if _number in _floating:
- # For floating, loop until first not NaN value
- for index in range(length):
- value = data[index]
- if not isnan(value):
- minimum = value
- min_index = index
- maximum = value
- max_index = index
- break
-
- if not min_positive:
- for index in range(index, length):
- value = data[index]
- if value > maximum:
- maximum = value
- max_index = index
- elif value < minimum:
- minimum = value
- min_index = index
-
- else:
- # Loop until min_pos is defined
- for index in range(index, length):
- value = data[index]
- if value > maximum:
- maximum = value
- max_index = index
- elif value < minimum:
- minimum = value
- min_index = index
-
- if value > 0:
- min_pos = value
- min_pos_index = index
- break
-
- # Loop until the end
- for index in range(index + 1, length):
- value = data[index]
- if value > maximum:
- maximum = value
- max_index = index
- else:
- if value < minimum:
- minimum = value
- min_index = index
-
- if 0 < value < min_pos:
- min_pos = value
- min_pos_index = index
-
- return _MinMaxResult(minimum,
- min_pos if min_pos > 0 else None,
- maximum,
- min_index,
- min_pos_index if min_pos > 0 else None,
- max_index)
-
-
-@cython.initializedcheck(False)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def _finite_min_max(_floating[::1] data, bint min_positive=False):
- """:func:`min_max` implementation for floats skipping infinite values
-
- See :func:`min_max` for documentation.
- """
- cdef:
- _floating value, minimum, min_pos, maximum
- unsigned int length
- unsigned int index = 0
- unsigned int min_index = 0
- unsigned int min_pos_index = 0
- unsigned int max_index = 0
-
- length = len(data)
-
- if length == 0:
- raise ValueError('Zero-size array')
-
- with nogil:
- minimum = INFINITY
- maximum = -INFINITY
- min_pos = INFINITY
-
- if not min_positive:
- for index in range(length):
- value = data[index]
- if isfinite(value):
- if value > maximum:
- maximum = value
- max_index = index
- if value < minimum:
- minimum = value
- min_index = index
-
- else:
- for index in range(index, length):
- value = data[index]
- if isfinite(value):
- if value > maximum:
- maximum = value
- max_index = index
- if value < minimum:
- minimum = value
- min_index = index
-
- if 0. < value < min_pos:
- min_pos = value
- min_pos_index = index
-
- return _MinMaxResult(minimum if isfinite(minimum) else None,
- min_pos if isfinite(min_pos) else None,
- maximum if isfinite(maximum) else None,
- min_index if isfinite(minimum) else None,
- min_pos_index if isfinite(min_pos) else None,
- max_index if isfinite(maximum) else None)
-
-
-def min_max(data not None, bint min_positive=False, bint finite=False):
- """Returns min, max and optionally strictly positive min of data.
-
- It also computes the indices of first occurrence of min/max.
-
- NaNs are ignored while computing min/max unless all data is NaNs,
- in which case returned min/max are NaNs.
-
- The result data type is that of the input data, except for the following cases.
- For input using non-native bytes order, the result is returned as native
- floating-point or integers. For input using 16-bits floating-point,
- the result is returned as 32-bits floating-point.
-
- Examples:
-
- >>> import numpy
- >>> data = numpy.arange(10)
-
- Usage as a function returning min and max:
-
- >>> min_, max_ = min_max(data)
-
- Usage as a function returning a result object to access all information:
-
- >>> result = min_max(data) # Do not get positive min
- >>> result.minimum, result.argmin
- 0, 0
- >>> result.maximum, result.argmax
- 9, 10
- >>> result.min_positive, result.argmin_positive # Not computed
- None, None
-
- Getting strictly positive min information:
-
- >>> result = min_max(data, min_positive=True)
- >>> result.min_positive, result.argmin_positive # Computed
- 1, 1
-
- If *finite* is True, min/max information is computed only from finite data.
- Then, all result fields (include minimum and maximum) can be None
- when all data is infinity or NaN.
-
- :param data: Array-like dataset
- :param bool min_positive: True to compute the positive min and argmin
- Default: False.
- :param bool finite: True to compute min/max from finite data only
- Default: False.
- :returns: An object with minimum, maximum and min_positive attributes
- and the indices of first occurrence in the flattened data:
- argmin, argmax and argmin_positive attributes.
- If all data is <= 0 or min_positive argument is False, then
- min_positive and argmin_positive are None.
- :raises: ValueError if data is empty
- """
- data = numpy.array(data, copy=False)
- native_endian_dtype = data.dtype.newbyteorder('N')
- if native_endian_dtype.kind == 'f' and native_endian_dtype.itemsize == 2:
- # Use native float32 instead of float16
- native_endian_dtype = "=f4"
- data = numpy.ascontiguousarray(data, dtype=native_endian_dtype).ravel()
- if finite and data.dtype.kind == 'f':
- return _finite_min_max(data, min_positive)
- else:
- return _min_max(data, min_positive)
diff --git a/silx/math/fft/__init__.py b/silx/math/fft/__init__.py
deleted file mode 100644
index ea12cd6..0000000
--- a/silx/math/fft/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-__authors__ = ["P. Paleo"]
-__license__ = "MIT"
-__date__ = "12/12/2018"
-
-from .fft import FFT
diff --git a/silx/math/fft/basefft.py b/silx/math/fft/basefft.py
deleted file mode 100644
index 854ca37..0000000
--- a/silx/math/fft/basefft.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-import numpy as np
-from pkg_resources import parse_version
-
-
-def check_version(package, required_version):
- """
- Check whether a given package version is superior or equal to required_version.
- """
- try:
- ver = getattr(package, "__version__")
- except AttributeError:
- try:
- ver = getattr(package, "version")
- except Exception:
- return False
- req_v = parse_version(required_version)
- ver_v = parse_version(ver)
- return ver_v >= req_v
-
-
-class BaseFFT(object):
- """
- Base class for all FFT backends.
- """
- def __init__(self, **kwargs):
- self.__get_args(**kwargs)
-
- if self.shape is None and self.dtype is None and self.template is None:
- raise ValueError("Please provide either (shape and dtype) or template")
- if self.template is not None:
- self.shape = self.template.shape
- self.dtype = self.template.dtype
- self.user_data = self.template
- self.data_allocated = False
- self.__calc_axes()
- self.__set_dtypes()
- self.__calc_shape()
-
- def __get_args(self, **kwargs):
- expected_args = {
- "shape": None,
- "dtype": None,
- "template": None,
- "shape_out": None,
- "axes": None,
- "normalize": "rescale",
- }
- for arg_name, default_val in expected_args.items():
- if arg_name not in kwargs:
- # Base class was not instantiated properly
- raise ValueError("Please provide argument %s" % arg_name)
- setattr(self, arg_name, default_val)
- for arg_name, arg_val in kwargs.items():
- setattr(self, arg_name, arg_val)
-
- def __set_dtypes(self):
- dtypes_mapping = {
- np.dtype("float32"): np.complex64,
- np.dtype("float64"): np.complex128,
- np.dtype("complex64"): np.complex64,
- np.dtype("complex128"): np.complex128
- }
- dp = {
- np.dtype("float32"): np.float64,
- np.dtype("complex64"): np.complex128
- }
- self.dtype_in = np.dtype(self.dtype)
- if self.dtype_in not in dtypes_mapping:
- raise ValueError("Invalid input data type: got %s" %
- self.dtype_in
- )
- self.dtype_out = dtypes_mapping[self.dtype_in]
-
- def __calc_shape(self):
- # TODO allow for C2C even for real input data (?)
- if self.dtype_in in [np.float32, np.float64]:
- last_dim = self.shape[-1]//2 + 1
- # FFTW convention
- self.shape_out = self.shape[:-1] + (self.shape[-1]//2 + 1,)
- else:
- self.shape_out = self.shape
-
- def __calc_axes(self):
- default_axes = tuple(range(len(self.shape)))
- if self.axes is None:
- self.axes = default_axes
- self.user_axes = None
- else:
- self.user_axes = self.axes
- # Handle possibly negative axes
- self.axes = tuple(np.array(default_axes)[np.array(self.user_axes)])
-
- def _allocate(self, shape, dtype):
- raise ValueError("This should be implemented by back-end FFT")
-
- def set_data(self, dst, src, shape, dtype, copy=True):
- raise ValueError("This should be implemented by back-end FFT")
-
- def allocate_arrays(self):
- if not(self.data_allocated):
- self.data_in = self._allocate(self.shape, self.dtype_in)
- self.data_out = self._allocate(self.shape_out, self.dtype_out)
- self.data_allocated = True
-
- def set_input_data(self, data, copy=True):
- if data is None:
- return self.data_in
- else:
- return self.set_data(self.data_in, data, self.shape, self.dtype_in, copy=copy, name="data_in")
-
- def set_output_data(self, data, copy=True):
- if data is None:
- return self.data_out
- else:
- return self.set_data(self.data_out, data, self.shape_out, self.dtype_out, copy=copy, name="data_out")
-
- def fft(self, array, **kwargs):
- raise ValueError("This should be implemented by back-end FFT")
-
- def ifft(self, array, **kwargs):
- raise ValueError("This should be implemented by back-end FFT")
diff --git a/silx/math/fft/clfft.py b/silx/math/fft/clfft.py
deleted file mode 100644
index dad8ec1..0000000
--- a/silx/math/fft/clfft.py
+++ /dev/null
@@ -1,286 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-import numpy as np
-
-from .basefft import BaseFFT, check_version
-try:
- import pyopencl as cl
- import pyopencl.array as parray
- import gpyfft
- from gpyfft.fft import FFT as cl_fft
- from ...opencl.common import ocl
- __have_clfft__ = True
-except ImportError:
- __have_clfft__ = False
-
-
-# Check gpyfft version
-__required_gpyfft_version__ = "0.3.0"
-if __have_clfft__:
- __have_clfft__ = check_version(gpyfft, __required_gpyfft_version__)
-
-
-class CLFFT(BaseFFT):
- """Initialize a clfft plan.
-
- Please see FFT class for parameters help.
-
- CLFFT-specific parameters
- --------------------------
-
- :param pyopencl.Context ctx:
- If set to other than None, an existing pyopencl context is used.
- :param bool fast_math:
- If set to True, computations will be done with "fast math" mode,
- i.e., more speed but less accuracy.
- :param bool choose_best_device:
- Whether to automatically choose the best available OpenCL device.
- """
- def __init__(
- self,
- shape=None,
- dtype=None,
- template=None,
- shape_out=None,
- axes=None,
- normalize="rescale",
- ctx=None,
- fast_math=False,
- choose_best_device=True,
- ):
- if not(__have_clfft__) or not(__have_clfft__):
- raise ImportError("Please install pyopencl and gpyfft >= %s to use the OpenCL back-end" % __required_gpyfft_version__)
-
- super(CLFFT, self).__init__(
- shape=shape,
- dtype=dtype,
- template=template,
- shape_out=shape_out,
- axes=axes,
- normalize=normalize,
- )
- self.ctx = ctx
- self.choose_best_device = choose_best_device
- self.fast_math = fast_math
- self.backend = "clfft"
-
- self.fix_axes()
- self.init_context_queue()
- self.allocate_arrays()
- self.real_transform = np.isrealobj(self.data_in)
- self.compute_forward_plan()
- self.compute_inverse_plan()
- self.refs = {
- "data_in": self.data_in,
- "data_out": self.data_out,
- }
- # TODO
- # Either pyopencl ElementWiseKernel, or built-in clfft callbacks
- if self.normalize != "rescale":
- raise NotImplementedError(
- "Normalization modes other than rescale are not implemented with OpenCL backend yet."
- )
-
- def fix_axes(self):
- """
- "Fix" axes.
-
- clfft does not have the same convention as FFTW/cuda/numpy.
- """
- self.axes = self.axes[::-1]
-
- def _allocate(self, shape, dtype):
- ary = parray.empty(self.queue, shape, dtype=dtype)
- ary.fill(0)
- return ary
-
-
- def check_array(self, array, shape, dtype, copy=True):
- if array.shape != shape:
- raise ValueError("Invalid data shape: expected %s, got %s" %
- (shape, array.shape)
- )
- if array.dtype != dtype:
- raise ValueError("Invalid data type: expected %s, got %s" %
- (dtype, array.dtype)
- )
-
-
- def set_data(self, dst, src, shape, dtype, copy=True, name=None):
- """
- dst is a device array owned by the current instance
- (either self.data_in or self.data_out).
-
- copy is ignored for device<-> arrays.
- """
- self.check_array(src, shape, dtype)
- if isinstance(src, np.ndarray):
- if name == "data_out":
- # Makes little sense to provide output=numpy_array
- return dst
- if not(src.flags["C_CONTIGUOUS"]):
- src = np.ascontiguousarray(src, dtype=dtype)
- # working on underlying buffer is notably faster
- #~ dst[:] = src[:]
- evt = cl.enqueue_copy(self.queue, dst.data, src)
- evt.wait()
- elif isinstance(src, parray.Array):
- # No copy, use the data as self.d_input or self.d_output
- # (this prevents the use of in-place transforms, however).
- # We have to keep their old references.
- if name is None:
- # This should not happen
- raise ValueError("Please provide either copy=True or name != None")
- assert id(self.refs[name]) == id(dst) # DEBUG
- setattr(self, name, src)
- return src
- else:
- raise ValueError(
- "Invalid array type %s, expected numpy.ndarray or pyopencl.array" %
- type(src)
- )
- return dst
-
-
- def recover_array_references(self):
- self.data_in = self.refs["data_in"]
- self.data_out = self.refs["data_out"]
-
-
- def init_context_queue(self):
- if self.ctx is None:
- if self.choose_best_device:
- self.ctx = ocl.create_context()
- else:
- self.ctx = cl.create_some_context()
- self.queue = cl.CommandQueue(self.ctx)
-
-
- def compute_forward_plan(self):
- self.plan_forward = cl_fft(
- self.ctx,
- self.queue,
- self.data_in,
- out_array=self.data_out,
- axes=self.axes,
- fast_math=self.fast_math,
- real=self.real_transform,
- )
-
-
- def compute_inverse_plan(self):
- self.plan_inverse = cl_fft(
- self.ctx,
- self.queue,
- self.data_out,
- out_array=self.data_in,
- axes=self.axes,
- fast_math=self.fast_math,
- real=self.real_transform,
- )
-
-
- def update_forward_plan_arrays(self):
- self.plan_forward.data = self.data_in
- self.plan_forward.result = self.data_out
-
-
- def update_inverse_plan_arrays(self):
- self.plan_inverse.data = self.data_out
- self.plan_inverse.result = self.data_in
-
-
- def copy_output_if_numpy(self, dst, src):
- if isinstance(dst, parray.Array):
- return
- # working on underlying buffer is notably faster
- #~ dst[:] = src[:]
- evt = cl.enqueue_copy(self.queue, dst, src.data)
- evt.wait()
-
-
- def fft(self, array, output=None, do_async=False):
- """
- Perform a (forward) Fast Fourier Transform.
-
- :param Union[numpy.ndarray,pyopencl.array] array:
- Input data. Must be consistent with the current context.
- :param Union[numpy.ndarray,pyopencl.array] output:
- Output data. By default, output is a numpy.ndarray.
- :param bool do_async:
- Whether to perform operation in asynchronous mode.
- Default is False, meaning that we wait for transform to complete.
- """
- self.set_input_data(array, copy=False)
- self.set_output_data(output, copy=False)
- self.update_forward_plan_arrays()
- event, = self.plan_forward.enqueue()
- if not(do_async):
- event.wait()
- if output is not None:
- self.copy_output_if_numpy(output, self.data_out)
- res = output
- else:
- res = self.data_out.get()
- self.recover_array_references()
- return res
-
-
- def ifft(self, array, output=None, do_async=False):
- """
- Perform a (inverse) Fast Fourier Transform.
-
- :param Union[numpy.ndarray,pyopencl.array] array:
- Input data. Must be consistent with the current context.
- :param Union[numpy.ndarray,pyopencl.array] output:
- Output data. By default, output is a numpy.ndarray.
- :param bool do_async:
- Whether to perform operation in asynchronous mode.
- Default is False, meaning that we wait for transform to complete.
- """
- self.set_output_data(array, copy=False)
- self.set_input_data(output, copy=False)
- self.update_inverse_plan_arrays()
- event, = self.plan_inverse.enqueue(forward=False)
- if not(do_async):
- event.wait()
- if output is not None:
- self.copy_output_if_numpy(output, self.data_in)
- res = output
- else:
- res = self.data_in.get()
- self.recover_array_references()
- return res
-
-
- def __del__(self):
- # It seems that gpyfft underlying clFFT destructors are not called.
- # This results in the following warning:
- # Warning: Program terminating, but clFFT resources not freed.
- # Please consider explicitly calling clfftTeardown( )
- del self.plan_forward
- del self.plan_inverse
-
diff --git a/silx/math/fft/cufft.py b/silx/math/fft/cufft.py
deleted file mode 100644
index 848f3e6..0000000
--- a/silx/math/fft/cufft.py
+++ /dev/null
@@ -1,253 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-import numpy as np
-
-from .basefft import BaseFFT
-try:
- import pycuda.gpuarray as gpuarray
- from skcuda.fft import Plan
- from skcuda.fft import fft as cu_fft
- from skcuda.fft import ifft as cu_ifft
- __have_cufft__ = True
-except ImportError:
- __have_cufft__ = False
-
-
-class CUFFT(BaseFFT):
- """Initialize a cufft plan
-
- Please see FFT class for parameters help.
-
- CUFFT-specific parameters
- --------------------------
-
- :param pycuda.driver.Stream stream:
- Stream with which to associate the plan. If no stream is specified,
- the default stream is used.
- """
- def __init__(
- self,
- shape=None,
- dtype=None,
- template=None,
- shape_out=None,
- axes=None,
- normalize="rescale",
- stream=None,
- ):
- if not(__have_cufft__) or not(__have_cufft__):
- raise ImportError("Please install pycuda and scikit-cuda to use the CUDA back-end")
-
- super(CUFFT, self).__init__(
- shape=shape,
- dtype=dtype,
- template=template,
- shape_out=shape_out,
- axes=axes,
- normalize=normalize,
- )
- self.cufft_stream = stream
- self.backend = "cufft"
-
- self.configure_batched_transform()
- self.allocate_arrays()
- self.real_transform = np.isrealobj(self.data_in)
- self.compute_forward_plan()
- self.compute_inverse_plan()
- self.refs = {
- "data_in": self.data_in,
- "data_out": self.data_out,
- }
- self.configure_normalization()
-
- def _allocate(self, shape, dtype):
- return gpuarray.zeros(shape, dtype)
-
- # TODO support batched transform where batch is other than dimension 0
- def configure_batched_transform(self):
- self.cufft_batch_size = 1
- self.cufft_shape = self.shape
- if (self.axes is not None) and (len(self.axes) < len(self.shape)):
- # In the easiest case, the transform is computed along the fastest dimensions:
- # - 1D transforms of lines of 2D data
- # - 2D transforms of images of 3D data (stacked along slow dim)
- # - 1D transforms of 3D data along fastest dim
- # Otherwise, we have to configure cuda "advanced memory layout",
- # which is not implemented yet.
-
- data_ndims = len(self.shape)
- supported_axes = {
- 2: [(1,)],
- 3: [(1, 2), (2, 1), (1,), (2,)],
- }
- if self.axes not in supported_axes[data_ndims]:
- raise NotImplementedError("With the CUDA backend, batched transform is only supported along fastest dimensions")
- self.cufft_batch_size = self.shape[0]
- self.cufft_shape = self.shape[1:]
- if data_ndims == 3 and len(self.axes) == 1:
- # 1D transform on 3D data: here only supported along fast dim,
- # so batch_size is Nx*Ny
- self.cufft_batch_size = np.prod(self.shape[:2])
- self.cufft_shape = (self.shape[-1],)
- if len(self.cufft_shape) == 1:
- self.cufft_shape = self.cufft_shape[0]
-
- def configure_normalization(self):
- # TODO
- if self.normalize == "ortho":
- raise NotImplementedError(
- "Normalization mode 'ortho' is not implemented with CUDA backend yet."
- )
- self.cufft_scale_inverse = (self.normalize == "rescale")
-
- def check_array(self, array, shape, dtype, copy=True):
- if array.shape != shape:
- raise ValueError("Invalid data shape: expected %s, got %s" %
- (shape, array.shape))
- if array.dtype != dtype:
- raise ValueError("Invalid data type: expected %s, got %s" %
- (dtype, array.dtype))
-
- def set_data(self, dst, src, shape, dtype, copy=True, name=None):
- """
- dst is a device array owned by the current instance
- (either self.data_in or self.data_out).
-
- copy is ignored for device<-> arrays.
- """
- self.check_array(src, shape, dtype)
- if isinstance(src, np.ndarray):
- if name == "data_out":
- # Makes little sense to provide output=numpy_array
- return dst
- if not(src.flags["C_CONTIGUOUS"]):
- src = np.ascontiguousarray(src, dtype=dtype)
- dst[:] = src[:]
- elif isinstance(src, gpuarray.GPUArray):
- # No copy, use the data as self.d_input or self.d_output
- # (this prevents the use of in-place transforms, however).
- # We have to keep their old references.
- if name is None:
- # This should not happen
- raise ValueError("Please provide either copy=True or name != None")
- assert id(self.refs[name]) == id(dst) # DEBUG
- setattr(self, name, src)
- return src
- else:
- raise ValueError(
- "Invalid array type %s, expected numpy.ndarray or pycuda.gpuarray" %
- type(src)
- )
- return dst
-
- def recover_array_references(self):
- self.data_in = self.refs["data_in"]
- self.data_out = self.refs["data_out"]
-
- def compute_forward_plan(self):
- self.plan_forward = Plan(
- self.cufft_shape,
- self.dtype,
- self.dtype_out,
- batch=self.cufft_batch_size,
- stream=self.cufft_stream,
- # cufft extensible plan API is only supported after 0.5.1
- # (commit 65288d28ca0b93e1234133f8d460dc6becb65121)
- # but there is still no official 0.5.2
- #~ auto_allocate=True # cufft extensible plan API
- )
-
- def compute_inverse_plan(self):
- self.plan_inverse = Plan(
- self.cufft_shape, # not shape_out
- self.dtype_out,
- self.dtype,
- batch=self.cufft_batch_size,
- stream=self.cufft_stream,
- # cufft extensible plan API is only supported after 0.5.1
- # (commit 65288d28ca0b93e1234133f8d460dc6becb65121)
- # but there is still no official 0.5.2
- #~ auto_allocate=True
- )
-
- def copy_output_if_numpy(self, dst, src):
- if isinstance(dst, gpuarray.GPUArray):
- return
- dst[:] = src[:]
-
- def fft(self, array, output=None):
- """
- Perform a (forward) Fast Fourier Transform.
-
- :param Union[numpy.ndarray,pycuda.gpuarray] array:
- Input data. Must be consistent with the current context.
- :param Union[numpy.ndarray,pycuda.gpuarray] output:
- Output data. By default, output is a numpy.ndarray.
- """
- data_in = self.set_input_data(array, copy=False)
- data_out = self.set_output_data(output, copy=False)
-
- cu_fft(
- data_in,
- data_out,
- self.plan_forward,
- scale=False
- )
-
- if output is not None:
- self.copy_output_if_numpy(output, self.data_out)
- res = output
- else:
- res = self.data_out.get()
- self.recover_array_references()
- return res
-
- def ifft(self, array, output=None):
- """
- Perform a (inverse) Fast Fourier Transform.
-
- :param Union[numpy.ndarray,pycuda.gpuarray] array:
- Input data. Must be consistent with the current context.
- :param Union[numpy.ndarray,pycuda.gpuarray] output:
- Output data. By default, output is a numpy.ndarray.
- """
- data_in = self.set_output_data(array, copy=False)
- data_out = self.set_input_data(output, copy=False)
-
- cu_ifft(
- data_in,
- data_out,
- self.plan_inverse,
- scale=self.cufft_scale_inverse,
- )
-
- if output is not None:
- self.copy_output_if_numpy(output, self.data_in)
- res = output
- else:
- res = self.data_in.get()
- self.recover_array_references()
- return res
diff --git a/silx/math/fft/fft.py b/silx/math/fft/fft.py
deleted file mode 100644
index eb0d73b..0000000
--- a/silx/math/fft/fft.py
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-from .fftw import FFTW
-from .clfft import CLFFT
-from .npfft import NPFFT
-from .cufft import CUFFT
-
-
-def FFT(
- shape=None,
- dtype=None,
- template=None,
- shape_out=None,
- axes=None,
- normalize="rescale",
- backend="numpy",
- **kwargs
-):
- """
- Initialize a FFT plan.
-
- :param List[int] shape:
- Shape of the input data.
- :param numpy.dtype dtype:
- Data type of the input data.
- :param numpy.ndarray template:
- Optional data, replacement for "shape" and "dtype".
- If provided, the arguments "shape" and "dtype" are ignored,
- and are instead inferred from it.
- :param List[int] shape_out:
- Optional shape of output data.
- By default, the data has the same shape as the input
- data (in case of C2C transform), or a shape with the last dimension halved
- (in case of R2C transform). If shape_out is provided, it must be greater
- or equal than the shape of input data. In this case, FFT is performed
- with zero-padding.
- :param List[int] axes:
- Axes along which FFT is computed.
- * For 2D transform: axes=(1,0)
- * For batched 1D transform of 2D image: axes=(0,)
- :param str normalize:
- Whether to normalize FFT and IFFT. Possible values are:
- * "rescale": in this case, Fourier data is divided by "N"
- before IFFT, so that (FFT(data)) = data
- * "ortho": in this case, FFT and IFFT are adjoint of eachother,
- the transform is unitary. Both FFT and IFFT are scaled with 1/sqrt(N).
- * "none": no normalizatio is done : IFFT(FFT(data)) = data*N
- :param str backend:
- FFT Backend to use. Value can be "numpy", "fftw", "opencl", "cuda".
- """
- backends = {
- "numpy": NPFFT,
- "np": NPFFT,
- "fftw": FFTW,
- "opencl": CLFFT,
- "clfft": CLFFT,
- "cuda": CUFFT,
- "cufft": CUFFT,
- }
-
- backend = backend.lower()
- if backend not in backends:
- raise ValueError("Unknown backend %s, available are %s" % (backend, backends))
- F = backends[backend](
- shape=shape,
- dtype=dtype,
- template=template,
- shape_out=shape_out,
- axes=axes,
- normalize=normalize,
- **kwargs
- )
- return F
diff --git a/silx/math/fft/fftw.py b/silx/math/fft/fftw.py
deleted file mode 100644
index ff6966c..0000000
--- a/silx/math/fft/fftw.py
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-import numpy as np
-
-from .basefft import BaseFFT, check_version
-try:
- import pyfftw
- __have_fftw__ = True
-except ImportError:
- __have_fftw__ = False
-
-
-# Check pyfftw version
-__required_pyfftw_version__ = "0.10.0"
-if __have_fftw__:
- __have_fftw__ = check_version(pyfftw, __required_pyfftw_version__)
-
-
-class FFTW(BaseFFT):
- """Initialize a FFTW plan.
-
- Please see FFT class for parameters help.
-
- FFTW-specific parameters
- -------------------------
-
- :param bool check_alignment:
- If set to True and "data" is provided, this will enforce the input data
- to be "byte aligned", which might imply extra memory usage.
- :param int num_threads:
- Number of threads for computing FFT.
- """
- def __init__(
- self,
- shape=None,
- dtype=None,
- template=None,
- shape_out=None,
- axes=None,
- normalize="rescale",
- check_alignment=False,
- num_threads=1,
- ):
- if not(__have_fftw__):
- raise ImportError("Please install pyfftw >= %s to use the FFTW back-end" % __required_pyfftw_version__)
- super(FFTW, self).__init__(
- shape=shape,
- dtype=dtype,
- template=template,
- shape_out=shape_out,
- axes=axes,
- normalize=normalize,
- )
- self.check_alignment = check_alignment
- self.num_threads = num_threads
- self.backend = "fftw"
-
- self.allocate_arrays()
- self.set_fftw_flags()
- self.compute_forward_plan()
- self.compute_inverse_plan()
- self.refs = {
- "data_in": self.data_in,
- "data_out": self.data_out,
- }
-
- def set_fftw_flags(self):
- self.fftw_flags = ('FFTW_MEASURE', ) # TODO
- self.fftw_planning_timelimit = None # TODO
- self.fftw_norm_modes = {
- "rescale": {"ortho": False, "normalize": True},
- "ortho": {"ortho": True, "normalize": False},
- "none": {"ortho": False, "normalize": False},
- }
- if self.normalize not in self.fftw_norm_modes:
- raise ValueError("Unknown normalization mode %s. Possible values are %s" %
- (self.normalize, self.fftw_norm_modes.keys())
- )
- self.fftw_norm_mode = self.fftw_norm_modes[self.normalize]
-
- def _allocate(self, shape, dtype):
- return pyfftw.zeros_aligned(shape, dtype=dtype)
-
- def check_array(self, array, shape, dtype, copy=True):
- if array.shape != shape:
- raise ValueError("Invalid data shape: expected %s, got %s" %
- (shape, array.shape)
- )
- if array.dtype != dtype:
- raise ValueError("Invalid data type: expected %s, got %s" %
- (dtype, array.dtype)
- )
-
- def set_data(self, self_array, array, shape, dtype, copy=True, name=None):
- """
- :param self_array: array owned by the current instance
- (either self.data_in or self.data_out).
- :type: numpy.ndarray
- :param self_array: data to set
- :type: numpy.ndarray
- :type tuple shape: shape of the array
- :param dtype: type of the array
- :type: numpy.dtype
- :param bool copy: should we copy the array
- :param str name: name of the array
-
- Copies are avoided when possible.
- """
- self.check_array(array, shape, dtype)
- if id(self.refs[name]) == id(array):
- # nothing to do: fft is performed on self.data_in or self.data_out
- arr_to_use = self.refs[name]
- if self.check_alignment and not(pyfftw.is_byte_aligned(array)):
- # If the array is not properly aligned,
- # create a temp. array copy it to self.data_in or self.data_out
- self_array[:] = array[:]
- arr_to_use = self_array
- else:
- # If the array is properly aligned, use it directly
- if copy:
- arr_to_use = np.copy(array)
- else:
- arr_to_use = array
- return arr_to_use
-
- def compute_forward_plan(self):
- self.plan_forward = pyfftw.FFTW(
- self.data_in,
- self.data_out,
- axes=self.axes,
- direction='FFTW_FORWARD',
- flags=self.fftw_flags,
- threads=self.num_threads,
- planning_timelimit=self.fftw_planning_timelimit,
- # the following seems to be taken into account only when using __call__
- ortho=self.fftw_norm_mode["ortho"],
- normalise_idft=self.fftw_norm_mode["normalize"],
- )
-
- def compute_inverse_plan(self):
- self.plan_inverse = pyfftw.FFTW(
- self.data_out,
- self.data_in,
- axes=self.axes,
- direction='FFTW_BACKWARD',
- flags=self.fftw_flags,
- threads=self.num_threads,
- planning_timelimit=self.fftw_planning_timelimit,
- # the following seem to be taken into account only when using __call__
- ortho=self.fftw_norm_mode["ortho"],
- normalise_idft=self.fftw_norm_mode["normalize"],
- )
-
- def fft(self, array, output=None):
- """
- Perform a (forward) Fast Fourier Transform.
-
- :param numpy.ndarray array:
- Input data. Must be consistent with the current context.
- :param numpy.ndarray output:
- Optional output data.
- """
- data_in = self.set_input_data(array, copy=False)
- data_out = self.set_output_data(output, copy=False)
- self.plan_forward.update_arrays(data_in, data_out)
- # execute.__call__ does both update_arrays() and normalization
- self.plan_forward(
- ortho=self.fftw_norm_mode["ortho"],
- )
- self.plan_forward.update_arrays(self.refs["data_in"], self.refs["data_out"])
- return data_out
-
- def ifft(self, array, output=None):
- """
- Perform a (inverse) Fast Fourier Transform.
-
- :param numpy.ndarray array:
- Input data. Must be consistent with the current context.
- :param numpy.ndarray output:
- Optional output data.
- """
- data_in = self.set_output_data(array, copy=False)
- data_out = self.set_input_data(output, copy=False)
- self.plan_inverse.update_arrays(data_in, data_out)
- # execute.__call__ does both update_arrays() and normalization
- self.plan_inverse(
- ortho=self.fftw_norm_mode["ortho"],
- normalise_idft=self.fftw_norm_mode["normalize"]
- )
- self.plan_inverse.update_arrays(self.refs["data_out"], self.refs["data_in"])
- return data_out
diff --git a/silx/math/fft/npfft.py b/silx/math/fft/npfft.py
deleted file mode 100644
index 20351de..0000000
--- a/silx/math/fft/npfft.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-import numpy as np
-
-from .basefft import BaseFFT
-
-
-class NPFFT(BaseFFT):
- """Initialize a numpy plan.
-
- Please see FFT class for parameters help.
- """
- def __init__(
- self,
- shape=None,
- dtype=None,
- template=None,
- shape_out=None,
- axes=None,
- normalize="rescale",
- ):
- super(NPFFT, self).__init__(
- shape=shape,
- dtype=dtype,
- template=template,
- shape_out=shape_out,
- axes=axes,
- normalize=normalize,
- )
- self.backend = "numpy"
- self.real_transform = False
- if template is not None and np.isrealobj(template):
- self.real_transform = True
- # For numpy functions.
- # TODO Issue warning if user wants ifft(fft(data)) = N*data ?
- if normalize != "ortho":
- self.normalize = None
- self.set_fft_functions()
- #~ self.allocate_arrays() # not needed for this backend
- self.compute_plans()
-
-
- def set_fft_functions(self):
- # (fwd, inv) = _fft_functions[is_real][ndim]
- self._fft_functions = {
- True: {
- 1: (np.fft.rfft, np.fft.irfft),
- 2: (np.fft.rfft2, np.fft.irfft2),
- 3: (np.fft.rfftn, np.fft.irfftn),
- },
- False: {
- 1: (np.fft.fft, np.fft.ifft),
- 2: (np.fft.fft2, np.fft.ifft2),
- 3: (np.fft.fftn, np.fft.ifftn),
- }
- }
-
-
- def _allocate(self, shape, dtype):
- return np.zeros(self.queue, shape, dtype=dtype)
-
-
- def compute_plans(self):
- ndim = len(self.shape)
- funcs = self._fft_functions[self.real_transform][np.minimum(ndim, 3)]
- if np.version.version[:4] in ["1.8.", "1.9."]:
- # norm keyword was introduced in 1.10 and we support numpy >= 1.8
- self.numpy_args = {}
- else:
- self.numpy_args = {"norm": self.normalize}
- # Batched transform
- if (self.user_axes is not None) and len(self.user_axes) < ndim:
- funcs = self._fft_functions[self.real_transform][np.minimum(ndim-1, 3)]
- self.numpy_args["axes"] = self.user_axes
- # Special case of batched 1D transform on 2D data
- if ndim == 2:
- assert len(self.user_axes) == 1
- self.numpy_args["axis"] = self.user_axes[0]
- self.numpy_args.pop("axes")
- self.numpy_funcs = funcs
-
-
- def fft(self, array):
- """
- Perform a (forward) Fast Fourier Transform.
-
- :param numpy.ndarray array:
- Input data. Must be consistent with the current context.
- """
- return self.numpy_funcs[0](array, **self.numpy_args)
-
-
- def ifft(self, array):
- """
- Perform a (inverse) Fast Fourier Transform.
-
- :param numpy.ndarray array:
- Input data. Must be consistent with the current context.
- """
- return self.numpy_funcs[1](array, **self.numpy_args)
-
diff --git a/silx/math/fft/setup.py b/silx/math/fft/setup.py
deleted file mode 100644
index 76bb864..0000000
--- a/silx/math/fft/setup.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["P. Naudet"]
-__license__ = "MIT"
-__date__ = "12/12/2018"
-
-import numpy
-from numpy.distutils.misc_util import Configuration
-
-
-def configuration(parent_package='', top_path=None):
- config = Configuration('fft', parent_package, top_path)
- config.add_subpackage('test')
- return config
-
-
-if __name__ == "__main__":
- from numpy.distutils.core import setup
- setup(configuration=configuration)
diff --git a/silx/math/fft/test/__init__.py b/silx/math/fft/test/__init__.py
deleted file mode 100644
index 83f8926..0000000
--- a/silx/math/fft/test/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-from .test_fft import suite
diff --git a/silx/math/fft/test/test_fft.py b/silx/math/fft/test/test_fft.py
deleted file mode 100644
index 9ef2fd2..0000000
--- a/silx/math/fft/test/test_fft.py
+++ /dev/null
@@ -1,270 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Test of the FFT module"""
-
-import numpy as np
-import unittest
-import logging
-try:
- from scipy.misc import ascent
- __have_scipy = True
-except ImportError:
- __have_scipy = False
-from silx.utils.testutils import ParametricTestCase
-from silx.math.fft.fft import FFT
-from silx.math.fft.clfft import __have_clfft__
-from silx.math.fft.cufft import __have_cufft__
-from silx.math.fft.fftw import __have_fftw__
-
-from silx.test.utils import test_options
-
-logger = logging.getLogger(__name__)
-
-
-class TransformInfos(object):
- def __init__(self):
- self.dimensions = [
- "1D",
- "batched_1D",
- "2D",
- "batched_2D",
- "3D",
- ]
- self.modes = {
- "R2C": np.float32,
- "R2C_double": np.float64,
- "C2C": np.complex64,
- "C2C_double": np.complex128,
- }
- self.sizes = {
- "1D": [(128,), (127,)],
- "2D": [(128, 128), (128, 127), (127, 128), (127, 127)],
- "3D": [(64, 64, 64), (64, 64, 63), (64, 63, 64), (63, 64, 64),
- (64, 63, 63), (63, 64, 63), (63, 63, 64), (63, 63, 63)]
- }
- self.axes = {
- "1D": None,
- "batched_1D": (-1,),
- "2D": None,
- "batched_2D": (-2, -1),
- "3D": None,
- }
- self.sizes["batched_1D"] = self.sizes["2D"]
- self.sizes["batched_2D"] = self.sizes["3D"]
-
-
-class TestData(object):
- def __init__(self):
- self.data = ascent().astype("float32")
- self.data1d = self.data[:, 0] # non-contiguous data
- self.data3d = np.tile(self.data[:64, :64], (64, 1, 1))
- self.data_refs = {
- 1: self.data1d,
- 2: self.data,
- 3: self.data3d,
- }
-
-
-@unittest.skipUnless(__have_scipy, "scipy is missing")
-class TestFFT(ParametricTestCase):
- """Test cuda/opencl/fftw backends of FFT"""
-
- def setUp(self):
- self.tol = {
- np.dtype("float32"): 1e-3,
- np.dtype("float64"): 1e-9,
- np.dtype("complex64"): 1e-3,
- np.dtype("complex128"): 1e-9,
- }
- self.transform_infos = TransformInfos()
- self.test_data = TestData()
-
- @staticmethod
- def calc_mae(arr1, arr2):
- """
- Compute the Max Absolute Error between two arrays
- """
- return np.max(np.abs(arr1 - arr2))
-
- @unittest.skipIf(not __have_cufft__,
- "cuda back-end requires pycuda and scikit-cuda")
- def test_cuda(self):
- import pycuda.autoinit
-
- # Error is higher when using cuda. fast_math mode ?
- self.tol[np.dtype("float32")] *= 2
-
- self.__run_tests(backend="cuda")
-
- @unittest.skipIf(not __have_clfft__,
- "opencl back-end requires pyopencl and gpyfft")
- def test_opencl(self):
- from silx.opencl.common import ocl
- if ocl is not None:
- self.__run_tests(backend="opencl", ctx=ocl.create_context())
-
- @unittest.skipIf(not __have_fftw__,
- "fftw back-end requires pyfftw")
- def test_fftw(self):
- self.__run_tests(backend="fftw")
-
- def __run_tests(self, backend, **extra_args):
- """Run all tests with the given backend
-
- :param str backend:
- :param dict extra_args: Additional arguments to provide to FFT
- """
- for trdim in self.transform_infos.dimensions:
- for mode in self.transform_infos.modes:
- for size in self.transform_infos.sizes[trdim]:
- with self.subTest(trdim=trdim, mode=mode, size=size):
- self.__test(backend, trdim, mode, size, **extra_args)
-
- def __test(self, backend, trdim, mode, size, **extra_args):
- """Compare given backend with numpy for given conditions"""
- logger.debug("backend: %s, trdim: %s, mode: %s, size: %s",
- backend, trdim, mode, str(size))
- if size == "3D" and test_options.TEST_LOW_MEM:
- self.skipTest("low mem")
-
- ndim = len(size)
- input_data = self.test_data.data_refs[ndim].astype(
- self.transform_infos.modes[mode])
- tol = self.tol[np.dtype(input_data.dtype)]
- if trdim == "3D":
- tol *= 10 # Error is relatively high in high dimensions
- # It seems that cuda has problems with C2D batched 1D
- if trdim == "batched_1D" and backend == "cuda" and mode == "C2C":
- tol *= 10
-
- # Python < 3.5 does not want to mix **extra_args with existing kwargs
- fft_args = {
- "template": input_data,
- "axes": self.transform_infos.axes[trdim],
- "backend": backend,
- }
- fft_args.update(extra_args)
- F = FFT(
- **fft_args
- )
- F_np = FFT(
- template=input_data,
- axes=self.transform_infos.axes[trdim],
- backend="numpy"
- )
-
- # Forward FFT
- res = F.fft(input_data)
- res_np = F_np.fft(input_data)
- mae = self.calc_mae(res, res_np)
- all_close = np.allclose(res, res_np, atol=tol, rtol=tol),
- self.assertTrue(
- all_close,
- "FFT %s:%s, MAE(%s, numpy) = %f (tol = %.2e)" % (mode, trdim, backend, mae, tol)
- )
-
- # Inverse FFT
- res2 = F.ifft(res)
- mae = self.calc_mae(res2, input_data)
- self.assertTrue(
- mae < tol,
- "IFFT %s:%s, MAE(%s, numpy) = %f" % (mode, trdim, backend, mae)
- )
-
-
-@unittest.skipUnless(__have_scipy, "scipy is missing")
-class TestNumpyFFT(ParametricTestCase):
- """
- Test the Numpy backend individually.
- """
-
- def setUp(self):
- transforms = {
- "1D": {
- True: (np.fft.rfft, np.fft.irfft),
- False: (np.fft.fft, np.fft.ifft),
- },
- "2D": {
- True: (np.fft.rfft2, np.fft.irfft2),
- False: (np.fft.fft2, np.fft.ifft2),
- },
- "3D": {
- True: (np.fft.rfftn, np.fft.irfftn),
- False: (np.fft.fftn, np.fft.ifftn),
- },
- }
- transforms["batched_1D"] = transforms["1D"]
- transforms["batched_2D"] = transforms["2D"]
- self.transforms = transforms
- self.transform_infos = TransformInfos()
- self.test_data = TestData()
-
- def test(self):
- """Test the numpy backend against native fft.
-
- Results should be exactly the same.
- """
- for trdim in self.transform_infos.dimensions:
- for mode in self.transform_infos.modes:
- for size in self.transform_infos.sizes[trdim]:
- with self.subTest(trdim=trdim, mode=mode, size=size):
- self.__test(trdim, mode, size)
-
- def __test(self, trdim, mode, size):
- logger.debug("trdim: %s, mode: %s, size: %s", trdim, mode, str(size))
- ndim = len(size)
- input_data = self.test_data.data_refs[ndim].astype(
- self.transform_infos.modes[mode])
- np_fft, np_ifft = self.transforms[trdim][np.isrealobj(input_data)]
-
- F = FFT(
- template=input_data,
- axes=self.transform_infos.axes[trdim],
- backend="numpy"
- )
- # Test FFT
- res = F.fft(input_data)
- ref = np_fft(input_data)
- self.assertTrue(np.allclose(res, ref))
-
- # Test IFFT
- res2 = F.ifft(res)
- ref2 = np_ifft(ref)
- self.assertTrue(np.allclose(res2, ref2))
-
-
-def suite():
- suite = unittest.TestSuite()
- for cls in (TestNumpyFFT, TestFFT):
- suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(cls))
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
-
-
diff --git a/silx/math/fit/__init__.py b/silx/math/fit/__init__.py
deleted file mode 100644
index 29e6a9e..0000000
--- a/silx/math/fit/__init__.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "22/06/2016"
-
-
-from .leastsq import leastsq, chisq_alpha_beta
-from .leastsq import \
- CFREE, CPOSITIVE, CQUOTED, CFIXED, \
- CFACTOR, CDELTA, CSUM
-
-from .functions import *
-from .filters import *
-from .peaks import peak_search, guess_fwhm
-from .fitmanager import FitManager
-from .fittheory import FitTheory
diff --git a/silx/math/fit/bgtheories.py b/silx/math/fit/bgtheories.py
deleted file mode 100644
index 631c43e..0000000
--- a/silx/math/fit/bgtheories.py
+++ /dev/null
@@ -1,440 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-#
-# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-########################################################################### */
-"""This modules defines a set of background model functions and associated
-estimation functions in a format that can be imported into a
-:class:`silx.math.fit.FitManager` object.
-
-A background function is a function that you want to add to a regular fit
-function prior to fitting the sum of both functions. This is useful, for
-instance, if you need to fit multiple gaussian peaks in an array of
-measured data points when the measurement is polluted by a background signal.
-
-The models include common background models such as a constant value or a
-linear background.
-
-It also includes background computation filters - *strip* and *snip* - that
-can extract a more complex low-curvature background signal from a signal with
-peaks having higher curvatures.
-
-The source code of this module can serve as a template for defining your
-own fit background theories. The minimal skeleton of such a theory definition
-file is::
-
- from silx.math.fit.fittheory import FitTheory
-
- def bgfunction1(x, y0, …):
- bg_signal = …
- return bg_signal
-
- def estimation_function1(x, y):
- …
- estimated_params = …
- constraints = …
- return estimated_params, constraints
-
- THEORY = {
- 'bg_theory_name1': FitTheory(
- description='Description of theory 1',
- function=bgfunction1,
- parameters=('param name 1', 'param name 2', …),
- estimate=estimation_function1,
- configure=configuration_function1,
- derivative=derivative_function1,
- is_background=True),
- 'theory_name_2': …,
- }
-"""
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "16/01/2017"
-
-from collections import OrderedDict
-import numpy
-from silx.math.fit.filters import strip, snip1d,\
- savitsky_golay
-from silx.math.fit.fittheory import FitTheory
-
-CONFIG = {
- "SmoothingFlag": False,
- "SmoothingWidth": 5,
- "AnchorsFlag": False,
- "AnchorsList": [],
- "StripWidth": 2,
- "StripIterations": 5000,
- "StripThresholdFactor": 1.0,
- "SnipWidth": 16,
- "EstimatePolyOnStrip": True
-}
-
-# to avoid costly computations when parameters stay the same
-_BG_STRIP_OLDY = numpy.array([])
-_BG_STRIP_OLDPARS = [0, 0]
-_BG_STRIP_OLDBG = numpy.array([])
-
-_BG_SNIP_OLDY = numpy.array([])
-_BG_SNIP_OLDWIDTH = None
-_BG_SNIP_OLDBG = numpy.array([])
-
-
-_BG_OLD_ANCHORS = []
-_BG_OLD_ANCHORS_FLAG = None
-
-_BG_SMOOTH_OLDWIDTH = None
-_BG_SMOOTH_OLDFLAG = None
-
-
-def _convert_anchors_to_indices(x):
- """Anchors stored in CONFIG["AnchorsList"] are abscissa.
- Convert then to indices (take first index where x >= anchor),
- then return the list of indices.
-
- :param x: Original array of abscissa
- :return: List of indices of anchors in x array.
- If CONFIG['AnchorsFlag'] is False or None, or if the list
- of indices is empty, return None.
- """
- # convert anchor X abscissa to index
- if CONFIG['AnchorsFlag'] and CONFIG['AnchorsList'] is not None:
- anchors_indices = []
- for anchor_x in CONFIG['AnchorsList']:
- if anchor_x <= x[0]:
- continue
- # take the first index where x > anchor_x
- indices = numpy.nonzero(x >= anchor_x)[0]
- if len(indices):
- anchors_indices.append(min(indices))
- if not len(anchors_indices):
- anchors_indices = None
- else:
- anchors_indices = None
-
- return anchors_indices
-
-
-def strip_bg(x, y0, width, niter):
- """Extract and return the strip bg from y0.
-
- Use anchors coordinates in CONFIG["AnchorsList"] if flag
- CONFIG["AnchorsFlag"] is True. Convert anchors from x coordinate
- to array index prior to passing it to silx.math.fit.filters.strip
-
- :param x: Abscissa array
- :param x: Ordinate array (data values at x positions)
- :param width: strip width
- :param niter: strip niter
- """
- global _BG_STRIP_OLDY
- global _BG_STRIP_OLDPARS
- global _BG_STRIP_OLDBG
- global _BG_SMOOTH_OLDWIDTH
- global _BG_SMOOTH_OLDFLAG
- global _BG_OLD_ANCHORS
- global _BG_OLD_ANCHORS_FLAG
-
- parameters_changed =\
- _BG_STRIP_OLDPARS != [width, niter] or\
- _BG_SMOOTH_OLDWIDTH != CONFIG["SmoothingWidth"] or\
- _BG_SMOOTH_OLDFLAG != CONFIG["SmoothingFlag"] or\
- _BG_OLD_ANCHORS_FLAG != CONFIG["AnchorsFlag"] or\
- _BG_OLD_ANCHORS != CONFIG["AnchorsList"]
-
- # same parameters
- if not parameters_changed:
- # same data
- if numpy.array_equal(_BG_STRIP_OLDY, y0):
- # same result
- return _BG_STRIP_OLDBG
-
- _BG_STRIP_OLDY = y0
- _BG_STRIP_OLDPARS = [width, niter]
- _BG_SMOOTH_OLDWIDTH = CONFIG["SmoothingWidth"]
- _BG_SMOOTH_OLDFLAG = CONFIG["SmoothingFlag"]
- _BG_OLD_ANCHORS = CONFIG["AnchorsList"]
- _BG_OLD_ANCHORS_FLAG = CONFIG["AnchorsFlag"]
-
- y1 = savitsky_golay(y0, CONFIG["SmoothingWidth"]) if CONFIG["SmoothingFlag"] else y0
-
- anchors_indices = _convert_anchors_to_indices(x)
-
- background = strip(y1,
- w=width,
- niterations=niter,
- factor=CONFIG["StripThresholdFactor"],
- anchors=anchors_indices)
-
- _BG_STRIP_OLDBG = background
-
- return background
-
-
-def snip_bg(x, y0, width):
- """Compute the snip bg for y0"""
- global _BG_SNIP_OLDY
- global _BG_SNIP_OLDWIDTH
- global _BG_SNIP_OLDBG
- global _BG_SMOOTH_OLDWIDTH
- global _BG_SMOOTH_OLDFLAG
- global _BG_OLD_ANCHORS
- global _BG_OLD_ANCHORS_FLAG
-
- parameters_changed =\
- _BG_SNIP_OLDWIDTH != width or\
- _BG_SMOOTH_OLDWIDTH != CONFIG["SmoothingWidth"] or\
- _BG_SMOOTH_OLDFLAG != CONFIG["SmoothingFlag"] or\
- _BG_OLD_ANCHORS_FLAG != CONFIG["AnchorsFlag"] or\
- _BG_OLD_ANCHORS != CONFIG["AnchorsList"]
-
- # same parameters
- if not parameters_changed:
- # same data
- if numpy.sum(_BG_SNIP_OLDY == y0) == len(y0):
- # same result
- return _BG_SNIP_OLDBG
-
- _BG_SNIP_OLDY = y0
- _BG_SNIP_OLDWIDTH = width
- _BG_SMOOTH_OLDWIDTH = CONFIG["SmoothingWidth"]
- _BG_SMOOTH_OLDFLAG = CONFIG["SmoothingFlag"]
- _BG_OLD_ANCHORS = CONFIG["AnchorsList"]
- _BG_OLD_ANCHORS_FLAG = CONFIG["AnchorsFlag"]
-
- y1 = savitsky_golay(y0, CONFIG["SmoothingWidth"]) if CONFIG["SmoothingFlag"] else y0
-
- anchors_indices = _convert_anchors_to_indices(x)
-
- if anchors_indices is None or not len(anchors_indices):
- anchors_indices = [0, len(y1) - 1]
-
- background = numpy.zeros_like(y1)
- previous_anchor = 0
- for anchor_index in anchors_indices:
- if (anchor_index > previous_anchor) and (anchor_index < len(y1)):
- background[previous_anchor:anchor_index] =\
- snip1d(y1[previous_anchor:anchor_index],
- width)
- previous_anchor = anchor_index
-
- if previous_anchor < len(y1):
- background[previous_anchor:] = snip1d(y1[previous_anchor:],
- width)
-
- _BG_SNIP_OLDBG = background
-
- return background
-
-
-def estimate_linear(x, y):
- """
- Estimate the linear parameters (constant, slope) of a y signal.
-
- Strip peaks, then perform a linear regression.
- """
- bg = strip_bg(x, y,
- width=CONFIG["StripWidth"],
- niter=CONFIG["StripIterations"])
- n = float(len(bg))
- Sy = numpy.sum(bg)
- Sx = float(numpy.sum(x))
- Sxx = float(numpy.sum(x * x))
- Sxy = float(numpy.sum(x * bg))
-
- deno = n * Sxx - (Sx * Sx)
- if deno != 0:
- bg = (Sxx * Sy - Sx * Sxy) / deno
- slope = (n * Sxy - Sx * Sy) / deno
- else:
- bg = 0.0
- slope = 0.0
- estimated_par = [bg, slope]
- # code = 0: FREE
- constraints = [[0, 0, 0], [0, 0, 0]]
- return estimated_par, constraints
-
-
-def estimate_strip(x, y):
- """Estimation function for strip parameters.
-
- Return parameters as defined in CONFIG dict,
- set constraints to FIXED.
- """
- estimated_par = [CONFIG["StripWidth"],
- CONFIG["StripIterations"]]
- constraints = numpy.zeros((len(estimated_par), 3), numpy.float64)
- # code = 3: FIXED
- constraints[0][0] = 3
- constraints[1][0] = 3
- return estimated_par, constraints
-
-
-def estimate_snip(x, y):
- """Estimation function for snip parameters.
-
- Return parameters as defined in CONFIG dict,
- set constraints to FIXED.
- """
- estimated_par = [CONFIG["SnipWidth"]]
- constraints = numpy.zeros((len(estimated_par), 3), numpy.float64)
- # code = 3: FIXED
- constraints[0][0] = 3
- return estimated_par, constraints
-
-
-def poly(x, y, *pars):
- """Order n polynomial.
- The order of the polynomial is defined by the number of
- coefficients (``*pars``).
-
- """
- p = numpy.poly1d(pars)
- return p(x)
-
-
-def estimate_poly(x, y, deg=2):
- """Estimate polynomial coefficients.
-
- """
- # extract bg signal with strip, to estimate polynomial on background
- if CONFIG["EstimatePolyOnStrip"]:
- y = strip_bg(x, y,
- CONFIG["StripWidth"],
- CONFIG["StripIterations"])
- pcoeffs = numpy.polyfit(x, y, deg)
- cons = numpy.zeros((deg + 1, 3), numpy.float64)
- return pcoeffs, cons
-
-
-def estimate_quadratic_poly(x, y):
- """Estimate quadratic polynomial coefficients.
- """
- return estimate_poly(x, y, deg=2)
-
-
-def estimate_cubic_poly(x, y):
- """Estimate cubic polynomial coefficients.
- """
- return estimate_poly(x, y, deg=3)
-
-
-def estimate_quartic_poly(x, y):
- """Estimate degree 4 polynomial coefficients.
- """
- return estimate_poly(x, y, deg=4)
-
-
-def estimate_quintic_poly(x, y):
- """Estimate degree 5 polynomial coefficients.
- """
- return estimate_poly(x, y, deg=5)
-
-
-def configure(**kw):
- """Update the CONFIG dict
- """
- # inspect **kw to find known keys, update them in CONFIG
- for key in CONFIG:
- if key in kw:
- CONFIG[key] = kw[key]
-
- return CONFIG
-
-
-THEORY = OrderedDict(
- (('No Background',
- FitTheory(
- description="No background function",
- function=lambda x, y0: numpy.zeros_like(x),
- parameters=[],
- is_background=True)),
- ('Constant',
- FitTheory(
- description='Constant background',
- function=lambda x, y0, c: c * numpy.ones_like(x),
- parameters=['Constant', ],
- estimate=lambda x, y: ([min(y)], [[0, 0, 0]]),
- is_background=True)),
- ('Linear',
- FitTheory(
- description="Linear background, parameters 'Constant' and"
- " 'Slope'",
- function=lambda x, y0, a, b: a + b * x,
- parameters=['Constant', 'Slope'],
- estimate=estimate_linear,
- configure=configure,
- is_background=True)),
- ('Strip',
- FitTheory(
- description="Compute background using a strip filter\n"
- "Parameters 'StripWidth', 'StripIterations'",
- function=strip_bg,
- parameters=['StripWidth', 'StripIterations'],
- estimate=estimate_strip,
- configure=configure,
- is_background=True)),
- ('Snip',
- FitTheory(
- description="Compute background using a snip filter\n"
- "Parameter 'SnipWidth'",
- function=snip_bg,
- parameters=['SnipWidth'],
- estimate=estimate_snip,
- configure=configure,
- is_background=True)),
- ('Degree 2 Polynomial',
- FitTheory(
- description="Quadratic polynomial background, Parameters "
- "'a', 'b' and 'c'\ny = a*x^2 + b*x +c",
- function=poly,
- parameters=['a', 'b', 'c'],
- estimate=estimate_quadratic_poly,
- configure=configure,
- is_background=True)),
- ('Degree 3 Polynomial',
- FitTheory(
- description="Cubic polynomial background, Parameters "
- "'a', 'b', 'c' and 'd'\n"
- "y = a*x^3 + b*x^2 + c*x + d",
- function=poly,
- parameters=['a', 'b', 'c', 'd'],
- estimate=estimate_cubic_poly,
- configure=configure,
- is_background=True)),
- ('Degree 4 Polynomial',
- FitTheory(
- description="Quartic polynomial background\n"
- "y = a*x^4 + b*x^3 + c*x^2 + d*x + e",
- function=poly,
- parameters=['a', 'b', 'c', 'd', 'e'],
- estimate=estimate_quartic_poly,
- configure=configure,
- is_background=True)),
- ('Degree 5 Polynomial',
- FitTheory(
- description="Quaintic polynomial background\n"
- "y = a*x^5 + b*x^4 + c*x^3 + d*x^2 + e*x + f",
- function=poly,
- parameters=['a', 'b', 'c', 'd', 'e', 'f'],
- estimate=estimate_quintic_poly,
- configure=configure,
- is_background=True))))
diff --git a/silx/math/fit/filters.pyx b/silx/math/fit/filters.pyx
deleted file mode 100644
index da1f6f5..0000000
--- a/silx/math/fit/filters.pyx
+++ /dev/null
@@ -1,416 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-"""This module provides background extraction functions and smoothing
-functions. These functions are extracted from PyMca module SpecFitFuns.
-
-Index of background extraction functions:
-------------------------------------------
-
- - :func:`strip`
- - :func:`snip1d`
- - :func:`snip2d`
- - :func:`snip3d`
-
-Smoothing functions:
---------------------
-
- - :func:`savitsky_golay`
- - :func:`smooth1d`
- - :func:`smooth2d`
- - :func:`smooth3d`
-
-References:
------------
-
-.. [Morhac97] Miroslav Morháč et al.
- Background elimination methods for multidimensional coincidence γ-ray spectra.
- Nucl. Instruments and Methods in Physics Research A401 (1997) 113-132.
- https://doi.org/10.1016/S0168-9002(97)01023-1
-
-.. [Ryan88] C.G. Ryan et al.
- SNIP, a statistics-sensitive background treatment for the quantitative analysis of PIXE spectra in geoscience applications.
- Nucl. Instruments and Methods in Physics Research B34 (1988) 396-402*.
- https://doi.org/10.1016/0168-583X(88)90063-8
-
-API documentation:
--------------------
-
-"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "15/05/2017"
-
-import logging
-import numpy
-
-_logger = logging.getLogger(__name__)
-
-cimport cython
-cimport silx.math.fit.filters_wrapper as filters_wrapper
-
-
-def strip(data, w=1, niterations=1000, factor=1.0, anchors=None):
- """Extract background from data using the strip algorithm, as explained at
- http://pymca.sourceforge.net/stripbackground.html.
-
- In its simplest implementation it is just as an iterative procedure
- depending on two parameters. These parameters are the strip background
- width ``w``, and the number of iterations. At each iteration, if the
- contents of channel ``i``, ``y(i)``, is above the average of the contents
- of the channels at ``w`` channels of distance, ``y(i-w)`` and
- ``y(i+w)``, ``y(i)`` is replaced by the average.
- At the end of the process we are left with something that resembles a spectrum
- in which the peaks have been stripped.
-
- :param data: Data array
- :type data: numpy.ndarray
- :param w: Strip width
- :param niterations: number of iterations
- :param factor: scaling factor applied to the average of ``y(i-w)`` and
- ``y(i+w)`` before comparing to ``y(i)``
- :param anchors: Array of anchors, indices of points that will not be
- modified during the stripping procedure.
- :return: Data with peaks stripped away
- """
- cdef:
- double[::1] input_c
- double[::1] output
- long[::1] anchors_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__"):
- raise TypeError("data must be a sequence (list, tuple) " +
- "or a numpy array")
- data_shape = (len(data), )
- else:
- data_shape = data.shape
-
- input_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- output = numpy.empty(shape=(input_c.size,),
- dtype=numpy.float64)
-
- if anchors is not None and len(anchors):
- # numpy.int_ is the same as C long (http://docs.scipy.org/doc/numpy/user/basics.types.html)
- anchors_c = numpy.array(anchors,
- copy=False,
- dtype=numpy.int_,
- order='C')
- len_anchors = anchors_c.size
- else:
- # Make a dummy length-1 array, because if I use shape=(0,) I get the error
- # IndexError: Out of bounds on buffer access (axis 0)
- anchors_c = numpy.empty(shape=(1,),
- dtype=numpy.int_)
- len_anchors = 0
-
-
- status = filters_wrapper.strip(&input_c[0], input_c.size,
- factor, niterations, w,
- &anchors_c[0], len_anchors, &output[0])
-
- return numpy.asarray(output).reshape(data_shape)
-
-
-def snip1d(data, snip_width):
- """Estimate the baseline (background) of a 1D data vector by clipping peaks.
-
- Implementation of the algorithm SNIP in 1D is described in [Morhac97]_.
- The original idea for 1D and the low-statistics-digital-filter (lsdf) comes
- from [Ryan88]_.
-
- :param data: Data array, preferably 1D and of type *numpy.float64*.
- Else, the data array will be flattened and converted to
- *dtype=numpy.float64* prior to applying the snip filter.
- :type data: numpy.ndarray
- :param snip_width: Width of the snip operator, in number of samples.
- A sample will be iteratively compared to it's neighbors up to a
- distance of ``snip_width`` samples. This parameters has a direct
- influence on the speed of the algorithm.
- :type width: int
- :return: Baseline of the input array, as an array of the same shape.
- :rtype: numpy.ndarray
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__"):
- raise TypeError("data must be a sequence (list, tuple) " +
- "or a numpy array")
- data_shape = (len(data), )
- else:
- data_shape = data.shape
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.snip1d(&data_c[0], data_c.size, snip_width)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def snip2d(data, snip_width):
- """Estimate the baseline (background) of a 2D data signal by clipping peaks.
-
- Implementation of the algorithm SNIP in 2D described in [Morhac97]_.
-
- :param data: 2D array
- :type data: numpy.ndarray
- :param width: Width of the snip operator, in number of samples. A wider
- snip operator will result in a smoother result (lower frequency peaks
- will be clipped), and a longer computation time.
- :type width: int
- :return: Baseline of the input array, as an array of the same shape.
- :rtype: numpy.ndarray
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__"):
- raise TypeError("data must be a 2D sequence (list, tuple) " +
- "or a 2D numpy array")
- nrows = len(data)
- ncolumns = len(data[0])
- data_shape = (len(data), len(data[0]))
-
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 2:
- ncolumns = data_shape[1]
- else:
- raise TypeError("data array must be 2-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.snip2d(&data_c[0], nrows, ncolumns, snip_width)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def snip3d(data, snip_width):
- """Estimate the baseline (background) of a 3D data signal by clipping peaks.
-
- Implementation of the algorithm SNIP in 3D described in [Morhac97]_.
-
- :param data: 3D array
- :type data: numpy.ndarray
- :param width: Width of the snip operator, in number of samples. A wider
- snip operator will result in a smoother result (lower frequency peaks
- will be clipped), and a longer computation time.
- :type width: int
-
- :return: Baseline of the input array, as an array of the same shape.
- :rtype: numpy.ndarray
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__") or\
- not hasattr(data[0][0], "__len__"):
- raise TypeError("data must be a 3D sequence (list, tuple) " +
- "or a 3D numpy array")
- nx = len(data)
- ny = len(data[0])
- nz = len(data[0][0])
- data_shape = (len(data), len(data[0]), len(data[0][0]))
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 3:
- nx = data_shape[0]
- ny = data_shape[1]
- nz = data_shape[2]
- else:
- raise TypeError("data array must be 3-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.snip3d(&data_c[0], nx, ny, nz, snip_width)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def savitsky_golay(data, npoints=5):
- """Smooth a curve using a Savitsky-Golay filter.
-
- :param data: Input data
- :type data: 1D numpy array
- :param npoints: Size of the smoothing operator in number of samples
- Must be between 3 and 100.
- :return: Smoothed data
- """
- cdef:
- double[::1] data_c
- double[::1] output
-
- data_c = numpy.array(data,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- output = numpy.empty(shape=(data_c.size,),
- dtype=numpy.float64)
-
- status = filters_wrapper.SavitskyGolay(&data_c[0], data_c.size,
- npoints, &output[0])
-
- if status:
- _logger.error("Smoothing failed. Check that npoints is greater " +
- "than 3 and smaller than 100.")
-
- return numpy.asarray(output).reshape(data.shape)
-
-
-def smooth1d(data):
- """Simple smoothing for 1D data.
-
- For a data array :math:`y` of length :math:`n`, the smoothed array
- :math:`ys` is calculated as a weighted average of neighboring samples:
-
- :math:`ys_0 = 0.75 y_0 + 0.25 y_1`
-
- :math:`ys_i = 0.25 (y_{i-1} + 2 y_i + y_{i+1})` for :math:`0 < i < n-1`
-
- :math:`ys_{n-1} = 0.25 y_{n-2} + 0.75 y_{n-1}`
-
-
- :param data: 1D data array
- :type data: numpy.ndarray
- :return: Smoothed data
- :rtype: numpy.ndarray(dtype=numpy.float64)
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__"):
- raise TypeError("data must be a sequence (list, tuple) " +
- "or a numpy array")
- data_shape = (len(data), )
- else:
- data_shape = data.shape
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.smooth1d(&data_c[0], data_c.size)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def smooth2d(data):
- """Simple smoothing for 2D data:
- :func:`smooth1d` is applied succesively along both axis
-
- :param data: 2D data array
- :type data: numpy.ndarray
- :return: Smoothed data
- :rtype: numpy.ndarray(dtype=numpy.float64)
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__"):
- raise TypeError("data must be a 2D sequence (list, tuple) " +
- "or a 2D numpy array")
- nrows = len(data)
- ncolumns = len(data[0])
- data_shape = (len(data), len(data[0]))
-
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 2:
- ncolumns = data_shape[1]
- else:
- raise TypeError("data array must be 2-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.smooth2d(&data_c[0], nrows, ncolumns)
-
- return numpy.asarray(data_c).reshape(data_shape)
-
-
-def smooth3d(data):
- """Simple smoothing for 3D data:
- :func:`smooth2d` is applied on each 2D slice of the data volume along all
- 3 axis
-
- :param data: 2D data array
- :type data: numpy.ndarray
- :return: Smoothed data
- :rtype: numpy.ndarray(dtype=numpy.float64)
- """
- cdef:
- double[::1] data_c
-
- if not isinstance(data, numpy.ndarray):
- if not hasattr(data, "__len__") or not hasattr(data[0], "__len__") or\
- not hasattr(data[0][0], "__len__"):
- raise TypeError("data must be a 3D sequence (list, tuple) " +
- "or a 3D numpy array")
- nx = len(data)
- ny = len(data[0])
- nz = len(data[0][0])
- data_shape = (len(data), len(data[0]), len(data[0][0]))
- else:
- data_shape = data.shape
- nrows = data_shape[0]
- if len(data_shape) == 3:
- nx = data_shape[0]
- ny = data_shape[1]
- nz = data_shape[2]
- else:
- raise TypeError("data array must be 3-dimensional")
-
- data_c = numpy.array(data,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
-
- filters_wrapper.smooth3d(&data_c[0], nx, ny, nz)
-
- return numpy.asarray(data_c).reshape(data_shape)
diff --git a/silx/math/fit/filters/include/filters.h b/silx/math/fit/filters/include/filters.h
deleted file mode 100644
index 1ee9a95..0000000
--- a/silx/math/fit/filters/include/filters.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#ifndef FITFILTERS_H
-#define FITFILTERS_H
-
-/* Background functions */
-void snip1d(double *data, int size, int width);
-//void snip1d_multiple(double *data, int n_channels, int snip_width, int n_spectra);
-void snip2d(double *data, int nrows, int ncolumns, int width);
-void snip3d(double *data, int nx, int ny, int nz, int width);
-
-int strip(double* input, long len_input, double c, long niter, int deltai,
- long* anchors, long len_anchors, double* output);
-
-/* Smoothing functions */
-
-int SavitskyGolay(double* input, long len_input, int npoints, double* output);
-
-void smooth1d(double *data, int size);
-void smooth2d(double *data, int size0, int size1);
-void smooth3d(double *data, int size0, int size1, int size2);
-
-
-#endif /* #define FITFILTERS_H */
diff --git a/silx/math/fit/filters/src/smoothnd.c b/silx/math/fit/filters/src/smoothnd.c
deleted file mode 100644
index cb96961..0000000
--- a/silx/math/fit/filters/src/smoothnd.c
+++ /dev/null
@@ -1,317 +0,0 @@
-#/*##########################################################################
-#
-# Copyright (c) 2004-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-
-#define MAX_SAVITSKY_GOLAY_WIDTH 101
-#define MIN_SAVITSKY_GOLAY_WIDTH 3
-
-/* Wrapped functions */
-void smooth1d(double *data, int size);
-void smooth2d(double *data, int size0, int size1);
-void smooth3d(double *data, int size0, int size1, int size2);
-int SavitskyGolay(double* input, long len_input, int npoints, double* output);
-
-/* Internal functions */
-long index2d(long row_idx, long col_idx, long ncols);
-long index3d(long x_idx, long y_idx, long z_idx, long ny, long nz);
-void smooth1d_rows(double *data, long nrows, long ncols);
-void smooth1d_cols(double *data, long nrows, long ncols);
-void smooth1d_x(double *data, long nx, long ny, long nz);
-void smooth1d_y(double *data, long nx, long ny, long nz);
-void smooth1d_z(double *data, long nx, long ny, long nz);
-void smooth2d_yzslice(double *data, long nx, long ny, long nz);
-void smooth2d_xzslice(double *data, long nx, long ny, long nz);
-void smooth2d_xyslice(double *data, long nx, long ny, long nz);
-
-
-/* Simple smoothing of a 1D array */
-void smooth1d(double *data, int size)
-{
- long i;
- double prev_sample;
- double next_sample;
-
- if (size < 3)
- {
- return;
- }
- prev_sample = data[0];
- for (i=0; i<(size-1); i++)
- {
- next_sample = 0.25 * (prev_sample + 2 * data[i] + data[i+1]);
- prev_sample = data[i];
- data[i] = next_sample;
- }
- data[size-1] = 0.25 * prev_sample + 0.75 * data[size-1];
- return;
-}
-
-/* Smoothing of a 2D array*/
-void smooth2d(double *data, int nrows, int ncols)
-{
- /* smooth the first dimension (rows) */
- smooth1d_rows(data, nrows, ncols);
-
- /* smooth the 2nd dimension */
- smooth1d_cols(data, nrows, ncols);
-}
-
-/* Smoothing of a 3D array */
-void smooth3d(double *data, int nx, int ny, int nz)
-{
- smooth2d_xyslice(data, nx, ny, nz);
- smooth2d_xzslice(data, nx, ny, nz);
- smooth2d_yzslice(data, nx, ny, nz);
-}
-
-/* 1D Savitsky-Golay smoothing */
-int SavitskyGolay(double* input, long len_input, int npoints, double* output)
-{
-
- //double dpoints = 5.;
- double coeff[MAX_SAVITSKY_GOLAY_WIDTH];
- int i, j, m;
- double dhelp, den;
- double *data;
-
- memcpy(output, input, len_input * sizeof(double));
-
- if (!(npoints % 2)) npoints +=1;
-
- if((npoints < MIN_SAVITSKY_GOLAY_WIDTH) || (len_input < npoints) || \
- (npoints > MAX_SAVITSKY_GOLAY_WIDTH))
- {
- /* do not smooth data */
- return 1;
- }
-
- /* calculate the coefficients */
- m = (int) (npoints/2);
- den = (double) ((2*m-1) * (2*m+1) * (2*m + 3));
- for (i=0; i<= m; i++){
- coeff[m+i] = (double) (3 * (3*m*m + 3*m - 1 - 5*i*i ));
- coeff[m-i] = coeff[m+i];
- }
-
- /* simple smoothing at the beginning */
- for (j=0; j<=(int)(npoints/3); j++)
- {
- smooth1d(output, m);
- }
-
- /* simple smoothing at the end */
- for (j=0; j<=(int)(npoints/3); j++)
- {
- smooth1d((output+len_input-m-1), m);
- }
-
- /*one does not need the whole spectrum buffer, but code is clearer */
- data = (double *) malloc(len_input * sizeof(double));
- memcpy(data, output, len_input * sizeof(double));
-
- /* the actual SG smoothing in the middle */
- for (i=m; i<(len_input-m); i++){
- dhelp = 0;
- for (j=-m;j<=m;j++) {
- dhelp += coeff[m+j] * (*(data+i+j));
- }
- if(dhelp > 0.0){
- *(output+i) = dhelp / den;
- }
- }
- free(data);
- return (0);
-}
-
-/*********************/
-/* Utility functions */
-/*********************/
-
-long index2d(long row_idx, long col_idx, long ncols)
-{
- return (row_idx*ncols+col_idx);
-}
-
-/* Apply smooth 1d on all rows in a 2D array*/
-void smooth1d_rows(double *data, long nrows, long ncols)
-{
- long row_idx;
-
- for (row_idx=0; row_idx < nrows; row_idx++)
- {
- smooth1d(&data[row_idx * ncols], ncols);
- }
-}
-
-/* Apply smooth 1d on all columns in a 2D array*/
-void smooth1d_cols(double *data, long nrows, long ncols)
-{
- long row_idx, col_idx;
- long this_idx2d, next_idx2d;
- double prev_sample;
- double next_sample;
-
- for (col_idx=0; col_idx < ncols; col_idx++)
- {
- prev_sample = data[index2d(0, col_idx, ncols)];
- for (row_idx=0; row_idx<(nrows-1); row_idx++)
- {
- this_idx2d = index2d(row_idx, col_idx, ncols);
- next_idx2d = index2d(row_idx+1, col_idx, ncols);
-
- next_sample = 0.25 * (prev_sample + \
- 2 * data[this_idx2d] + \
- data[next_idx2d]);
- prev_sample = data[this_idx2d];
- data[this_idx2d] = next_sample;
- }
-
- this_idx2d = index2d(nrows-1, col_idx, ncols);
- data[this_idx2d] = 0.25 * prev_sample + 0.75 * data[this_idx2d];
- }
-}
-
-long index3d(long x_idx, long y_idx, long z_idx, long ny, long nz)
-{
- return ((x_idx*ny + y_idx) * nz + z_idx);
-}
-
-/* Apply smooth 1d along first dimension in a 3D array*/
-void smooth1d_x(double *data, long nx, long ny, long nz)
-{
- long x_idx, y_idx, z_idx;
- long this_idx3d, next_idx3d;
- double prev_sample;
- double next_sample;
-
- for (y_idx=0; y_idx < ny; y_idx++)
- {
- for (z_idx=0; z_idx < nz; z_idx++)
- {
- prev_sample = data[index3d(0, y_idx, z_idx, ny, nz)];
- for (x_idx=0; x_idx<(nx-1); x_idx++)
- {
- this_idx3d = index3d(x_idx, y_idx, z_idx, ny, nz);
- next_idx3d = index3d(x_idx+1, y_idx, z_idx, ny, nz);
-
- next_sample = 0.25 * (prev_sample + \
- 2 * data[this_idx3d] + \
- data[next_idx3d]);
- prev_sample = data[this_idx3d];
- data[this_idx3d] = next_sample;
- }
-
- this_idx3d = index3d(nx-1, y_idx, z_idx, ny, nz);
- data[this_idx3d] = 0.25 * prev_sample + 0.75 * data[this_idx3d];
- }
- }
-}
-
-/* Apply smooth 1d along second dimension in a 3D array*/
-void smooth1d_y(double *data, long nx, long ny, long nz)
-{
- long x_idx, y_idx, z_idx;
- long this_idx3d, next_idx3d;
- double prev_sample;
- double next_sample;
-
- for (x_idx=0; x_idx < nx; x_idx++)
- {
- for (z_idx=0; z_idx < nz; z_idx++)
- {
- prev_sample = data[index3d(x_idx, 0, z_idx, ny, nz)];
- for (y_idx=0; y_idx<(ny-1); y_idx++)
- {
- this_idx3d = index3d(x_idx, y_idx, z_idx, ny, nz);
- next_idx3d = index3d(x_idx, y_idx+1, z_idx, ny, nz);
-
- next_sample = 0.25 * (prev_sample + \
- 2 * data[this_idx3d] + \
- data[next_idx3d]);
- prev_sample = data[this_idx3d];
- data[this_idx3d] = next_sample;
- }
-
- this_idx3d = index3d(x_idx, ny-1, z_idx, ny, nz);
- data[this_idx3d] = 0.25 * prev_sample + 0.75 * data[this_idx3d];
- }
- }
-}
-
-/* Apply smooth 1d along third dimension in a 3D array*/
-void smooth1d_z(double *data, long nx, long ny, long nz)
-{
- long x_idx, y_idx;
- long idx3d_first_sample;
-
- for (x_idx=0; x_idx < nx; x_idx++)
- {
- for (y_idx=0; y_idx < ny; y_idx++)
- {
- idx3d_first_sample = index3d(x_idx, y_idx, 0, ny, nz);
- /*We can use regular 1D smoothing function because z samples
- are contiguous in memory*/
- smooth1d(&data[idx3d_first_sample], nz);
- }
- }
-}
-
-/* 2D smoothing of a YZ slice in a 3D volume*/
-void smooth2d_yzslice(double *data, long nx, long ny, long nz)
-{
- long x_idx;
- long slice_size = ny * nz;
-
- /* a YZ slice is a "normal" 2D array of memory-contiguous data*/
- for (x_idx=0; x_idx < nx; x_idx++)
- {
- smooth2d(&data[x_idx*slice_size], ny, nz);
- }
-}
-
-/* 2D smoothing of a XZ slice in a 3D volume*/
-void smooth2d_xzslice(double *data, long nx, long ny, long nz)
-{
-
- /* smooth along the first dimension */
- smooth1d_x(data, nx, ny, nz);
-
- /* smooth along the third dimension */
- smooth1d_z(data, nx, ny, nz);
-}
-
-/* 2D smoothing of a XY slice in a 3D volume*/
-void smooth2d_xyslice(double *data, long nx, long ny, long nz)
-{
- /* smooth along the first dimension */
- smooth1d_x(data, nx, ny, nz);
-
- /* smooth along the second dimension */
- smooth1d_y(data, nx, ny, nz);
-}
-
diff --git a/silx/math/fit/filters/src/snip1d.c b/silx/math/fit/filters/src/snip1d.c
deleted file mode 100644
index 994a272..0000000
--- a/silx/math/fit/filters/src/snip1d.c
+++ /dev/null
@@ -1,149 +0,0 @@
-#/*##########################################################################
-# Copyright (c) 2004-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-/*
- Implementation of the algorithm SNIP in 1D described in
- Miroslav Morhac et al. Nucl. Instruments and Methods in Physics Research A401 (1997) 113-132.
-
- The original idea for 1D and the low-statistics-digital-filter (lsdf) come from
- C.G. Ryan et al. Nucl. Instruments and Methods in Physics Research B34 (1988) 396-402.
-*/
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-
-void lls(double *data, int size);
-void lls_inv(double *data, int size);
-void snip1d(double *data, int n_channels, int snip_width);
-void snip1d_multiple(double *data, int n_channels, int snip_width, int n_spectra);
-void lsdf(double *data, int size, int fwhm, double f, double A, double M, double ratio);
-
-void lls(double *data, int size)
-{
- int i;
- for (i=0; i< size; i++)
- {
- data[i] = log(log(sqrt(data[i]+1.0)+1.0)+1.0);
- }
-}
-
-void lls_inv(double *data, int size)
-{
- int i;
- double tmp;
- for (i=0; i< size; i++)
- {
- /* slightly different than the published formula because
- with the original formula:
-
- tmp = exp(exp(data[i]-1.0)-1.0);
- data[i] = tmp * tmp - 1.0;
-
- one does not recover the original data */
-
- tmp = exp(exp(data[i])-1.0)-1.0;
- data[i] = tmp * tmp - 1.0;
- }
-}
-
-void lsdf(double *data, int size, int fwhm, double f, double A, double M, double ratio)
-{
- int channel, i, j;
- double L, R, S;
- int width;
- double dhelp;
-
- width = (int) (f * fwhm);
- for (channel=width; channel<(size-width); channel++)
- {
- i = width;
- while(i>0)
- {
- L=0;
- R=0;
- for(j=channel-i; j<channel; j++)
- {
- L += data[j];
- }
- for(j=channel+1; j<channel+i; j++)
- {
- R += data[j];
- }
- S = data[channel] + L + R;
- if (S<M)
- {
- data[channel] = S /(2*i+1);
- break;
- }
- dhelp = (R+1)/(L+1);
- if ((dhelp < ratio) && (dhelp > (1/ratio)))
- {
- if (S<(A*sqrt(data[channel])))
- {
- data[channel] = S /(2*i+1);
- break;
- }
- }
- i=i-1;
- }
- }
-}
-
-
-void snip1d(double *data, int n_channels, int snip_width)
-{
- snip1d_multiple(data, n_channels, snip_width, 1);
-}
-
-void snip1d_multiple(double *data, int n_channels, int snip_width, int n_spectra)
-{
- int i;
- int j;
- int p;
- int offset;
- double *w;
-
- i = (int) (0.5 * snip_width);
- /* lsdf(data, size, i, 1.5, 75., 10., 1.3); */
-
- w = (double *) malloc(n_channels * sizeof(double));
-
- for (j=0; j < n_spectra; j++)
- {
- offset = j * n_channels;
- for (p = snip_width; p > 0; p--)
- {
- for (i=p; i<(n_channels - p); i++)
- {
- w[i] = MIN(data[i + offset], 0.5*(data[i + offset - p] + data[ i + offset + p]));
- }
- for (i=p; i<(n_channels - p); i++)
- {
- data[i+offset] = w[i];
- }
- }
- }
- free(w);
-}
diff --git a/silx/math/fit/filters/src/snip2d.c b/silx/math/fit/filters/src/snip2d.c
deleted file mode 100644
index 235759c..0000000
--- a/silx/math/fit/filters/src/snip2d.c
+++ /dev/null
@@ -1,96 +0,0 @@
-#/*##########################################################################
-#
-# The PyMca X-Ray Fluorescence Toolkit
-#
-# Copyright (c) 2004-2014 European Synchrotron Radiation Facility
-#
-# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
-# the ESRF by the Software group.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-/*
- Implementation of the algorithm SNIP in 2D described in
- Miroslav Morhac et al. Nucl. Instruments and Methods in Physics Research A401 (1997) 113-132.
-*/
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-
-void lls(double *data, int size);
-void lls_inv(double *data, int size);
-
-void snip2d(double *data, int nrows, int ncolumns, int width)
-{
- int i, j;
- int p;
- int size;
- double *w;
- double P1, P2, P3, P4;
- double S1, S2, S3, S4;
- double dhelp;
- int iminuspxncolumns; /* (i-p) * ncolumns */
- int ixncolumns; /* i * ncolumns */
- int ipluspxncolumns; /* (i+p) * ncolumns */
-
- size = nrows * ncolumns;
- w = (double *) malloc(size * sizeof(double));
-
- for (p=width; p > 0; p--)
- {
- for (i=p; i<(nrows-p); i++)
- {
- iminuspxncolumns = (i-p) * ncolumns;
- ixncolumns = i * ncolumns;
- ipluspxncolumns = (i+p) * ncolumns;
- for (j=p; j<(ncolumns-p); j++)
- {
- P4 = data[ iminuspxncolumns + (j-p)]; /* P4 = data[i-p][j-p] */
- S4 = data[ iminuspxncolumns + j]; /* S4 = data[i-p][j] */
- P2 = data[ iminuspxncolumns + (j+p)]; /* P2 = data[i-p][j+p] */
- S3 = data[ ixncolumns + (j-p)]; /* S3 = data[i][j-p] */
- S2 = data[ ixncolumns + (j+p)]; /* S2 = data[i][j+p] */
- P3 = data[ ipluspxncolumns + (j-p)]; /* P3 = data[i+p][j-p] */
- S1 = data[ ipluspxncolumns + j]; /* S1 = data[i+p][j] */
- P1 = data[ ipluspxncolumns + (j+p)]; /* P1 = data[i+p][j+p] */
- dhelp = 0.5*(P1+P3);
- S1 = MAX(S1, dhelp) - dhelp;
- dhelp = 0.5*(P1+P2);
- S2 = MAX(S2, dhelp) - dhelp;
- dhelp = 0.5*(P3+P4);
- S3 = MAX(S3, dhelp) - dhelp;
- dhelp = 0.5*(P2+P4);
- S4 = MAX(S4, dhelp) - dhelp;
- w[ixncolumns + j] = MIN(data[ixncolumns + j], 0.5 * (S1+S2+S3+S4) + 0.25 * (P1+P2+P3+P4));
- }
- }
- for (i=p; i<(nrows-p); i++)
- {
- ixncolumns = i * ncolumns;
- for (j=p; j<(ncolumns-p); j++)
- {
- data[ixncolumns + j] = w[ixncolumns + j];
- }
- }
- }
- free(w);
-}
diff --git a/silx/math/fit/filters/src/snip3d.c b/silx/math/fit/filters/src/snip3d.c
deleted file mode 100644
index cf48ee4..0000000
--- a/silx/math/fit/filters/src/snip3d.c
+++ /dev/null
@@ -1,186 +0,0 @@
-#/*##########################################################################
-#
-# The PyMca X-Ray Fluorescence Toolkit
-#
-# Copyright (c) 2004-2014 European Synchrotron Radiation Facility
-#
-# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
-# the ESRF by the Software group.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-/*
- Implementation of the algorithm SNIP in 3D described in
- Miroslav Morhac et al. Nucl. Instruments and Methods in Physics Research A401 (1997) 113-132.
-*/
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-
-void lls(double *data, int size);
-void lls_inv(double *data, int size);
-
-void snip3d(double *data, int nx, int ny, int nz, int width)
-{
- int i, j, k;
- int p;
- int size;
- double *w;
- double P1, P2, P3, P4, P5, P6, P7, P8;
- double R1, R2, R3, R4, R5, R6;
- double S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12;
- double dhelp;
- long ioffset;
- long iplus;
- long imin;
- long joffset;
- long jplus;
- long jmin;
-
- size = nx * ny * nz;
- w = (double *) malloc(size * sizeof(double));
-
- for (p=width; p > 0; p--)
- {
- for (i=p; i<(nx-p); i++)
- {
- ioffset = i * ny * nz;
- iplus = (i + p) * ny * nz;
- imin = (i - p) * ny * nz;
- for (j=p; j<(ny-p); j++)
- {
- joffset = j * nz;
- jplus = (j + p) * nz;
- jmin = (j - p) * nz;
- for (k=p; k<(nz-p); k++)
- {
- P1 = data[iplus + jplus + k-p]; /* P1 = data[i+p][j+p][k-p] */
- P2 = data[imin + jplus + k-p]; /* P2 = data[i-p][j+p][k-p] */
- P3 = data[iplus + jmin + k-p]; /* P3 = data[i+p][j-p][k-p] */
- P4 = data[imin + jmin + k-p]; /* P4 = data[i-p][j-p][k-p] */
- P5 = data[iplus + jplus + k+p]; /* P5 = data[i+p][j+p][k+p] */
- P6 = data[imin + jplus + k+p]; /* P6 = data[i-p][j+p][k+p] */
- P7 = data[imin + jmin + k+p]; /* P7 = data[i-p][j-p][k+p] */
- P8 = data[iplus + jmin + k+p]; /* P8 = data[i+p][j-p][k+p] */
-
- S1 = data[iplus + joffset + k-p]; /* S1 = data[i+p][j][k-p] */
- S2 = data[ioffset + jmin + k-p]; /* S2 = data[i][j+p][k-p] */
- S3 = data[imin + joffset + k-p]; /* S3 = data[i-p][j][k-p] */
- S4 = data[ioffset + jmin + k-p]; /* S4 = data[i][j-p][k-p] */
- S5 = data[imin + joffset + k+p]; /* S5 = data[i-p][j][k+p] */
- S6 = data[ioffset + jplus + k+p]; /* S6 = data[i][j+p][k+p] */
- S7 = data[imin + joffset + k+p]; /* S7 = data[i-p][j][k+p] */
- S8 = data[ioffset + jmin + k+p]; /* S8 = data[i][j-p][k+p] */
- S9 = data[imin + jplus + k]; /* S9 = data[i-p][j+p][k] */
- S10 = data[imin + jmin + k]; /* S10 = data[i-p][j-p][k] */
- S11 = data[iplus + jmin + k]; /* S11 = data[i+p][j-p][k] */
- S12 = data[iplus + jplus + k]; /* S12 = data[i+p][j+p][k] */
-
- R1 = data[ioffset + joffset + k-p]; /* R1 = data[i][j][k-p] */
- R2 = data[ioffset + joffset + k+p]; /* R2 = data[i][j][k+p] */
- R3 = data[imin + joffset + k]; /* R3 = data[i-p][j][k] */
- R4 = data[iplus + joffset + k]; /* R4 = data[i+p][j][k] */
- R5 = data[ioffset + jplus + k]; /* R5 = data[i][j+p][k] */
- R6 = data[ioffset + jmin + k]; /* R6 = data[i][j-p][k] */
-
- dhelp = 0.5*(P1+P3);
- S1 = MAX(S1, dhelp) - dhelp;
-
- dhelp = 0.5*(P1+P2);
- S2 = MAX(S2, dhelp) - dhelp;
-
- dhelp = 0.5*(P2+P4);
- S3 = MAX(S3, dhelp) - dhelp;
-
- dhelp = 0.5*(P3+P4);
- S4 = MAX(S4, dhelp) - dhelp;
-
- dhelp = 0.5*(P5+P8); /* Different from paper (P5+P7) but according to drawing */
- S5 = MAX(S5, dhelp) - dhelp;
-
- dhelp = 0.5*(P5+P6);
- S6 = MAX(S6, dhelp) - dhelp;
-
- dhelp = 0.5*(P6+P7); /* Different from paper (P6+P8) but according to drawing */
- S7 = MAX(S7, dhelp) - dhelp;
-
- dhelp = 0.5*(P7+P8);
- S8 = MAX(S8, dhelp) - dhelp;
-
- dhelp = 0.5*(P2+P6);
- S9 = MAX(S9, dhelp) - dhelp;
-
- dhelp = 0.5*(P4+P7); /* Different from paper (P4+P8) but according to drawing */
- S10 = MAX(S10, dhelp) - dhelp;
-
- dhelp = 0.5*(P3+P8); /* Different from paper (P1+P5) but according to drawing */
- S11 = MAX(S11, dhelp) - dhelp;
-
- dhelp = 0.5*(P1+P5); /* Different from paper (P3+P7) but according to drawing */
- S12 = MAX(S12, dhelp) - dhelp;
-
- /* The published formulae correspond to have:
- P7 and P8 interchanged, and S11 and S12 interchanged
- with respect to the published drawing */
-
- dhelp = 0.5 * (S1+S2+S3+S4) + 0.25 * (P1+P2+P3+P4);
- R1 = MAX(R1, dhelp) - dhelp;
-
- dhelp = 0.5 * (S5+S6+S7+S8) + 0.25 * (P5+P6+P7+P8);
- R2 = MAX(R2, dhelp) - dhelp;
-
- dhelp = 0.5 * (S3+S7+S9+S10) + 0.25 * (P2+P4+P6+P7); /* Again same P7 and P8 change */
- R3 = MAX(R3, dhelp) - dhelp;
-
- dhelp = 0.5 * (S1+S5+S11+S12) + 0.25 * (P1+P3+P5+P8); /* Again same P7 and P8 change */
- R4 = MAX(R4, dhelp) - dhelp;
-
- dhelp = 0.5 * (S2+S6+S9+S12) + 0.25 * (P1+P2+P5+P6); /* Again same S11 and S12 change */
- R5 = MAX(R5, dhelp) - dhelp;
-
- dhelp = 0.5 * (S4+S8+S10+S11) + 0.25 * (P3+P4+P7+P8); /* Again same S11 and S12 change */
- R6 = MAX(R6, dhelp) - dhelp;
-
- dhelp = 0.5 * (R1 + R2 + R3 + R4 + R5 + R6) +\
- 0.25 * (S1 + S2 + S3 + S4 + S5 + S6) +\
- 0.25 * (S7 + S8 + S9 + S10 + S11 + S12) +\
- 0.125 * (P1 + P2 + P3 + P4 + P5 + P6 + P7 + P8);
- w[ioffset + joffset + k] = MIN(data[ioffset + joffset + k], dhelp);
- }
- }
- }
- for (i=p; i<(nx-p); i++)
- {
- ioffset = i * ny * nz;
- for (j=p; j<(ny-p); j++)
- {
- joffset = j * nz;
- for (k=p; k<(nz-p); j++)
- {
- data[ioffset + joffset + k] = w[ioffset + joffset + k];
- }
- }
- }
- }
- free(w);
-}
diff --git a/silx/math/fit/filters/src/strip.c b/silx/math/fit/filters/src/strip.c
deleted file mode 100644
index dec0742..0000000
--- a/silx/math/fit/filters/src/strip.c
+++ /dev/null
@@ -1,118 +0,0 @@
-#/*##########################################################################
-# Copyright (c) 2004-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-/*
- This file provides a background strip function, to isolate low frequency
- background signal from a spectrum (and later substact it from the signal
- to be left only with the peaks to be fitted).
-
- It is adapted from PyMca source file "SpecFitFuns.c". The main difference
- with the original code is that this code does not handle the python
- wrapping, which is done elsewhere using cython.
-
- Authors: V.A. Sole, P. Knobel
- License: MIT
- Last modified: 17/06/2016
-*/
-
-#include <string.h>
-
-#include <stdio.h>
-
-/* strip(double* input, double c, long niter, double* output)
-
- The strip background is probably PyMca's most popular background model.
-
- In its simplest implementation it is just as an iterative procedure depending
- on two parameters. These parameters are the strip background width w, and the
- strip background number of iterations. At each iteration, if the contents of
- channel i, y(i), is above the average of the contents of the channels at w
- channels of distance, y(i-w) and y(i+w), y(i) is replaced by the average.
- At the end of the process we are left with something that resembles a spectrum
- in which the peaks have been "stripped".
-
- Parameters:
-
- - input: Input data array
- - c: scaling factor applied to the average of y(i-w) and y(i+w) before
- comparing to y(i)
- - niter: number of iterations
- - deltai: operator width (in number of channels)
- - anchors: Array of anchors, indices of points that will not be
- modified during the stripping procedure.
- - output: output array
-
-*/
-int strip(double* input, long len_input,
- double c, long niter, int deltai,
- long* anchors, long len_anchors,
- double* output)
-{
- long iter_index, array_index, anchor_index, anchor;
- int anchor_nearby_flag;
- double t_mean;
-
- memcpy(output, input, len_input * sizeof(double));
-
- if (deltai <=0) deltai = 1;
-
- if (len_input < (2*deltai+1)) return(-1);
-
- if (len_anchors > 0) {
- for (iter_index = 0; iter_index < niter; iter_index++) {
- for (array_index = deltai; array_index < len_input - deltai; array_index++) {
- /* if index is within +- deltai of an anchor, don't do anything */
- anchor_nearby_flag = 0;
- for (anchor_index=0; anchor_index<len_anchors; anchor_index++)
- {
- anchor = anchors[anchor_index];
- if (array_index > (anchor - deltai) && array_index < (anchor + deltai))
- {
- anchor_nearby_flag = 1;
- break;
- }
- }
- /* skip this array_index index */
- if (anchor_nearby_flag) {
- continue;
- }
-
- t_mean = 0.5 * (input[array_index-deltai] + input[array_index+deltai]);
- if (input[array_index] > (t_mean * c))
- output[array_index] = t_mean;
- }
- memcpy(input, output, len_input * sizeof(double));
- }
- }
- else {
- for (iter_index = 0; iter_index < niter; iter_index++) {
- for (array_index=deltai; array_index < len_input - deltai; array_index++) {
- t_mean = 0.5 * (input[array_index-deltai] + input[array_index+deltai]);
-
- if (input[array_index] > (t_mean * c))
- output[array_index] = t_mean;
- }
- memcpy(input, output, len_input * sizeof(double));
- }
- }
- return(0);
-}
diff --git a/silx/math/fit/filters_wrapper.pxd b/silx/math/fit/filters_wrapper.pxd
deleted file mode 100644
index e4f7c72..0000000
--- a/silx/math/fit/filters_wrapper.pxd
+++ /dev/null
@@ -1,71 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "22/06/2016"
-
-cimport cython
-
-cdef extern from "filters.h":
- void snip1d(double *data,
- int size,
- int width)
-
- void snip2d(double *data,
- int nrows,
- int ncolumns,
- int width)
-
- void snip3d(double *data,
- int nx,
- int ny,
- int nz,
- int width)
-
- int strip(double* input,
- long len_input,
- double c,
- long niter,
- int deltai,
- long* anchors,
- long len_anchors,
- double* output)
-
- int SavitskyGolay(double* input,
- long len_input,
- int npoints,
- double* output)
-
- void smooth1d(double *data,
- int size)
-
- void smooth2d(double *data,
- int size0,
- int size1)
-
- void smooth3d(double *data,
- int size0,
- int size1,
- int size2)
diff --git a/silx/math/fit/fitmanager.py b/silx/math/fit/fitmanager.py
deleted file mode 100644
index b60e073..0000000
--- a/silx/math/fit/fitmanager.py
+++ /dev/null
@@ -1,1087 +0,0 @@
-# coding: utf-8
-# /*#########################################################################
-#
-# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ##########################################################################*/
-"""
-This module provides a tool to perform advanced fitting. The actual fit relies
-on :func:`silx.math.fit.leastsq`.
-
-This module deals with:
-
- - handling of the model functions (using a set of default functions or
- loading custom user functions)
- - handling of estimation function, that are used to determine the number
- of parameters to be fitted for functions with unknown number of
- parameters (such as the sum of a variable number of gaussian curves),
- and find reasonable initial parameters for input to the iterative
- fitting algorithm
- - handling of custom derivative functions that can be passed as a
- parameter to :func:`silx.math.fit.leastsq`
- - providing different background models
-
-"""
-from collections import OrderedDict
-import logging
-import numpy
-from numpy.linalg.linalg import LinAlgError
-import os
-import sys
-
-from .filters import strip, smooth1d
-from .leastsq import leastsq
-from .fittheory import FitTheory
-from . import bgtheories
-
-
-__authors__ = ["V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "16/01/2017"
-
-_logger = logging.getLogger(__name__)
-
-
-class FitManager(object):
- """
- Fit functions manager
-
- :param x: Abscissa data. If ``None``, :attr:`xdata` is set to
- ``numpy.array([0.0, 1.0, 2.0, ..., len(y)-1])``
- :type x: Sequence or numpy array or None
- :param y: The dependant data ``y = f(x)``. ``y`` must have the same
- shape as ``x`` if ``x`` is not ``None``.
- :type y: Sequence or numpy array or None
- :param sigmay: The uncertainties in the ``ydata`` array. These can be
- used as weights in the least-squares problem, if ``weight_flag``
- is ``True``.
- If ``None``, the uncertainties are assumed to be 1, unless
- ``weight_flag`` is ``True``, in which case the square-root
- of ``y`` is used.
- :type sigmay: Sequence or numpy array or None
- :param weight_flag: If this parameter is ``True`` and ``sigmay``
- uncertainties are not specified, the square root of ``y`` is used
- as weights in the least-squares problem. If ``False``, the
- uncertainties are set to 1.
- :type weight_flag: boolean
- """
- def __init__(self, x=None, y=None, sigmay=None, weight_flag=False):
- """
- """
- self.fitconfig = {
- 'WeightFlag': weight_flag,
- 'fitbkg': 'No Background',
- 'fittheory': None,
- # Next few parameters are defined for compatibility with legacy theories
- # which take the background as argument for their estimation function
- 'StripWidth': 2,
- 'StripIterations': 5000,
- 'StripThresholdFactor': 1.0,
- 'SmoothingFlag': False
- }
- """Dictionary of fit configuration parameters.
- These parameters can be modified using the :meth:`configure` method.
-
- Keys are:
-
- - 'fitbkg': name of the function used for fitting a low frequency
- background signal
- - 'FwhmPoints': default full width at half maximum value for the
- peaks'.
- - 'Sensitivity': Sensitivity parameter for the peak detection
- algorithm (:func:`silx.math.fit.peak_search`)
- """
-
- self.theories = OrderedDict()
- """Dictionary of fit theories, defining functions to be fitted
- to individual peaks.
-
- Keys are descriptive theory names (e.g "Gaussians" or "Step up").
- Values are :class:`silx.math.fit.fittheory.FitTheory` objects with
- the following attributes:
-
- - *"function"* is the fit function for an individual peak
- - *"parameters"* is a sequence of parameter names
- - *"estimate"* is the parameter estimation function
- - *"configure"* is the function returning the configuration dict
- for the theory in the format described in the :attr:` fitconfig`
- documentation
- - *"derivative"* (optional) is a custom derivative function, whose
- signature is described in the documentation of
- :func:`silx.math.fit.leastsq.leastsq`
- (``model_deriv(xdata, parameters, index)``).
- - *"description"* is a description string
- """
-
- self.selectedtheory = None
- """Name of currently selected theory. This name matches a key in
- :attr:`theories`."""
-
- self.bgtheories = OrderedDict()
- """Dictionary of background theories.
-
- See :attr:`theories` for documentation on theories.
- """
-
- # Load default theories (constant, linear, strip)
- self.loadbgtheories(bgtheories)
-
- self.selectedbg = 'No Background'
- """Name of currently selected background theory. This name must be
- an existing key in :attr:`bgtheories`."""
-
- self.fit_results = []
- """This list stores detailed information about all fit parameters.
- It is initialized in :meth:`estimate` and completed with final fit
- values in :meth:`runfit`.
-
- Each fit parameter is stored as a dictionary with following fields:
-
- - 'name': Parameter name.
- - 'estimation': Estimated value.
- - 'group': Group number. Group 0 corresponds to the background
- function parameters. Group ``n`` (for ``n>0``) corresponds to
- the fit function parameters for the n-th peak.
- - 'code': Constraint code
-
- - 0 - FREE
- - 1 - POSITIVE
- - 2 - QUOTED
- - 3 - FIXED
- - 4 - FACTOR
- - 5 - DELTA
- - 6 - SUM
-
- - 'cons1':
-
- - Ignored if 'code' is FREE, POSITIVE or FIXED.
- - Min value of the parameter if code is QUOTED
- - Index of fitted parameter to which 'cons2' is related
- if code is FACTOR, DELTA or SUM.
-
- - 'cons2':
-
- - Ignored if 'code' is FREE, POSITIVE or FIXED.
- - Max value of the parameter if QUOTED
- - Factor to apply to related parameter with index 'cons1' if
- 'code' is FACTOR
- - Difference with parameter with index 'cons1' if
- 'code' is DELTA
- - Sum obtained when adding parameter with index 'cons1' if
- 'code' is SUM
-
- - 'fitresult': Fitted value.
- - 'sigma': Standard deviation for the parameter estimate
- - 'xmin': Lower limit of the ``x`` data range on which the fit
- was performed
- - 'xmax': Upeer limit of the ``x`` data range on which the fit
- was performed
- """
-
- self.parameter_names = []
- """This list stores all fit parameter names: background function
- parameters and fit function parameters for every peak. It is filled
- in :meth:`estimate`.
-
- It is the responsibility of the estimate function defined in
- :attr:`theories` to determine how many parameters are needed,
- based on how many peaks are detected and how many parameters are needed
- to fit an individual peak.
- """
-
- self.setdata(x, y, sigmay)
-
- ##################
- # Public methods #
- ##################
- def addbackground(self, bgname, bgtheory):
- """Add a new background theory to dictionary :attr:`bgtheories`.
-
- :param bgname: String with the name describing the function
- :param bgtheory: :class:`FitTheory` object
- :type bgtheory: :class:`silx.math.fit.fittheory.FitTheory`
- """
- self.bgtheories[bgname] = bgtheory
-
- def addtheory(self, name, theory=None,
- function=None, parameters=None,
- estimate=None, configure=None, derivative=None,
- description=None, pymca_legacy=False):
- """Add a new theory to dictionary :attr:`theories`.
-
- You can pass a name and a :class:`FitTheory` object as arguments, or
- alternatively provide all arguments necessary to instantiate a new
- :class:`FitTheory` object.
-
- See :meth:`loadtheories` for more information on estimation functions,
- configuration functions and custom derivative functions.
-
- :param name: String with the name describing the function
- :param theory: :class:`FitTheory` object, defining a fit function and
- associated information (estimation function, description…).
- If this parameter is provided, all other parameters, except for
- ``name``, are ignored.
- :type theory: :class:`silx.math.fit.fittheory.FitTheory`
- :param callable function: Mandatory argument if ``theory`` is not provided.
- See documentation for :attr:`silx.math.fit.fittheory.FitTheory.function`.
- :param List[str] parameters: Mandatory argument if ``theory`` is not provided.
- See documentation for :attr:`silx.math.fit.fittheory.FitTheory.parameters`.
- :param callable estimate: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.estimate`
- :param callable configure: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.configure`
- :param callable derivative: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.derivative`
- :param str description: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.description`
- :param config_widget: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.config_widget`
- :param bool pymca_legacy: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.pymca_legacy`
- """
- if theory is not None:
- self.theories[name] = theory
-
- elif function is not None and parameters is not None:
- self.theories[name] = FitTheory(
- description=description,
- function=function,
- parameters=parameters,
- estimate=estimate,
- configure=configure,
- derivative=derivative,
- pymca_legacy=pymca_legacy
- )
-
- else:
- raise TypeError("You must supply a FitTheory object or define " +
- "a fit function and its parameters.")
-
- def addbgtheory(self, name, theory=None,
- function=None, parameters=None,
- estimate=None, configure=None,
- derivative=None, description=None):
- """Add a new theory to dictionary :attr:`bgtheories`.
-
- You can pass a name and a :class:`FitTheory` object as arguments, or
- alternatively provide all arguments necessary to instantiate a new
- :class:`FitTheory` object.
-
- :param name: String with the name describing the function
- :param theory: :class:`FitTheory` object, defining a fit function and
- associated information (estimation function, description…).
- If this parameter is provided, all other parameters, except for
- ``name``, are ignored.
- :type theory: :class:`silx.math.fit.fittheory.FitTheory`
- :param function function: Mandatory argument if ``theory`` is not provided.
- See documentation for :attr:`silx.math.fit.fittheory.FitTheory.function`.
- :param list[str] parameters: Mandatory argument if ``theory`` is not provided.
- See documentation for :attr:`silx.math.fit.fittheory.FitTheory.parameters`.
- :param function estimate: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.estimate`
- :param function configure: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.configure`
- :param function derivative: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.derivative`
- :param str description: See documentation for
- :attr:`silx.math.fit.fittheory.FitTheory.description`
- """
- if theory is not None:
- self.bgtheories[name] = theory
-
- elif function is not None and parameters is not None:
- self.bgtheories[name] = FitTheory(
- description=description,
- function=function,
- parameters=parameters,
- estimate=estimate,
- configure=configure,
- derivative=derivative,
- is_background=True
- )
-
- else:
- raise TypeError("You must supply a FitTheory object or define " +
- "a background function and its parameters.")
-
- def configure(self, **kw):
- """Configure the current theory by filling or updating the
- :attr:`fitconfig` dictionary.
- Call the custom configuration function, if any. This allows the user
- to modify the behavior of the custom fit function or the custom
- estimate function.
-
- This methods accepts only named parameters. All ``**kw`` parameters
- are expected to be fields of :attr:`fitconfig` to be updated, unless
- they have a special meaning for the custom configuration function
- of the currently selected theory..
-
- This method returns the modified config dictionary returned by the
- custom configuration function.
- """
- # inspect **kw to find known keys, update them in self.fitconfig
- for key in self.fitconfig:
- if key in kw:
- self.fitconfig[key] = kw[key]
-
- # initialize dict with existing config dict
- result = {}
- result.update(self.fitconfig)
-
- if "WeightFlag" in kw:
- if kw["WeightFlag"]:
- self.enableweight()
- else:
- self.disableweight()
-
- if self.selectedtheory is None:
- return result
-
- # Apply custom configuration function
- custom_config_fun = self.theories[self.selectedtheory].configure
- if custom_config_fun is not None:
- result.update(custom_config_fun(**kw))
-
- custom_bg_config_fun = self.bgtheories[self.selectedbg].configure
- if custom_bg_config_fun is not None:
- result.update(custom_bg_config_fun(**kw))
-
- # Update self.fitconfig with custom config
- for key in self.fitconfig:
- if key in result:
- self.fitconfig[key] = result[key]
-
- result.update(self.fitconfig)
- return result
-
- def estimate(self, callback=None):
- """
- Fill :attr:`fit_results` with an estimation of the fit parameters.
-
- At first, the background parameters are estimated, if a background
- model has been specified.
- Then, a custom estimation function related to the model function is
- called.
-
- This process determines the number of needed fit parameters and
- provides an initial estimation for them, to serve as an input for the
- actual iterative fitting performed in :meth:`runfit`.
-
- :param callback: Optional callback function, conforming to the
- signature ``callback(data)`` with ``data`` being a dictionary.
- This callback function is called before and after the estimation
- process, and is given a dictionary containing the values of
- :attr:`state` (``'Estimate in progress'`` or ``'Ready to Fit'``)
- and :attr:`chisq`.
- This is used for instance in :mod:`silx.gui.fit.FitWidget` to
- update a widget displaying a status message.
- :return: Estimated parameters
- """
- self.state = 'Estimate in progress'
- self.chisq = None
-
- if callback is not None:
- callback(data={'chisq': self.chisq,
- 'status': self.state})
-
- CONS = {0: 'FREE',
- 1: 'POSITIVE',
- 2: 'QUOTED',
- 3: 'FIXED',
- 4: 'FACTOR',
- 5: 'DELTA',
- 6: 'SUM',
- 7: 'IGNORE'}
-
- # Filter-out not finite data
- xwork = self.xdata[self._finite_mask]
- ywork = self.ydata[self._finite_mask]
-
- # estimate the background
- bg_params, bg_constraints = self.estimate_bkg(xwork, ywork)
-
- # estimate the function
- try:
- fun_params, fun_constraints = self.estimate_fun(xwork, ywork)
- except LinAlgError:
- self.state = 'Estimate failed'
- if callback is not None:
- callback(data={'status': self.state})
- raise
-
- # build the names
- self.parameter_names = []
-
- for bg_param_name in self.bgtheories[self.selectedbg].parameters:
- self.parameter_names.append(bg_param_name)
-
- fun_param_names = self.theories[self.selectedtheory].parameters
- param_index, peak_index = 0, 0
- while param_index < len(fun_params):
- peak_index += 1
- for fun_param_name in fun_param_names:
- self.parameter_names.append(fun_param_name + "%d" % peak_index)
- param_index += 1
-
- self.fit_results = []
- nb_fun_params_per_group = len(fun_param_names)
- group_number = 0
- xmin = min(xwork)
- xmax = max(xwork)
- nb_bg_params = len(bg_params)
- for (pindex, pname) in enumerate(self.parameter_names):
- # First come background parameters
- if pindex < nb_bg_params:
- estimation_value = bg_params[pindex]
- constraint_code = CONS[int(bg_constraints[pindex][0])]
- cons1 = bg_constraints[pindex][1]
- cons2 = bg_constraints[pindex][2]
- # then come peak function parameters
- else:
- fun_param_index = pindex - nb_bg_params
-
- # increment group_number for each new fitted peak
- if (fun_param_index % nb_fun_params_per_group) == 0:
- group_number += 1
-
- estimation_value = fun_params[fun_param_index]
- constraint_code = CONS[int(fun_constraints[fun_param_index][0])]
- # cons1 is the index of another fit parameter. In the global
- # fit_results, we must adjust the index to account for the bg
- # params added to the start of the list.
- cons1 = fun_constraints[fun_param_index][1]
- if constraint_code in ["FACTOR", "DELTA", "SUM"]:
- cons1 += nb_bg_params
- cons2 = fun_constraints[fun_param_index][2]
-
- self.fit_results.append({'name': pname,
- 'estimation': estimation_value,
- 'group': group_number,
- 'code': constraint_code,
- 'cons1': cons1,
- 'cons2': cons2,
- 'fitresult': 0.0,
- 'sigma': 0.0,
- 'xmin': xmin,
- 'xmax': xmax})
-
- self.state = 'Ready to Fit'
- self.chisq = None
- self.niter = 0
-
- if callback is not None:
- callback(data={'chisq': self.chisq,
- 'status': self.state})
- return numpy.append(bg_params, fun_params)
-
- def fit(self):
- """Convenience method to call :meth:`estimate` followed by :meth:`runfit`.
-
- :return: Output of :meth:`runfit`"""
- self.estimate()
- return self.runfit()
-
- def gendata(self, x=None, paramlist=None, estimated=False):
- """Return a data array using the currently selected fit function
- and the fitted parameters.
-
- :param x: Independent variable where the function is calculated.
- If ``None``, use :attr:`xdata`.
- :param paramlist: List of dictionaries, each dictionary item being a
- fit parameter. The dictionary's format is documented in
- :attr:`fit_results`.
- If ``None`` (default), use parameters from :attr:`fit_results`.
- :param estimated: If *True*, use estimated parameters.
- :return: :meth:`fitfunction` calculated for parameters whose code is
- not set to ``"IGNORE"``.
-
- This calculates :meth:`fitfunction` on `x` data using fit parameters
- from a list of parameter dictionaries, if field ``code`` is not set
- to ``"IGNORE"``.
- """
- x = self.xdata if x is None else numpy.array(x, copy=False)
-
- if paramlist is None:
- paramlist = self.fit_results
- active_params = []
- for param in paramlist:
- if param['code'] not in ['IGNORE', 7]:
- if not estimated:
- active_params.append(param['fitresult'])
- else:
- active_params.append(param['estimation'])
-
- # Mask x with not finite (support nD x)
- finite_mask = numpy.all(numpy.isfinite(x), axis=tuple(range(1, x.ndim)))
-
- if numpy.all(finite_mask): # All values are finite: fast path
- return self.fitfunction(numpy.array(x, copy=True), *active_params)
-
- else: # Only run fitfunction on finite data and complete result with NaNs
- # Create result with same number as elements as x, filling holes with NaNs
- result = numpy.full((x.shape[0],), numpy.nan, dtype=numpy.float64)
- result[finite_mask] = self.fitfunction(
- numpy.array(x[finite_mask], copy=True), *active_params)
- return result
-
- def get_estimation(self):
- """Return the list of fit parameter names."""
- if self.state not in ["Ready to fit", "Fit in progress", "Ready"]:
- _logger.warning("get_estimation() called before estimate() completed")
- return [param["estimation"] for param in self.fit_results]
-
- def get_names(self):
- """Return the list of fit parameter estimations."""
- if self.state not in ["Ready to fit", "Fit in progress", "Ready"]:
- msg = "get_names() called before estimate() completed, "
- msg += "names are not populated at this stage"
- _logger.warning(msg)
- return [param["name"] for param in self.fit_results]
-
- def get_fitted_parameters(self):
- """Return the list of fitted parameters."""
- if self.state not in ["Ready"]:
- msg = "get_fitted_parameters() called before runfit() completed, "
- msg += "results are not available a this stage"
- _logger.warning(msg)
- return [param["fitresult"] for param in self.fit_results]
-
- def loadtheories(self, theories):
- """Import user defined fit functions defined in an external Python
- source file, and save them in :attr:`theories`.
-
- An example of such a file can be found in the sources of
- :mod:`silx.math.fit.fittheories`. It must contain a
- dictionary named ``THEORY`` with the following structure::
-
- THEORY = {
- 'theory_name_1':
- FitTheory(description='Description of theory 1',
- function=fitfunction1,
- parameters=('param name 1', 'param name 2', …),
- estimate=estimation_function1,
- configure=configuration_function1,
- derivative=derivative_function1),
- 'theory_name_2':
- FitTheory(…),
- }
-
- See documentation of :mod:`silx.math.fit.fittheories` and
- :mod:`silx.math.fit.fittheory` for more
- information on designing your fit functions file.
-
- This method can also load user defined functions in the legacy
- format used in *PyMca*.
-
- :param theories: Name of python source file, or module containing the
- definition of fit functions.
- :raise: ImportError if theories cannot be imported
- """
- from types import ModuleType
- if isinstance(theories, ModuleType):
- theories_module = theories
- else:
- # if theories is not a module, it must be a string
- string_types = (basestring,) if sys.version_info[0] == 2 else (str,) # noqa
- if not isinstance(theories, string_types):
- raise ImportError("theory must be a python module, a module" +
- "name or a python filename")
- # if theories is a filename
- if os.path.isfile(theories):
- sys.path.append(os.path.dirname(theories))
- f = os.path.basename(os.path.splitext(theories)[0])
- theories_module = __import__(f)
- # if theories is a module name
- else:
- theories_module = __import__(theories)
-
- if hasattr(theories_module, "INIT"):
- theories.INIT()
-
- if not hasattr(theories_module, "THEORY"):
- msg = "File %s does not contain a THEORY dictionary" % theories
- raise ImportError(msg)
-
- elif isinstance(theories_module.THEORY, dict):
- # silx format for theory definition
- for theory_name, fittheory in list(theories_module.THEORY.items()):
- self.addtheory(theory_name, fittheory)
- else:
- self._load_legacy_theories(theories_module)
-
- def loadbgtheories(self, theories):
- """Import user defined background functions defined in an external Python
- module (source file), and save them in :attr:`theories`.
-
- An example of such a file can be found in the sources of
- :mod:`silx.math.fit.fittheories`. It must contain a
- dictionary named ``THEORY`` with the following structure::
-
- THEORY = {
- 'theory_name_1':
- FitTheory(description='Description of theory 1',
- function=fitfunction1,
- parameters=('param name 1', 'param name 2', …),
- estimate=estimation_function1,
- configure=configuration_function1,
- 'theory_name_2':
- FitTheory(…),
- }
-
- See documentation of :mod:`silx.math.fit.bgtheories` and
- :mod:`silx.math.fit.fittheory` for more
- information on designing your background functions file.
-
- :param theories: Module or name of python source file containing the
- definition of background functions.
- :raise: ImportError if theories cannot be imported
- """
- from types import ModuleType
- if isinstance(theories, ModuleType):
- theories_module = theories
- else:
- # if theories is not a module, it must be a string
- string_types = (basestring,) if sys.version_info[0] == 2 else (str,) # noqa
- if not isinstance(theories, string_types):
- raise ImportError("theory must be a python module, a module" +
- "name or a python filename")
- # if theories is a filename
- if os.path.isfile(theories):
- sys.path.append(os.path.dirname(theories))
- f = os.path.basename(os.path.splitext(theories)[0])
- theories_module = __import__(f)
- # if theories is a module name
- else:
- theories_module = __import__(theories)
-
- if hasattr(theories_module, "INIT"):
- theories.INIT()
-
- if not hasattr(theories_module, "THEORY"):
- msg = "File %s does not contain a THEORY dictionary" % theories
- raise ImportError(msg)
-
- elif isinstance(theories_module.THEORY, dict):
- # silx format for theory definition
- for theory_name, fittheory in list(theories_module.THEORY.items()):
- self.addbgtheory(theory_name, fittheory)
-
- def setbackground(self, theory):
- """Choose a background type from within :attr:`bgtheories`.
-
- This updates :attr:`selectedbg`.
-
- :param theory: The name of the background to be used.
- :raise: KeyError if ``theory`` is not a key of :attr:`bgtheories``.
- """
- if theory in self.bgtheories:
- self.selectedbg = theory
- else:
- msg = "No theory with name %s in bgtheories.\n" % theory
- msg += "Available theories: %s\n" % self.bgtheories.keys()
- raise KeyError(msg)
-
- # run configure to apply our fitconfig to the selected theory
- # through its custom config function
- self.configure(**self.fitconfig)
-
- def setdata(self, x, y, sigmay=None, xmin=None, xmax=None):
- """Set data attributes:
-
- - ``xdata0``, ``ydata0`` and ``sigmay0`` store the initial data
- and uncertainties. These attributes are not modified after
- initialization.
- - ``xdata``, ``ydata`` and ``sigmay`` store the data after
- removing values where ``xdata < xmin`` or ``xdata > xmax``.
- These attributes may be modified at a latter stage by filters.
-
- :param x: Abscissa data. If ``None``, :attr:`xdata`` is set to
- ``numpy.array([0.0, 1.0, 2.0, ..., len(y)-1])``
- :type x: Sequence or numpy array or None
- :param y: The dependant data ``y = f(x)``. ``y`` must have the same
- shape as ``x`` if ``x`` is not ``None``.
- :type y: Sequence or numpy array or None
- :param sigmay: The uncertainties in the ``ydata`` array. These are
- used as weights in the least-squares problem.
- If ``None``, the uncertainties are assumed to be 1.
- :type sigmay: Sequence or numpy array or None
- :param xmin: Lower value of x values to use for fitting
- :param xmax: Upper value of x values to use for fitting
- """
- if y is None:
- self.xdata0 = numpy.array([], numpy.float64)
- self.ydata0 = numpy.array([], numpy.float64)
- # self.sigmay0 = numpy.array([], numpy.float64)
- self.xdata = numpy.array([], numpy.float64)
- self.ydata = numpy.array([], numpy.float64)
- # self.sigmay = numpy.array([], numpy.float64)
-
- else:
- self.ydata0 = numpy.array(y)
- self.ydata = numpy.array(y)
- if x is None:
- self.xdata0 = numpy.arange(len(self.ydata0))
- self.xdata = numpy.arange(len(self.ydata0))
- else:
- self.xdata0 = numpy.array(x)
- self.xdata = numpy.array(x)
-
- # default weight
- if sigmay is None:
- self.sigmay0 = None
- self.sigmay = numpy.sqrt(self.ydata) if self.fitconfig["WeightFlag"] else None
- else:
- self.sigmay0 = numpy.array(sigmay)
- self.sigmay = numpy.array(sigmay) if self.fitconfig["WeightFlag"] else None
-
- # take the data between limits, using boolean array indexing
- if (xmin is not None or xmax is not None) and len(self.xdata):
- xmin = xmin if xmin is not None else min(self.xdata)
- xmax = xmax if xmax is not None else max(self.xdata)
- bool_array = (self.xdata >= xmin) & (self.xdata <= xmax)
- self.xdata = self.xdata[bool_array]
- self.ydata = self.ydata[bool_array]
- self.sigmay = self.sigmay[bool_array] if sigmay is not None else None
-
- self._finite_mask = numpy.logical_and(
- numpy.all(numpy.isfinite(self.xdata), axis=tuple(range(1, self.xdata.ndim))),
- numpy.isfinite(self.ydata))
-
- def enableweight(self):
- """This method can be called to set :attr:`sigmay`. If :attr:`sigmay0` was filled with
- actual uncertainties in :meth:`setdata`, use these values.
- Else, use ``sqrt(self.ydata)``.
- """
- if self.sigmay0 is None:
- self.sigmay = numpy.sqrt(self.ydata) if self.fitconfig["WeightFlag"] else None
- else:
- self.sigmay = self.sigmay0
-
- def disableweight(self):
- """This method can be called to set :attr:`sigmay` equal to ``None``.
- As a result, :func:`leastsq` will consider that the weights in the
- least square problem are 1 for all samples."""
- self.sigmay = None
-
- def settheory(self, theory):
- """Pick a theory from :attr:`theories`.
-
- :param theory: Name of the theory to be used.
- :raise: KeyError if ``theory`` is not a key of :attr:`theories`.
- """
- if theory is None:
- self.selectedtheory = None
- elif theory in self.theories:
- self.selectedtheory = theory
- else:
- msg = "No theory with name %s in theories.\n" % theory
- msg += "Available theories: %s\n" % self.theories.keys()
- raise KeyError(msg)
-
- # run configure to apply our fitconfig to the selected theory
- # through its custom config function
- self.configure(**self.fitconfig)
-
- def runfit(self, callback=None):
- """Run the actual fitting and fill :attr:`fit_results` with fit results.
-
- Before running this method, :attr:`fit_results` must already be
- populated with a list of all parameters and their estimated values.
- For this, run :meth:`estimate` beforehand.
-
- :param callback: Optional callback function, conforming to the
- signature ``callback(data)`` with ``data`` being a dictionary.
- This callback function is called before and after the estimation
- process, and is given a dictionary containing the values of
- :attr:`state` (``'Fit in progress'`` or ``'Ready'``)
- and :attr:`chisq`.
- This is used for instance in :mod:`silx.gui.fit.FitWidget` to
- update a widget displaying a status message.
- :return: Tuple ``(fitted parameters, uncertainties, infodict)``.
- *infodict* is the dictionary returned by
- :func:`silx.math.fit.leastsq` when called with option
- ``full_output=True``. Uncertainties is a sequence of uncertainty
- values associated with each fitted parameter.
- """
- # self.dataupdate()
-
- self.state = 'Fit in progress'
- self.chisq = None
-
- if callback is not None:
- callback(data={'chisq': self.chisq,
- 'status': self.state})
-
- param_val = []
- param_constraints = []
- # Initial values are set to the ones computed in estimate()
- for param in self.fit_results:
- param_val.append(param['estimation'])
- param_constraints.append([param['code'], param['cons1'], param['cons2']])
-
- # Filter-out not finite data
- ywork = self.ydata[self._finite_mask]
- xwork = self.xdata[self._finite_mask]
-
- try:
- params, covariance_matrix, infodict = leastsq(
- self.fitfunction, # bg + actual model function
- xwork, ywork, param_val,
- sigma=self.sigmay,
- constraints=param_constraints,
- model_deriv=self.theories[self.selectedtheory].derivative,
- full_output=True, left_derivative=True)
- except LinAlgError:
- self.state = 'Fit failed'
- callback(data={'status': self.state})
- raise
-
- sigmas = infodict['uncertainties']
-
- for i, param in enumerate(self.fit_results):
- if param['code'] != 'IGNORE':
- param['fitresult'] = params[i]
- param['sigma'] = sigmas[i]
-
- self.chisq = infodict["reduced_chisq"]
- self.niter = infodict["niter"]
- self.state = 'Ready'
-
- if callback is not None:
- callback(data={'chisq': self.chisq,
- 'status': self.state})
-
- return params, sigmas, infodict
-
- ###################
- # Private methods #
- ###################
- def fitfunction(self, x, *pars):
- """Function to be fitted.
-
- This is the sum of the selected background function plus
- the selected fit model function.
-
- :param x: Independent variable where the function is calculated.
- :param pars: Sequence of all fit parameters. The first few parameters
- are background parameters, then come the peak function parameters.
- :return: Output of the fit function with ``x`` as input and ``pars``
- as fit parameters.
- """
- result = numpy.zeros(numpy.shape(x), numpy.float64)
-
- if self.selectedbg is not None:
- bg_pars_list = self.bgtheories[self.selectedbg].parameters
- nb_bg_pars = len(bg_pars_list)
-
- bgfun = self.bgtheories[self.selectedbg].function
- result += bgfun(x, self.ydata, *pars[0:nb_bg_pars])
- else:
- nb_bg_pars = 0
-
- selectedfun = self.theories[self.selectedtheory].function
- result += selectedfun(x, *pars[nb_bg_pars:])
-
- return result
-
- def estimate_bkg(self, x, y):
- """Estimate background parameters using the function defined in
- the current fit configuration.
-
- To change the selected background model, attribute :attr:`selectdbg`
- must be changed using method :meth:`setbackground`.
-
- The actual background function to be used is
- referenced in :attr:`bgtheories`
-
- :param x: Sequence of x data
- :param y: sequence of y data
- :return: Tuple of two sequences and one data array
- ``(estimated_param, constraints, bg_data)``:
-
- - ``estimated_param`` is a list of estimated values for each
- background parameter.
- - ``constraints`` is a 2D sequence of dimension ``(n_parameters, 3)``
-
- - ``constraints[i][0]``: Constraint code.
- See explanation about codes in :attr:`fit_results`
-
- - ``constraints[i][1]``
- See explanation about 'cons1' in :attr:`fit_results`
- documentation.
-
- - ``constraints[i][2]``
- See explanation about 'cons2' in :attr:`fit_results`
- documentation.
- """
- background_estimate_function = self.bgtheories[self.selectedbg].estimate
- if background_estimate_function is not None:
- return background_estimate_function(x, y)
- else:
- return [], []
-
- def estimate_fun(self, x, y):
- """Estimate fit parameters using the function defined in
- the current fit configuration.
-
- :param x: Sequence of x data
- :param y: sequence of y data
- :param bg: Background signal, to be subtracted from ``y`` before fitting.
- :return: Tuple of two sequences ``(estimated_param, constraints)``:
-
- - ``estimated_param`` is a list of estimated values for each
- background parameter.
- - ``constraints`` is a 2D sequence of dimension (n_parameters, 3)
-
- - ``constraints[i][0]``: Constraint code.
- See explanation about codes in :attr:`fit_results`
-
- - ``constraints[i][1]``
- See explanation about 'cons1' in :attr:`fit_results`
- documentation.
-
- - ``constraints[i][2]``
- See explanation about 'cons2' in :attr:`fit_results`
- documentation.
- :raise: ``TypeError`` if estimation function is not callable
-
- """
- estimatefunction = self.theories[self.selectedtheory].estimate
- if hasattr(estimatefunction, '__call__'):
- if not self.theories[self.selectedtheory].pymca_legacy:
- return estimatefunction(x, y)
- else:
- # legacy pymca estimate functions have a different signature
- if self.fitconfig["fitbkg"] == "No Background":
- bg = numpy.zeros_like(y)
- else:
- if self.fitconfig["SmoothingFlag"]:
- y = smooth1d(y)
- bg = strip(y,
- w=self.fitconfig["StripWidth"],
- niterations=self.fitconfig["StripIterations"],
- factor=self.fitconfig["StripThresholdFactor"])
- # fitconfig can be filled by user defined config function
- xscaling = self.fitconfig.get('Xscaling', 1.0)
- yscaling = self.fitconfig.get('Yscaling', 1.0)
- return estimatefunction(x, y, bg, xscaling, yscaling)
- else:
- raise TypeError("Estimation function in attribute " +
- "theories[%s]" % self.selectedtheory +
- " must be callable.")
-
- def _load_legacy_theories(self, theories_module):
- """Load theories from a custom module in the old PyMca format.
-
- See PyMca5.PyMcaMath.fitting.SpecfitFunctions for an example.
- """
- mandatory_attributes = ["THEORY", "PARAMETERS",
- "FUNCTION", "ESTIMATE"]
- err_msg = "Custom fit function file must define: "
- err_msg += ", ".join(mandatory_attributes)
- for attr in mandatory_attributes:
- if not hasattr(theories_module, attr):
- raise ImportError(err_msg)
-
- derivative = theories_module.DERIVATIVE if hasattr(theories_module, "DERIVATIVE") else None
- configure = theories_module.CONFIGURE if hasattr(theories_module, "CONFIGURE") else None
- estimate = theories_module.ESTIMATE if hasattr(theories_module, "ESTIMATE") else None
- if isinstance(theories_module.THEORY, (list, tuple)):
- # multiple fit functions
- for i in range(len(theories_module.THEORY)):
- deriv = derivative[i] if derivative is not None else None
- config = configure[i] if configure is not None else None
- estim = estimate[i] if estimate is not None else None
- self.addtheory(theories_module.THEORY[i],
- FitTheory(
- theories_module.FUNCTION[i],
- theories_module.PARAMETERS[i],
- estim,
- config,
- deriv,
- pymca_legacy=True))
- else:
- # single fit function
- self.addtheory(theories_module.THEORY,
- FitTheory(
- theories_module.FUNCTION,
- theories_module.PARAMETERS,
- estimate,
- configure,
- derivative,
- pymca_legacy=True))
-
-
-def test():
- from .functions import sum_gauss
- from . import fittheories
- from . import bgtheories
-
- # Create synthetic data with a sum of gaussian functions
- x = numpy.arange(1000).astype(numpy.float64)
-
- p = [1000, 100., 250,
- 255, 690., 45,
- 1500, 800.5, 95]
- y = 0.5 * x + 13 + sum_gauss(x, *p)
-
- # Fitting
- fit = FitManager()
- # more sensitivity necessary to resolve
- # overlapping peaks at x=690 and x=800.5
- fit.setdata(x=x, y=y)
- fit.loadtheories(fittheories)
- fit.settheory('Gaussians')
- fit.loadbgtheories(bgtheories)
- fit.setbackground('Linear')
- fit.estimate()
- fit.runfit()
-
- print("Searched parameters = ", p)
- print("Obtained parameters : ")
- dummy_list = []
- for param in fit.fit_results:
- print(param['name'], ' = ', param['fitresult'])
- dummy_list.append(param['fitresult'])
- print("chisq = ", fit.chisq)
-
- # Plot
- constant, slope = dummy_list[:2]
- p1 = dummy_list[2:]
- print(p1)
- y2 = slope * x + constant + sum_gauss(x, *p1)
-
- try:
- from silx.gui import qt
- from silx.gui.plot.PlotWindow import PlotWindow
- app = qt.QApplication([])
- pw = PlotWindow(control=True)
- pw.addCurve(x, y, "Original")
- pw.addCurve(x, y2, "Fit result")
- pw.legendsDockWidget.show()
- pw.show()
- app.exec_()
- except ImportError:
- _logger.warning("Could not import qt to display fit result as curve")
-
-
-if __name__ == "__main__":
- test()
diff --git a/silx/math/fit/fittheories.py b/silx/math/fit/fittheories.py
deleted file mode 100644
index 6b19a38..0000000
--- a/silx/math/fit/fittheories.py
+++ /dev/null
@@ -1,1374 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-#
-# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-########################################################################### */
-"""This modules provides a set of fit functions and associated
-estimation functions in a format that can be imported into a
-:class:`silx.math.fit.FitManager` instance.
-
-These functions are well suited for fitting multiple gaussian shaped peaks
-typically found in spectroscopy data. The estimation functions are designed
-to detect how many peaks are present in the data, and provide an initial
-estimate for their height, their center location and their full-width
-at half maximum (fwhm).
-
-The limitation of these estimation algorithms is that only gaussians having a
-similar fwhm can be detected by the peak search algorithm.
-This *search fwhm* can be defined by the user, if
-he knows the characteristics of his data, or can be automatically estimated
-based on the fwhm of the largest peak in the data.
-
-The source code of this module can serve as template for defining your own
-fit functions.
-
-The functions to be imported by :meth:`FitManager.loadtheories` are defined by
-a dictionary :const:`THEORY`: with the following structure::
-
- from silx.math.fit.fittheory import FitTheory
-
- THEORY = {
- 'theory_name_1': FitTheory(
- description='Description of theory 1',
- function=fitfunction1,
- parameters=('param name 1', 'param name 2', …),
- estimate=estimation_function1,
- configure=configuration_function1,
- derivative=derivative_function1),
-
- 'theory_name_2': FitTheory(…),
- }
-
-.. note::
-
- Consider using an OrderedDict instead of a regular dictionary, when
- defining your own theory dictionary, if the order matters to you.
- This will likely be the case if you intend to load a selection of
- functions in a GUI such as :class:`silx.gui.fit.FitManager`.
-
-Theory names can be customized (e.g. ``gauss, lorentz, splitgauss``…).
-
-The mandatory parameters for :class:`FitTheory` are ``function`` and
-``parameters``.
-
-You can also define an ``INIT`` function that will be executed by
-:meth:`FitManager.loadtheories`.
-
-See the documentation of :class:`silx.math.fit.fittheory.FitTheory`
-for more information.
-
-Module members:
----------------
-"""
-import numpy
-from collections import OrderedDict
-import logging
-
-from silx.math.fit import functions
-from silx.math.fit.peaks import peak_search, guess_fwhm
-from silx.math.fit.filters import strip, savitsky_golay
-from silx.math.fit.leastsq import leastsq
-from silx.math.fit.fittheory import FitTheory
-
-_logger = logging.getLogger(__name__)
-
-__authors__ = ["V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "15/05/2017"
-
-
-DEFAULT_CONFIG = {
- 'NoConstraintsFlag': False,
- 'PositiveFwhmFlag': True,
- 'PositiveHeightAreaFlag': True,
- 'SameFwhmFlag': False,
- 'QuotedPositionFlag': False, # peak not outside data range
- 'QuotedEtaFlag': False, # force 0 < eta < 1
- # Peak detection
- 'AutoScaling': False,
- 'Yscaling': 1.0,
- 'FwhmPoints': 8,
- 'AutoFwhm': True,
- 'Sensitivity': 2.5,
- 'ForcePeakPresence': True,
- # Hypermet
- 'HypermetTails': 15,
- 'QuotedFwhmFlag': 0,
- 'MaxFwhm2InputRatio': 1.5,
- 'MinFwhm2InputRatio': 0.4,
- # short tail parameters
- 'MinGaussArea4ShortTail': 50000.,
- 'InitialShortTailAreaRatio': 0.050,
- 'MaxShortTailAreaRatio': 0.100,
- 'MinShortTailAreaRatio': 0.0010,
- 'InitialShortTailSlopeRatio': 0.70,
- 'MaxShortTailSlopeRatio': 2.00,
- 'MinShortTailSlopeRatio': 0.50,
- # long tail parameters
- 'MinGaussArea4LongTail': 1000.0,
- 'InitialLongTailAreaRatio': 0.050,
- 'MaxLongTailAreaRatio': 0.300,
- 'MinLongTailAreaRatio': 0.010,
- 'InitialLongTailSlopeRatio': 20.0,
- 'MaxLongTailSlopeRatio': 50.0,
- 'MinLongTailSlopeRatio': 5.0,
- # step tail
- 'MinGaussHeight4StepTail': 5000.,
- 'InitialStepTailHeightRatio': 0.002,
- 'MaxStepTailHeightRatio': 0.0100,
- 'MinStepTailHeightRatio': 0.0001,
- # Hypermet constraints
- # position in range [estimated position +- estimated fwhm/2]
- 'HypermetQuotedPositionFlag': True,
- 'DeltaPositionFwhmUnits': 0.5,
- 'SameSlopeRatioFlag': 1,
- 'SameAreaRatioFlag': 1,
- # Strip bg removal
- 'StripBackgroundFlag': True,
- 'SmoothingFlag': True,
- 'SmoothingWidth': 5,
- 'StripWidth': 2,
- 'StripIterations': 5000,
- 'StripThresholdFactor': 1.0}
-"""This dictionary defines default configuration parameters that have effects
-on fit functions and estimation functions, mainly on fit constraints.
-This dictionary is accessible as attribute :attr:`FitTheories.config`,
-which can be modified by configuration functions defined in
-:const:`CONFIGURE`.
-"""
-
-CFREE = 0
-CPOSITIVE = 1
-CQUOTED = 2
-CFIXED = 3
-CFACTOR = 4
-CDELTA = 5
-CSUM = 6
-CIGNORED = 7
-
-
-class FitTheories(object):
- """Class wrapping functions from :class:`silx.math.fit.functions`
- and providing estimate functions for all of these fit functions."""
- def __init__(self, config=None):
- if config is None:
- self.config = DEFAULT_CONFIG
- else:
- self.config = config
-
- def ahypermet(self, x, *pars):
- """
- Wrapping of :func:`silx.math.fit.functions.sum_ahypermet` without
- the tail flags in the function signature.
-
- Depending on the value of `self.config['HypermetTails']`, one can
- activate or deactivate the various terms of the hypermet function.
-
- `self.config['HypermetTails']` must be an integer between 0 and 15.
- It is a set of 4 binary flags, one for activating each one of the
- hypermet terms: *gaussian function, short tail, long tail, step*.
-
- For example, 15 can be expressed as ``1111`` in base 2, so a flag of
- 15 means all terms are active.
- """
- g_term = self.config['HypermetTails'] & 1
- st_term = (self.config['HypermetTails'] >> 1) & 1
- lt_term = (self.config['HypermetTails'] >> 2) & 1
- step_term = (self.config['HypermetTails'] >> 3) & 1
- return functions.sum_ahypermet(x, *pars,
- gaussian_term=g_term, st_term=st_term,
- lt_term=lt_term, step_term=step_term)
-
- def poly(self, x, *pars):
- """Order n polynomial.
- The order of the polynomial is defined by the number of
- coefficients (``*pars``).
-
- """
- p = numpy.poly1d(pars)
- return p(x)
-
- @staticmethod
- def estimate_poly(x, y, n=2):
- """Estimate polynomial coefficients for a degree n polynomial.
-
- """
- pcoeffs = numpy.polyfit(x, y, n)
- constraints = numpy.zeros((n + 1, 3), numpy.float64)
- return pcoeffs, constraints
-
- def estimate_quadratic(self, x, y):
- """Estimate quadratic coefficients
-
- """
- return self.estimate_poly(x, y, n=2)
-
- def estimate_cubic(self, x, y):
- """Estimate coefficients for a degree 3 polynomial
-
- """
- return self.estimate_poly(x, y, n=3)
-
- def estimate_quartic(self, x, y):
- """Estimate coefficients for a degree 4 polynomial
-
- """
- return self.estimate_poly(x, y, n=4)
-
- def estimate_quintic(self, x, y):
- """Estimate coefficients for a degree 5 polynomial
-
- """
- return self.estimate_poly(x, y, n=5)
-
- def strip_bg(self, y):
- """Return the strip background of y, using parameters from
- :attr:`config` dictionary (*StripBackgroundFlag, StripWidth,
- StripIterations, StripThresholdFactor*)"""
- remove_strip_bg = self.config.get('StripBackgroundFlag', False)
- if remove_strip_bg:
- if self.config['SmoothingFlag']:
- y = savitsky_golay(y, self.config['SmoothingWidth'])
- strip_width = self.config['StripWidth']
- strip_niterations = self.config['StripIterations']
- strip_thr_factor = self.config['StripThresholdFactor']
- return strip(y, w=strip_width,
- niterations=strip_niterations,
- factor=strip_thr_factor)
- else:
- return numpy.zeros_like(y)
-
- def guess_yscaling(self, y):
- """Estimate scaling for y prior to peak search.
- A smoothing filter is applied to y to estimate the noise level
- (chi-squared)
-
- :param y: Data array
- :return: Scaling factor
- """
- # ensure y is an array
- yy = numpy.array(y, copy=False)
-
- # smooth
- convolution_kernel = numpy.ones(shape=(3,)) / 3.
- ysmooth = numpy.convolve(y, convolution_kernel, mode="same")
-
- # remove zeros
- idx_array = numpy.fabs(y) > 0.0
- yy = yy[idx_array]
- ysmooth = ysmooth[idx_array]
-
- # compute scaling factor
- chisq = numpy.mean((yy - ysmooth)**2 / numpy.fabs(yy))
- if chisq > 0:
- return 1. / chisq
- else:
- return 1.0
-
- def peak_search(self, y, fwhm, sensitivity):
- """Search for peaks in y array, after padding the array and
- multiplying its value by a scaling factor.
-
- :param y: 1-D data array
- :param int fwhm: Typical full width at half maximum for peaks,
- in number of points. This parameter is used for to discriminate between
- true peaks and background fluctuations.
- :param float sensitivity: Sensitivity parameter. This is a threshold factor
- for peak detection. Only peaks larger than the standard deviation
- of the noise multiplied by this sensitivity parameter are detected.
- :return: List of peak indices
- """
- # add padding
- ysearch = numpy.ones((len(y) + 2 * fwhm,), numpy.float64)
- ysearch[0:fwhm] = y[0]
- ysearch[-1:-fwhm - 1:-1] = y[len(y)-1]
- ysearch[fwhm:fwhm + len(y)] = y[:]
-
- scaling = self.guess_yscaling(y) if self.config["AutoScaling"] else self.config["Yscaling"]
-
- if len(ysearch) > 1.5 * fwhm:
- peaks = peak_search(scaling * ysearch,
- fwhm=fwhm, sensitivity=sensitivity)
- return [peak_index - fwhm for peak_index in peaks
- if 0 <= peak_index - fwhm < len(y)]
- else:
- return []
-
- def estimate_height_position_fwhm(self, x, y):
- """Estimation of *Height, Position, FWHM* of peaks, for gaussian-like
- curves.
-
- This functions finds how many parameters are needed, based on the
- number of peaks detected. Then it estimates the fit parameters
- with a few iterations of fitting gaussian functions.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Height, Position, FWHM*.
- Fit constraints depend on :attr:`config`.
- """
- fittedpar = []
-
- bg = self.strip_bg(y)
-
- if self.config['AutoFwhm']:
- search_fwhm = guess_fwhm(y)
- else:
- search_fwhm = int(float(self.config['FwhmPoints']))
- search_sens = float(self.config['Sensitivity'])
-
- if search_fwhm < 3:
- _logger.warning("Setting peak fwhm to 3 (lower limit)")
- search_fwhm = 3
- self.config['FwhmPoints'] = 3
-
- if search_sens < 1:
- _logger.warning("Setting peak search sensitivity to 1. " +
- "(lower limit to filter out noise peaks)")
- search_sens = 1
- self.config['Sensitivity'] = 1
-
- npoints = len(y)
-
- # Find indices of peaks in data array
- peaks = self.peak_search(y,
- fwhm=search_fwhm,
- sensitivity=search_sens)
-
- if not len(peaks):
- forcepeak = int(float(self.config.get('ForcePeakPresence', 0)))
- if forcepeak:
- delta = y - bg
- # get index of global maximum
- # (first one if several samples are equal to this value)
- peaks = [numpy.nonzero(delta == delta.max())[0][0]]
-
- # Find index of largest peak in peaks array
- index_largest_peak = 0
- if len(peaks) > 0:
- # estimate fwhm as 5 * sampling interval
- sig = 5 * abs(x[npoints - 1] - x[0]) / npoints
- peakpos = x[int(peaks[0])]
- if abs(peakpos) < 1.0e-16:
- peakpos = 0.0
- param = numpy.array(
- [y[int(peaks[0])] - bg[int(peaks[0])], peakpos, sig])
- height_largest_peak = param[0]
- peak_index = 1
- for i in peaks[1:]:
- param2 = numpy.array(
- [y[int(i)] - bg[int(i)], x[int(i)], sig])
- param = numpy.concatenate((param, param2))
- if param2[0] > height_largest_peak:
- height_largest_peak = param2[0]
- index_largest_peak = peak_index
- peak_index += 1
-
- # Subtract background
- xw = x
- yw = y - bg
-
- cons = numpy.zeros((len(param), 3), numpy.float64)
-
- # peak height must be positive
- cons[0:len(param):3, 0] = CPOSITIVE
- # force peaks to stay around their position
- cons[1:len(param):3, 0] = CQUOTED
-
- # set possible peak range to estimated peak +- guessed fwhm
- if len(xw) > search_fwhm:
- fwhmx = numpy.fabs(xw[int(search_fwhm)] - xw[0])
- cons[1:len(param):3, 1] = param[1:len(param):3] - 0.5 * fwhmx
- cons[1:len(param):3, 2] = param[1:len(param):3] + 0.5 * fwhmx
- else:
- shape = [max(1, int(x)) for x in (param[1:len(param):3])]
- cons[1:len(param):3, 1] = min(xw) * numpy.ones(
- shape,
- numpy.float64)
- cons[1:len(param):3, 2] = max(xw) * numpy.ones(
- shape,
- numpy.float64)
-
- # ensure fwhm is positive
- cons[2:len(param):3, 0] = CPOSITIVE
-
- # run a quick iterative fit (4 iterations) to improve
- # estimations
- fittedpar, _, _ = leastsq(functions.sum_gauss, xw, yw, param,
- max_iter=4, constraints=cons.tolist(),
- full_output=True)
-
- # set final constraints based on config parameters
- cons = numpy.zeros((len(fittedpar), 3), numpy.float64)
- peak_index = 0
- for i in range(len(peaks)):
- # Setup height area constrains
- if not self.config['NoConstraintsFlag']:
- if self.config['PositiveHeightAreaFlag']:
- cons[peak_index, 0] = CPOSITIVE
- cons[peak_index, 1] = 0
- cons[peak_index, 2] = 0
- peak_index += 1
-
- # Setup position constrains
- if not self.config['NoConstraintsFlag']:
- if self.config['QuotedPositionFlag']:
- cons[peak_index, 0] = CQUOTED
- cons[peak_index, 1] = min(x)
- cons[peak_index, 2] = max(x)
- peak_index += 1
-
- # Setup positive FWHM constrains
- if not self.config['NoConstraintsFlag']:
- if self.config['PositiveFwhmFlag']:
- cons[peak_index, 0] = CPOSITIVE
- cons[peak_index, 1] = 0
- cons[peak_index, 2] = 0
- if self.config['SameFwhmFlag']:
- if i != index_largest_peak:
- cons[peak_index, 0] = CFACTOR
- cons[peak_index, 1] = 3 * index_largest_peak + 2
- cons[peak_index, 2] = 1.0
- peak_index += 1
-
- return fittedpar, cons
-
- def estimate_agauss(self, x, y):
- """Estimation of *Area, Position, FWHM* of peaks, for gaussian-like
- curves.
-
- This functions uses :meth:`estimate_height_position_fwhm`, then
- converts the height parameters to area under the curve with the
- formula ``area = sqrt(2*pi) * height * fwhm / (2 * sqrt(2 * log(2))``
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Area, Position, FWHM*.
- Fit constraints depend on :attr:`config`.
- """
- fittedpar, cons = self.estimate_height_position_fwhm(x, y)
- # get the number of found peaks
- npeaks = len(fittedpar) // 3
- for i in range(npeaks):
- height = fittedpar[3 * i]
- fwhm = fittedpar[3 * i + 2]
- # Replace height with area in fittedpar
- fittedpar[3 * i] = numpy.sqrt(2 * numpy.pi) * height * fwhm / (
- 2.0 * numpy.sqrt(2 * numpy.log(2)))
- return fittedpar, cons
-
- def estimate_alorentz(self, x, y):
- """Estimation of *Area, Position, FWHM* of peaks, for Lorentzian
- curves.
-
- This functions uses :meth:`estimate_height_position_fwhm`, then
- converts the height parameters to area under the curve with the
- formula ``area = height * fwhm * 0.5 * pi``
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Area, Position, FWHM*.
- Fit constraints depend on :attr:`config`.
- """
- fittedpar, cons = self.estimate_height_position_fwhm(x, y)
- # get the number of found peaks
- npeaks = len(fittedpar) // 3
- for i in range(npeaks):
- height = fittedpar[3 * i]
- fwhm = fittedpar[3 * i + 2]
- # Replace height with area in fittedpar
- fittedpar[3 * i] = (height * fwhm * 0.5 * numpy.pi)
- return fittedpar, cons
-
- def estimate_splitgauss(self, x, y):
- """Estimation of *Height, Position, FWHM1, FWHM2* of peaks, for
- asymmetric gaussian-like curves.
-
- This functions uses :meth:`estimate_height_position_fwhm`, then
- adds a second (identical) estimation of FWHM to the fit parameters
- for each peak, and the corresponding constraint.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Height, Position, FWHM1, FWHM2*.
- Fit constraints depend on :attr:`config`.
- """
- fittedpar, cons = self.estimate_height_position_fwhm(x, y)
- # get the number of found peaks
- npeaks = len(fittedpar) // 3
- estimated_parameters = []
- estimated_constraints = numpy.zeros((4 * npeaks, 3), numpy.float64)
- for i in range(npeaks):
- for j in range(3):
- estimated_parameters.append(fittedpar[3 * i + j])
- # fwhm2 estimate = fwhm1
- estimated_parameters.append(fittedpar[3 * i + 2])
- # height
- estimated_constraints[4 * i, 0] = cons[3 * i, 0]
- estimated_constraints[4 * i, 1] = cons[3 * i, 1]
- estimated_constraints[4 * i, 2] = cons[3 * i, 2]
- # position
- estimated_constraints[4 * i + 1, 0] = cons[3 * i + 1, 0]
- estimated_constraints[4 * i + 1, 1] = cons[3 * i + 1, 1]
- estimated_constraints[4 * i + 1, 2] = cons[3 * i + 1, 2]
- # fwhm1
- estimated_constraints[4 * i + 2, 0] = cons[3 * i + 2, 0]
- estimated_constraints[4 * i + 2, 1] = cons[3 * i + 2, 1]
- estimated_constraints[4 * i + 2, 2] = cons[3 * i + 2, 2]
- # fwhm2
- estimated_constraints[4 * i + 3, 0] = cons[3 * i + 2, 0]
- estimated_constraints[4 * i + 3, 1] = cons[3 * i + 2, 1]
- estimated_constraints[4 * i + 3, 2] = cons[3 * i + 2, 2]
- if cons[3 * i + 2, 0] == CFACTOR:
- # convert indices of related parameters
- # (this happens if SameFwhmFlag == True)
- estimated_constraints[4 * i + 2, 1] = \
- int(cons[3 * i + 2, 1] / 3) * 4 + 2
- estimated_constraints[4 * i + 3, 1] = \
- int(cons[3 * i + 2, 1] / 3) * 4 + 3
- return estimated_parameters, estimated_constraints
-
- def estimate_pvoigt(self, x, y):
- """Estimation of *Height, Position, FWHM, eta* of peaks, for
- pseudo-Voigt curves.
-
- Pseudo-Voigt are a sum of a gaussian curve *G(x)* and a lorentzian
- curve *L(x)* with the same height, center, fwhm parameters:
- ``y(x) = eta * G(x) + (1-eta) * L(x)``
-
- This functions uses :meth:`estimate_height_position_fwhm`, then
- adds a constant estimation of *eta* (0.5) to the fit parameters
- for each peak, and the corresponding constraint.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Height, Position, FWHM, eta*.
- Constraint for the eta parameter can be set to QUOTED (0.--1.)
- by setting :attr:`config`['QuotedEtaFlag'] to ``True``.
- If this is not the case, the constraint code is set to FREE.
- """
- fittedpar, cons = self.estimate_height_position_fwhm(x, y)
- npeaks = len(fittedpar) // 3
- newpar = []
- newcons = numpy.zeros((4 * npeaks, 3), numpy.float64)
- # find out related parameters proper index
- if not self.config['NoConstraintsFlag']:
- if self.config['SameFwhmFlag']:
- j = 0
- # get the index of the free FWHM
- for i in range(npeaks):
- if cons[3 * i + 2, 0] != 4:
- j = i
- for i in range(npeaks):
- if i != j:
- cons[3 * i + 2, 1] = 4 * j + 2
- for i in range(npeaks):
- newpar.append(fittedpar[3 * i])
- newpar.append(fittedpar[3 * i + 1])
- newpar.append(fittedpar[3 * i + 2])
- newpar.append(0.5)
- # height
- newcons[4 * i, 0] = cons[3 * i, 0]
- newcons[4 * i, 1] = cons[3 * i, 1]
- newcons[4 * i, 2] = cons[3 * i, 2]
- # position
- newcons[4 * i + 1, 0] = cons[3 * i + 1, 0]
- newcons[4 * i + 1, 1] = cons[3 * i + 1, 1]
- newcons[4 * i + 1, 2] = cons[3 * i + 1, 2]
- # fwhm
- newcons[4 * i + 2, 0] = cons[3 * i + 2, 0]
- newcons[4 * i + 2, 1] = cons[3 * i + 2, 1]
- newcons[4 * i + 2, 2] = cons[3 * i + 2, 2]
- # Eta constrains
- newcons[4 * i + 3, 0] = CFREE
- newcons[4 * i + 3, 1] = 0
- newcons[4 * i + 3, 2] = 0
- if self.config['QuotedEtaFlag']:
- newcons[4 * i + 3, 0] = CQUOTED
- newcons[4 * i + 3, 1] = 0.0
- newcons[4 * i + 3, 2] = 1.0
- return newpar, newcons
-
- def estimate_splitpvoigt(self, x, y):
- """Estimation of *Height, Position, FWHM1, FWHM2, eta* of peaks, for
- asymmetric pseudo-Voigt curves.
-
- This functions uses :meth:`estimate_height_position_fwhm`, then
- adds an identical FWHM2 parameter and a constant estimation of
- *eta* (0.5) to the fit parameters for each peak, and the corresponding
- constraints.
-
- Constraint for the eta parameter can be set to QUOTED (0.--1.)
- by setting :attr:`config`['QuotedEtaFlag'] to ``True``.
- If this is not the case, the constraint code is set to FREE.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Height, Position, FWHM1, FWHM2, eta*.
- """
- fittedpar, cons = self.estimate_height_position_fwhm(x, y)
- npeaks = len(fittedpar) // 3
- newpar = []
- newcons = numpy.zeros((5 * npeaks, 3), numpy.float64)
- # find out related parameters proper index
- if not self.config['NoConstraintsFlag']:
- if self.config['SameFwhmFlag']:
- j = 0
- # get the index of the free FWHM
- for i in range(npeaks):
- if cons[3 * i + 2, 0] != 4:
- j = i
- for i in range(npeaks):
- if i != j:
- cons[3 * i + 2, 1] = 4 * j + 2
- for i in range(npeaks):
- # height
- newpar.append(fittedpar[3 * i])
- # position
- newpar.append(fittedpar[3 * i + 1])
- # fwhm1
- newpar.append(fittedpar[3 * i + 2])
- # fwhm2 estimate equal to fwhm1
- newpar.append(fittedpar[3 * i + 2])
- # eta
- newpar.append(0.5)
- # constraint codes
- # ----------------
- # height
- newcons[5 * i, 0] = cons[3 * i, 0]
- # position
- newcons[5 * i + 1, 0] = cons[3 * i + 1, 0]
- # fwhm1
- newcons[5 * i + 2, 0] = cons[3 * i + 2, 0]
- # fwhm2
- newcons[5 * i + 3, 0] = cons[3 * i + 2, 0]
- # cons 1
- # ------
- newcons[5 * i, 1] = cons[3 * i, 1]
- newcons[5 * i + 1, 1] = cons[3 * i + 1, 1]
- newcons[5 * i + 2, 1] = cons[3 * i + 2, 1]
- newcons[5 * i + 3, 1] = cons[3 * i + 2, 1]
- # cons 2
- # ------
- newcons[5 * i, 2] = cons[3 * i, 2]
- newcons[5 * i + 1, 2] = cons[3 * i + 1, 2]
- newcons[5 * i + 2, 2] = cons[3 * i + 2, 2]
- newcons[5 * i + 3, 2] = cons[3 * i + 2, 2]
-
- if cons[3 * i + 2, 0] == CFACTOR:
- # fwhm2 connstraint depends on fwhm1
- newcons[5 * i + 3, 1] = newcons[5 * i + 2, 1] + 1
- # eta constraints
- newcons[5 * i + 4, 0] = CFREE
- newcons[5 * i + 4, 1] = 0
- newcons[5 * i + 4, 2] = 0
- if self.config['QuotedEtaFlag']:
- newcons[5 * i + 4, 0] = CQUOTED
- newcons[5 * i + 4, 1] = 0.0
- newcons[5 * i + 4, 2] = 1.0
- return newpar, newcons
-
- def estimate_apvoigt(self, x, y):
- """Estimation of *Area, Position, FWHM1, eta* of peaks, for
- pseudo-Voigt curves.
-
- This functions uses :meth:`estimate_pvoigt`, then converts the height
- parameter to area.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *Area, Position, FWHM, eta*.
- """
- fittedpar, cons = self.estimate_pvoigt(x, y)
- npeaks = len(fittedpar) // 4
- # Assume 50% of the area is determined by the gaussian and 50% by
- # the Lorentzian.
- for i in range(npeaks):
- height = fittedpar[4 * i]
- fwhm = fittedpar[4 * i + 2]
- fittedpar[4 * i] = 0.5 * (height * fwhm * 0.5 * numpy.pi) +\
- 0.5 * (height * fwhm / (2.0 * numpy.sqrt(2 * numpy.log(2)))
- ) * numpy.sqrt(2 * numpy.pi)
- return fittedpar, cons
-
- def estimate_ahypermet(self, x, y):
- """Estimation of *area, position, fwhm, st_area_r, st_slope_r,
- lt_area_r, lt_slope_r, step_height_r* of peaks, for hypermet curves.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each peak are:
- *area, position, fwhm, st_area_r, st_slope_r,
- lt_area_r, lt_slope_r, step_height_r* .
- """
- yscaling = self.config.get('Yscaling', 1.0)
- if yscaling == 0:
- yscaling = 1.0
- fittedpar, cons = self.estimate_height_position_fwhm(x, y)
- npeaks = len(fittedpar) // 3
- newpar = []
- newcons = numpy.zeros((8 * npeaks, 3), numpy.float64)
- main_peak = 0
- # find out related parameters proper index
- if not self.config['NoConstraintsFlag']:
- if self.config['SameFwhmFlag']:
- j = 0
- # get the index of the free FWHM
- for i in range(npeaks):
- if cons[3 * i + 2, 0] != 4:
- j = i
- for i in range(npeaks):
- if i != j:
- cons[3 * i + 2, 1] = 8 * j + 2
- main_peak = j
- for i in range(npeaks):
- if fittedpar[3 * i] > fittedpar[3 * main_peak]:
- main_peak = i
-
- for i in range(npeaks):
- height = fittedpar[3 * i]
- position = fittedpar[3 * i + 1]
- fwhm = fittedpar[3 * i + 2]
- area = (height * fwhm / (2.0 * numpy.sqrt(2 * numpy.log(2)))
- ) * numpy.sqrt(2 * numpy.pi)
- # the gaussian parameters
- newpar.append(area)
- newpar.append(position)
- newpar.append(fwhm)
- # print "area, pos , fwhm = ",area,position,fwhm
- # Avoid zero derivatives because of not calculating contribution
- g_term = 1
- st_term = 1
- lt_term = 1
- step_term = 1
- if self.config['HypermetTails'] != 0:
- g_term = self.config['HypermetTails'] & 1
- st_term = (self.config['HypermetTails'] >> 1) & 1
- lt_term = (self.config['HypermetTails'] >> 2) & 1
- step_term = (self.config['HypermetTails'] >> 3) & 1
- if g_term == 0:
- # fix the gaussian parameters
- newcons[8 * i, 0] = CFIXED
- newcons[8 * i + 1, 0] = CFIXED
- newcons[8 * i + 2, 0] = CFIXED
- # the short tail parameters
- if ((area * yscaling) <
- self.config['MinGaussArea4ShortTail']) | \
- (st_term == 0):
- newpar.append(0.0)
- newpar.append(0.0)
- newcons[8 * i + 3, 0] = CFIXED
- newcons[8 * i + 3, 1] = 0.0
- newcons[8 * i + 3, 2] = 0.0
- newcons[8 * i + 4, 0] = CFIXED
- newcons[8 * i + 4, 1] = 0.0
- newcons[8 * i + 4, 2] = 0.0
- else:
- newpar.append(self.config['InitialShortTailAreaRatio'])
- newpar.append(self.config['InitialShortTailSlopeRatio'])
- newcons[8 * i + 3, 0] = CQUOTED
- newcons[8 * i + 3, 1] = self.config['MinShortTailAreaRatio']
- newcons[8 * i + 3, 2] = self.config['MaxShortTailAreaRatio']
- newcons[8 * i + 4, 0] = CQUOTED
- newcons[8 * i + 4, 1] = self.config['MinShortTailSlopeRatio']
- newcons[8 * i + 4, 2] = self.config['MaxShortTailSlopeRatio']
- # the long tail parameters
- if ((area * yscaling) <
- self.config['MinGaussArea4LongTail']) | \
- (lt_term == 0):
- newpar.append(0.0)
- newpar.append(0.0)
- newcons[8 * i + 5, 0] = CFIXED
- newcons[8 * i + 5, 1] = 0.0
- newcons[8 * i + 5, 2] = 0.0
- newcons[8 * i + 6, 0] = CFIXED
- newcons[8 * i + 6, 1] = 0.0
- newcons[8 * i + 6, 2] = 0.0
- else:
- newpar.append(self.config['InitialLongTailAreaRatio'])
- newpar.append(self.config['InitialLongTailSlopeRatio'])
- newcons[8 * i + 5, 0] = CQUOTED
- newcons[8 * i + 5, 1] = self.config['MinLongTailAreaRatio']
- newcons[8 * i + 5, 2] = self.config['MaxLongTailAreaRatio']
- newcons[8 * i + 6, 0] = CQUOTED
- newcons[8 * i + 6, 1] = self.config['MinLongTailSlopeRatio']
- newcons[8 * i + 6, 2] = self.config['MaxLongTailSlopeRatio']
- # the step parameters
- if ((height * yscaling) <
- self.config['MinGaussHeight4StepTail']) | \
- (step_term == 0):
- newpar.append(0.0)
- newcons[8 * i + 7, 0] = CFIXED
- newcons[8 * i + 7, 1] = 0.0
- newcons[8 * i + 7, 2] = 0.0
- else:
- newpar.append(self.config['InitialStepTailHeightRatio'])
- newcons[8 * i + 7, 0] = CQUOTED
- newcons[8 * i + 7, 1] = self.config['MinStepTailHeightRatio']
- newcons[8 * i + 7, 2] = self.config['MaxStepTailHeightRatio']
- # if self.config['NoConstraintsFlag'] == 1:
- # newcons=numpy.zeros((8*npeaks, 3),numpy.float64)
- if npeaks > 0:
- if g_term:
- if self.config['PositiveHeightAreaFlag']:
- for i in range(npeaks):
- newcons[8 * i, 0] = CPOSITIVE
- if self.config['PositiveFwhmFlag']:
- for i in range(npeaks):
- newcons[8 * i + 2, 0] = CPOSITIVE
- if self.config['SameFwhmFlag']:
- for i in range(npeaks):
- if i != main_peak:
- newcons[8 * i + 2, 0] = CFACTOR
- newcons[8 * i + 2, 1] = 8 * main_peak + 2
- newcons[8 * i + 2, 2] = 1.0
- if self.config['HypermetQuotedPositionFlag']:
- for i in range(npeaks):
- delta = self.config['DeltaPositionFwhmUnits'] * fwhm
- newcons[8 * i + 1, 0] = CQUOTED
- newcons[8 * i + 1, 1] = newpar[8 * i + 1] - delta
- newcons[8 * i + 1, 2] = newpar[8 * i + 1] + delta
- if self.config['SameSlopeRatioFlag']:
- for i in range(npeaks):
- if i != main_peak:
- newcons[8 * i + 4, 0] = CFACTOR
- newcons[8 * i + 4, 1] = 8 * main_peak + 4
- newcons[8 * i + 4, 2] = 1.0
- newcons[8 * i + 6, 0] = CFACTOR
- newcons[8 * i + 6, 1] = 8 * main_peak + 6
- newcons[8 * i + 6, 2] = 1.0
- if self.config['SameAreaRatioFlag']:
- for i in range(npeaks):
- if i != main_peak:
- newcons[8 * i + 3, 0] = CFACTOR
- newcons[8 * i + 3, 1] = 8 * main_peak + 3
- newcons[8 * i + 3, 2] = 1.0
- newcons[8 * i + 5, 0] = CFACTOR
- newcons[8 * i + 5, 1] = 8 * main_peak + 5
- newcons[8 * i + 5, 2] = 1.0
- return newpar, newcons
-
- def estimate_stepdown(self, x, y):
- """Estimation of parameters for stepdown curves.
-
- The functions estimates gaussian parameters for the derivative of
- the data, takes the largest gaussian peak and uses its estimated
- parameters to define the center of the step and its fwhm. The
- estimated amplitude returned is simply ``max(y) - min(y)``.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit newconstraints.
- Parameters to be estimated for each stepdown are:
- *height, centroid, fwhm* .
- """
- crappyfilter = [-0.25, -0.75, 0.0, 0.75, 0.25]
- cutoff = len(crappyfilter) // 2
- y_deriv = numpy.convolve(y,
- crappyfilter,
- mode="valid")
-
- # make the derivative's peak have the same amplitude as the step
- if max(y_deriv) > 0:
- y_deriv = y_deriv * max(y) / max(y_deriv)
-
- fittedpar, newcons = self.estimate_height_position_fwhm(
- x[cutoff:-cutoff], y_deriv)
-
- data_amplitude = max(y) - min(y)
-
- # use parameters from largest gaussian found
- if len(fittedpar):
- npeaks = len(fittedpar) // 3
- largest_index = 0
- largest = [data_amplitude,
- fittedpar[3 * largest_index + 1],
- fittedpar[3 * largest_index + 2]]
- for i in range(npeaks):
- if fittedpar[3 * i] > largest[0]:
- largest_index = i
- largest = [data_amplitude,
- fittedpar[3 * largest_index + 1],
- fittedpar[3 * largest_index + 2]]
- else:
- # no peak was found
- largest = [data_amplitude, # height
- x[len(x)//2], # center: middle of x range
- self.config["FwhmPoints"] * (x[1] - x[0])] # fwhm: default value
-
- # Setup constrains
- newcons = numpy.zeros((3, 3), numpy.float64)
- if not self.config['NoConstraintsFlag']:
- # Setup height constrains
- if self.config['PositiveHeightAreaFlag']:
- newcons[0, 0] = CPOSITIVE
- newcons[0, 1] = 0
- newcons[0, 2] = 0
-
- # Setup position constrains
- if self.config['QuotedPositionFlag']:
- newcons[1, 0] = CQUOTED
- newcons[1, 1] = min(x)
- newcons[1, 2] = max(x)
-
- # Setup positive FWHM constrains
- if self.config['PositiveFwhmFlag']:
- newcons[2, 0] = CPOSITIVE
- newcons[2, 1] = 0
- newcons[2, 2] = 0
-
- return largest, newcons
-
- def estimate_slit(self, x, y):
- """Estimation of parameters for slit curves.
-
- The functions estimates stepup and stepdown parameters for the largest
- steps, and uses them for calculating the center (middle between stepup
- and stepdown), the height (maximum amplitude in data), the fwhm
- (distance between the up- and down-step centers) and the beamfwhm
- (average of FWHM for up- and down-step).
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each slit are:
- *height, position, fwhm, beamfwhm* .
- """
- largestup, cons = self.estimate_stepup(x, y)
- largestdown, cons = self.estimate_stepdown(x, y)
- fwhm = numpy.fabs(largestdown[1] - largestup[1])
- beamfwhm = 0.5 * (largestup[2] + largestdown[1])
- beamfwhm = min(beamfwhm, fwhm / 10.0)
- beamfwhm = max(beamfwhm, (max(x) - min(x)) * 3.0 / len(x))
-
- y_minus_bg = y - self.strip_bg(y)
- height = max(y_minus_bg)
-
- i1 = numpy.nonzero(y_minus_bg >= 0.5 * height)[0]
- xx = numpy.take(x, i1)
- position = (xx[0] + xx[-1]) / 2.0
- fwhm = xx[-1] - xx[0]
- largest = [height, position, fwhm, beamfwhm]
- cons = numpy.zeros((4, 3), numpy.float64)
- # Setup constrains
- if not self.config['NoConstraintsFlag']:
- # Setup height constrains
- if self.config['PositiveHeightAreaFlag']:
- cons[0, 0] = CPOSITIVE
- cons[0, 1] = 0
- cons[0, 2] = 0
-
- # Setup position constrains
- if self.config['QuotedPositionFlag']:
- cons[1, 0] = CQUOTED
- cons[1, 1] = min(x)
- cons[1, 2] = max(x)
-
- # Setup positive FWHM constrains
- if self.config['PositiveFwhmFlag']:
- cons[2, 0] = CPOSITIVE
- cons[2, 1] = 0
- cons[2, 2] = 0
-
- # Setup positive FWHM constrains
- if self.config['PositiveFwhmFlag']:
- cons[3, 0] = CPOSITIVE
- cons[3, 1] = 0
- cons[3, 2] = 0
- return largest, cons
-
- def estimate_stepup(self, x, y):
- """Estimation of parameters for a single step up curve.
-
- The functions estimates gaussian parameters for the derivative of
- the data, takes the largest gaussian peak and uses its estimated
- parameters to define the center of the step and its fwhm. The
- estimated amplitude returned is simply ``max(y) - min(y)``.
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- Parameters to be estimated for each stepup are:
- *height, centroid, fwhm* .
- """
- crappyfilter = [0.25, 0.75, 0.0, -0.75, -0.25]
- cutoff = len(crappyfilter) // 2
- y_deriv = numpy.convolve(y, crappyfilter, mode="valid")
- if max(y_deriv) > 0:
- y_deriv = y_deriv * max(y) / max(y_deriv)
-
- fittedpar, cons = self.estimate_height_position_fwhm(
- x[cutoff:-cutoff], y_deriv)
-
- # for height, use the data amplitude after removing the background
- data_amplitude = max(y) - min(y)
-
- # find params of the largest gaussian found
- if len(fittedpar):
- npeaks = len(fittedpar) // 3
- largest_index = 0
- largest = [data_amplitude,
- fittedpar[3 * largest_index + 1],
- fittedpar[3 * largest_index + 2]]
- for i in range(npeaks):
- if fittedpar[3 * i] > largest[0]:
- largest_index = i
- largest = [fittedpar[3 * largest_index],
- fittedpar[3 * largest_index + 1],
- fittedpar[3 * largest_index + 2]]
- else:
- # no peak was found
- largest = [data_amplitude, # height
- x[len(x)//2], # center: middle of x range
- self.config["FwhmPoints"] * (x[1] - x[0])] # fwhm: default value
-
- newcons = numpy.zeros((3, 3), numpy.float64)
- # Setup constrains
- if not self.config['NoConstraintsFlag']:
- # Setup height constraints
- if self.config['PositiveHeightAreaFlag']:
- newcons[0, 0] = CPOSITIVE
- newcons[0, 1] = 0
- newcons[0, 2] = 0
-
- # Setup position constraints
- if self.config['QuotedPositionFlag']:
- newcons[1, 0] = CQUOTED
- newcons[1, 1] = min(x)
- newcons[1, 2] = max(x)
-
- # Setup positive FWHM constraints
- if self.config['PositiveFwhmFlag']:
- newcons[2, 0] = CPOSITIVE
- newcons[2, 1] = 0
- newcons[2, 2] = 0
-
- return largest, newcons
-
- def estimate_periodic_gauss(self, x, y):
- """Estimation of parameters for periodic gaussian curves:
- *number of peaks, distance between peaks, height, position of the
- first peak, fwhm*
-
- The functions detects all peaks, then computes the parameters the
- following way:
-
- - *distance*: average of distances between detected peaks
- - *height*: average height of detected peaks
- - *fwhm*: fwhm of the highest peak (in number of samples) if
- field ``'AutoFwhm'`` in :attr:`config` is ``True``, else take
- the default value (field ``'FwhmPoints'`` in :attr:`config`)
-
- :param x: Array of abscissa values
- :param y: Array of ordinate values (``y = f(x)``)
- :return: Tuple of estimated fit parameters and fit constraints.
- """
- yscaling = self.config.get('Yscaling', 1.0)
- if yscaling == 0:
- yscaling = 1.0
-
- bg = self.strip_bg(y)
-
- if self.config['AutoFwhm']:
- search_fwhm = guess_fwhm(y)
- else:
- search_fwhm = int(float(self.config['FwhmPoints']))
- search_sens = float(self.config['Sensitivity'])
-
- if search_fwhm < 3:
- search_fwhm = 3
-
- if search_sens < 1:
- search_sens = 1
-
- if len(y) > 1.5 * search_fwhm:
- peaks = peak_search(yscaling * y, fwhm=search_fwhm,
- sensitivity=search_sens)
- else:
- peaks = []
- npeaks = len(peaks)
- if not npeaks:
- fittedpar = []
- cons = numpy.zeros((len(fittedpar), 3), numpy.float64)
- return fittedpar, cons
-
- fittedpar = [0.0, 0.0, 0.0, 0.0, 0.0]
-
- # The number of peaks
- fittedpar[0] = npeaks
-
- # The separation between peaks in x units
- delta = 0.0
- height = 0.0
- for i in range(npeaks):
- height += y[int(peaks[i])] - bg[int(peaks[i])]
- if i != npeaks - 1:
- delta += (x[int(peaks[i + 1])] - x[int(peaks[i])])
-
- # delta between peaks
- if npeaks > 1:
- fittedpar[1] = delta / (npeaks - 1)
-
- # starting height
- fittedpar[2] = height / npeaks
-
- # position of the first peak
- fittedpar[3] = x[int(peaks[0])]
-
- # Estimate the fwhm
- fittedpar[4] = search_fwhm
-
- # setup constraints
- cons = numpy.zeros((5, 3), numpy.float64)
- cons[0, 0] = CFIXED # the number of gaussians
- if npeaks == 1:
- cons[1, 0] = CFIXED # the delta between peaks
- else:
- cons[1, 0] = CFREE
- j = 2
- # Setup height area constrains
- if not self.config['NoConstraintsFlag']:
- if self.config['PositiveHeightAreaFlag']:
- # POSITIVE = 1
- cons[j, 0] = CPOSITIVE
- cons[j, 1] = 0
- cons[j, 2] = 0
- j += 1
-
- # Setup position constrains
- if not self.config['NoConstraintsFlag']:
- if self.config['QuotedPositionFlag']:
- # QUOTED = 2
- cons[j, 0] = CQUOTED
- cons[j, 1] = min(x)
- cons[j, 2] = max(x)
- j += 1
-
- # Setup positive FWHM constrains
- if not self.config['NoConstraintsFlag']:
- if self.config['PositiveFwhmFlag']:
- # POSITIVE=1
- cons[j, 0] = CPOSITIVE
- cons[j, 1] = 0
- cons[j, 2] = 0
- j += 1
- return fittedpar, cons
-
- def configure(self, **kw):
- """Add new / unknown keyword arguments to :attr:`config`,
- update entries in :attr:`config` if the parameter name is a existing
- key.
-
- :param kw: Dictionary of keyword arguments.
- :return: Configuration dictionary :attr:`config`
- """
- if not kw.keys():
- return self.config
- for key in kw.keys():
- notdone = 1
- # take care of lower / upper case problems ...
- for config_key in self.config.keys():
- if config_key.lower() == key.lower():
- self.config[config_key] = kw[key]
- notdone = 0
- if notdone:
- self.config[key] = kw[key]
- return self.config
-
-fitfuns = FitTheories()
-
-THEORY = OrderedDict((
- ('Gaussians',
- FitTheory(description='Gaussian functions',
- function=functions.sum_gauss,
- parameters=('Height', 'Position', 'FWHM'),
- estimate=fitfuns.estimate_height_position_fwhm,
- configure=fitfuns.configure)),
- ('Lorentz',
- FitTheory(description='Lorentzian functions',
- function=functions.sum_lorentz,
- parameters=('Height', 'Position', 'FWHM'),
- estimate=fitfuns.estimate_height_position_fwhm,
- configure=fitfuns.configure)),
- ('Area Gaussians',
- FitTheory(description='Gaussian functions (area)',
- function=functions.sum_agauss,
- parameters=('Area', 'Position', 'FWHM'),
- estimate=fitfuns.estimate_agauss,
- configure=fitfuns.configure)),
- ('Area Lorentz',
- FitTheory(description='Lorentzian functions (area)',
- function=functions.sum_alorentz,
- parameters=('Area', 'Position', 'FWHM'),
- estimate=fitfuns.estimate_alorentz,
- configure=fitfuns.configure)),
- ('Pseudo-Voigt Line',
- FitTheory(description='Pseudo-Voigt functions',
- function=functions.sum_pvoigt,
- parameters=('Height', 'Position', 'FWHM', 'Eta'),
- estimate=fitfuns.estimate_pvoigt,
- configure=fitfuns.configure)),
- ('Area Pseudo-Voigt',
- FitTheory(description='Pseudo-Voigt functions (area)',
- function=functions.sum_apvoigt,
- parameters=('Area', 'Position', 'FWHM', 'Eta'),
- estimate=fitfuns.estimate_apvoigt,
- configure=fitfuns.configure)),
- ('Split Gaussian',
- FitTheory(description='Asymmetric gaussian functions',
- function=functions.sum_splitgauss,
- parameters=('Height', 'Position', 'LowFWHM',
- 'HighFWHM'),
- estimate=fitfuns.estimate_splitgauss,
- configure=fitfuns.configure)),
- ('Split Lorentz',
- FitTheory(description='Asymmetric lorentzian functions',
- function=functions.sum_splitlorentz,
- parameters=('Height', 'Position', 'LowFWHM', 'HighFWHM'),
- estimate=fitfuns.estimate_splitgauss,
- configure=fitfuns.configure)),
- ('Split Pseudo-Voigt',
- FitTheory(description='Asymmetric pseudo-Voigt functions',
- function=functions.sum_splitpvoigt,
- parameters=('Height', 'Position', 'LowFWHM',
- 'HighFWHM', 'Eta'),
- estimate=fitfuns.estimate_splitpvoigt,
- configure=fitfuns.configure)),
- ('Step Down',
- FitTheory(description='Step down function',
- function=functions.sum_stepdown,
- parameters=('Height', 'Position', 'FWHM'),
- estimate=fitfuns.estimate_stepdown,
- configure=fitfuns.configure)),
- ('Step Up',
- FitTheory(description='Step up function',
- function=functions.sum_stepup,
- parameters=('Height', 'Position', 'FWHM'),
- estimate=fitfuns.estimate_stepup,
- configure=fitfuns.configure)),
- ('Slit',
- FitTheory(description='Slit function',
- function=functions.sum_slit,
- parameters=('Height', 'Position', 'FWHM', 'BeamFWHM'),
- estimate=fitfuns.estimate_slit,
- configure=fitfuns.configure)),
- ('Atan',
- FitTheory(description='Arctan step up function',
- function=functions.atan_stepup,
- parameters=('Height', 'Position', 'Width'),
- estimate=fitfuns.estimate_stepup,
- configure=fitfuns.configure)),
- ('Hypermet',
- FitTheory(description='Hypermet functions',
- function=fitfuns.ahypermet, # customized version of functions.sum_ahypermet
- parameters=('G_Area', 'Position', 'FWHM', 'ST_Area',
- 'ST_Slope', 'LT_Area', 'LT_Slope', 'Step_H'),
- estimate=fitfuns.estimate_ahypermet,
- configure=fitfuns.configure)),
- # ('Periodic Gaussians',
- # FitTheory(description='Periodic gaussian functions',
- # function=functions.periodic_gauss,
- # parameters=('N', 'Delta', 'Height', 'Position', 'FWHM'),
- # estimate=fitfuns.estimate_periodic_gauss,
- # configure=fitfuns.configure))
- ('Degree 2 Polynomial',
- FitTheory(description='Degree 2 polynomial'
- '\ny = a*x^2 + b*x +c',
- function=fitfuns.poly,
- parameters=['a', 'b', 'c'],
- estimate=fitfuns.estimate_quadratic)),
- ('Degree 3 Polynomial',
- FitTheory(description='Degree 3 polynomial'
- '\ny = a*x^3 + b*x^2 + c*x + d',
- function=fitfuns.poly,
- parameters=['a', 'b', 'c', 'd'],
- estimate=fitfuns.estimate_cubic)),
- ('Degree 4 Polynomial',
- FitTheory(description='Degree 4 polynomial'
- '\ny = a*x^4 + b*x^3 + c*x^2 + d*x + e',
- function=fitfuns.poly,
- parameters=['a', 'b', 'c', 'd', 'e'],
- estimate=fitfuns.estimate_quartic)),
- ('Degree 5 Polynomial',
- FitTheory(description='Degree 5 polynomial'
- '\ny = a*x^5 + b*x^4 + c*x^3 + d*x^2 + e*x + f',
- function=fitfuns.poly,
- parameters=['a', 'b', 'c', 'd', 'e', 'f'],
- estimate=fitfuns.estimate_quintic)),
-))
-"""Dictionary of fit theories: fit functions and their associated estimation
-function, parameters list, configuration function and description.
-"""
-
-
-def test(a):
- from silx.math.fit import fitmanager
- x = numpy.arange(1000).astype(numpy.float64)
- p = [1500, 100., 50.0,
- 1500, 700., 50.0]
- y_synthetic = functions.sum_gauss(x, *p) + 1
-
- fit = fitmanager.FitManager(x, y_synthetic)
- fit.addtheory('Gaussians', functions.sum_gauss, ['Height', 'Position', 'FWHM'],
- a.estimate_height_position_fwhm)
- fit.settheory('Gaussians')
- fit.setbackground('Linear')
-
- fit.estimate()
- fit.runfit()
-
- y_fit = fit.gendata()
-
- print("Fit parameter names: %s" % str(fit.get_names()))
- print("Theoretical parameters: %s" % str(numpy.append([1, 0], p)))
- print("Fitted parameters: %s" % str(fit.get_fitted_parameters()))
-
- try:
- from silx.gui import qt
- from silx.gui.plot import plot1D
- app = qt.QApplication([])
-
- # Offset of 1 to see the difference in log scale
- plot1D(x, (y_synthetic + 1, y_fit), "Input data + 1, Fit")
-
- app.exec_()
- except ImportError:
- _logger.warning("Unable to load qt binding, can't plot results.")
-
-
-if __name__ == "__main__":
- test(fitfuns)
diff --git a/silx/math/fit/fittheory.py b/silx/math/fit/fittheory.py
deleted file mode 100644
index fa42e6b..0000000
--- a/silx/math/fit/fittheory.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-########################################################################### */
-"""
-This module defines the :class:`FitTheory` object that is used by
-:class:`silx.math.fit.FitManager` to define fit functions and background
-models.
-"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "09/08/2016"
-
-
-class FitTheory(object):
- """This class defines a fit theory, which consists of:
-
- - a model function, the actual function to be fitted
- - parameters names
- - an estimation function, that return the estimated initial parameters
- that serve as input for :func:`silx.math.fit.leastsq`
- - an optional configuration function, that can be used to modify
- configuration parameters to alter the behavior of the fit function
- and the estimation function
- - an optional derivative function, that replaces the default model
- derivative used in :func:`silx.math.fit.leastsq`
- """
- def __init__(self, function, parameters,
- estimate=None, configure=None, derivative=None,
- description=None, pymca_legacy=False, is_background=False):
- """
- :param function function: Actual function. See documentation for
- :attr:`function`.
- :param list[str] parameters: List of parameter names for the function.
- See documentation for :attr:`parameters`.
- :param function estimate: Optional estimation function.
- See documentation for :attr:`estimate`
- :param function configure: Optional configuration function.
- See documentation for :attr:`configure`
- :param function derivative: Optional custom derivative function.
- See documentation for :attr:`derivative`
- :param str description: Optional description string.
- See documentation for :attr:`description`
- :param bool pymca_legacy: Flag to indicate that the theory is a PyMca
- legacy theory. See documentation for :attr:`pymca_legacy`
- :param bool is_background: Flag to indicate that the theory is a
- background theory. This has implications regarding the function's
- signature, as explained in the documentation for :attr:`function`.
- """
- self.function = function
- """Regular fit functions must have the signature ``f(x, *params) -> y``,
- where *x* is a 1D array of values for the independent variable,
- *params* are the parameters to be fitted and *y* is the output array
- that we want to have the best fit to a series of data points.
-
- Background functions used by :class:`FitManager` must have a slightly
- different signature: ``f(x, y0, *params) -> bg``, where *y0* is the
- array of original data points and *bg* is the background signal that
- we want to subtract from the data array prior to fitting the regular
- fit function.
-
- The number of parameters must be the same as in :attr:`parameters`, or
- a multiple of this number if the function is defined as a sum of a
- variable number of base functions and if :attr:`estimate` is designed
- to be able to estimate the number of needed base functions.
- """
-
- self.parameters = parameters
- """List of parameters names.
-
- This list can contain the minimum number of parameters, if the
- function takes a variable number of parameters,
- and if the estimation function is responsible for finding the number
- of required parameters """
-
- self.estimate = estimate
- """The estimation function should have the following signature::
-
- f(x, y) -> (estimated_param, constraints)
-
- Parameters:
-
- - ``x`` is a sequence of values for the independent variable
- - ``y`` is a sequence of the same length as ``x`` containing the
- data to be fitted
-
- Return values:
-
- - ``estimated_param`` is a sequence of estimated fit parameters to
- be used as initial values for an iterative fit.
- - ``constraints`` is a sequence of shape *(n, 3)*, where *n* is the
- number of estimated parameters, containing the constraints for each
- parameter to be fitted. See :func:`silx.math.fit.leastsq` for more
- explanations about constraints."""
- if estimate is None:
- self.estimate = self.default_estimate
-
- self.configure = configure
- """The optional configuration function must conform to the signature
- ``f(**kw) -> dict`` (i.e it must accept any named argument and
- return a dictionary).
- It can be used to modify configuration parameters to alter the
- behavior of the fit function and the estimation function."""
-
- self.derivative = derivative
- """The optional derivative function must conform to the signature
- ``model_deriv(xdata, parameters, index)``, where parameters is a
- sequence with the current values of the fitting parameters, index is
- the fitting parameter index for which the the derivative has to be
- provided in the supplied array of xdata points."""
-
- self.description = description
- """Optional description string for this particular fit theory."""
-
- self.pymca_legacy = pymca_legacy
- """This attribute can be set to *True* to indicate that the theory
- is a PyMca legacy theory.
-
- This tells :mod:`silx.math.fit.fitmanager` that the signature of
- the estimate function is::
-
- f(x, y, bg, xscaling, yscaling) -> (estimated_param, constraints)
- """
-
- self.is_background = is_background
- """Flag to indicate that the theory is background theory.
-
- A background function is an secondary function that needs to be added
- to the main fit function to better fit the original data.
- If this flag is set to *True*, modules using this theory are informed
- that :attr:`function` has the signature ``f(x, y0, *params) -> bg``,
- instead of the usual fit function signature."""
-
- def default_estimate(self, x=None, y=None, bg=None):
- """Default estimate function. Return an array of *ones* as the
- initial estimated parameters, and set all constraints to zero
- (FREE)"""
- estimated_parameters = [1. for _ in self.parameters]
- estimated_constraints = [[0, 0, 0] for _ in self.parameters]
- return estimated_parameters, estimated_constraints
diff --git a/silx/math/fit/functions.pyx b/silx/math/fit/functions.pyx
deleted file mode 100644
index 1f78563..0000000
--- a/silx/math/fit/functions.pyx
+++ /dev/null
@@ -1,985 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-"""This module provides fit functions.
-
-List of fit functions:
------------------------
-
- - :func:`sum_gauss`
- - :func:`sum_agauss`
- - :func:`sum_splitgauss`
- - :func:`sum_fastagauss`
-
- - :func:`sum_apvoigt`
- - :func:`sum_pvoigt`
- - :func:`sum_splitpvoigt`
-
- - :func:`sum_lorentz`
- - :func:`sum_alorentz`
- - :func:`sum_splitlorentz`
-
- - :func:`sum_stepdown`
- - :func:`sum_stepup`
- - :func:`sum_slit`
-
- - :func:`sum_ahypermet`
- - :func:`sum_fastahypermet`
-
-Full documentation:
--------------------
-
-"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "16/08/2017"
-
-import logging
-import numpy
-
-_logger = logging.getLogger(__name__)
-
-cimport cython
-cimport silx.math.fit.functions_wrapper as functions_wrapper
-
-
-def erf(x):
- """Return the gaussian error function
-
- :param x: Independent variable where the gaussian error function is
- calculated
- :type x: numpy.ndarray or scalar
- :return: Gaussian error function ``y=erf(x)``
- :raise: IndexError if ``x`` is an empty array
- """
- cdef:
- double[::1] x_c
- double[::1] y_c
-
-
- # force list into numpy array
- if not hasattr(x, "shape"):
- x = numpy.asarray(x)
-
- for len_dim in x.shape:
- if len_dim == 0:
- raise IndexError("Cannot compute erf for an empty array")
-
- x_c = numpy.array(x, copy=False, dtype=numpy.float64, order='C').reshape(-1)
- y_c = numpy.empty(shape=(x_c.size,), dtype=numpy.float64)
-
- status = functions_wrapper.erf_array(&x_c[0], x_c.size, &y_c[0])
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def erfc(x):
- """Return the gaussian complementary error function
-
- :param x: Independent variable where the gaussian complementary error
- function is calculated
- :type x: numpy.ndarray or scalar
- :return: Gaussian complementary error function ``y=erfc(x)``
- :type rtype: numpy.ndarray
- :raise: IndexError if ``x`` is an empty array
- """
- cdef:
- double[::1] x_c
- double[::1] y_c
-
- # force list into numpy array
- if not hasattr(x, "shape"):
- x = numpy.asarray(x)
-
- for len_dim in x.shape:
- if len_dim == 0:
- raise IndexError("Cannot compute erfc for an empty array")
-
- x_c = numpy.array(x, copy=False, dtype=numpy.float64, order='C').reshape(-1)
- y_c = numpy.empty(shape=(x_c.size,), dtype=numpy.float64)
-
- status = functions_wrapper.erfc_array(&x_c[0], x_c.size, &y_c[0])
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_gauss(x, *params):
- """Return a sum of gaussian functions defined by *(height, centroid, fwhm)*,
- where:
-
- - *height* is the peak amplitude
- - *centroid* is the peak x-coordinate
- - *fwhm* is the full-width at half maximum
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of gaussian parameters (length must be a multiple
- of 3):
- *(height1, centroid1, fwhm1, height2, centroid2, fwhm2,...)*
- :return: Array of sum of gaussian functions at each ``x`` coordinate.
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No gaussian parameters specified. " +
- "At least 3 parameters are required.")
-
- # ensure float64 (double) type and 1D contiguous data layout in memory
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_gauss(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- # reshape y_c to match original, possibly unusual, data shape
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_agauss(x, *params):
- """Return a sum of gaussian functions defined by *(area, centroid, fwhm)*,
- where:
-
- - *area* is the area underneath the peak
- - *centroid* is the peak x-coordinate
- - *fwhm* is the full-width at half maximum
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of gaussian parameters (length must be a multiple
- of 3):
- *(area1, centroid1, fwhm1, area2, centroid2, fwhm2,...)*
- :return: Array of sum of gaussian functions at each ``x`` coordinate.
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No gaussian parameters specified. " +
- "At least 3 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_agauss(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_fastagauss(x, *params):
- """Return a sum of gaussian functions defined by *(area, centroid, fwhm)*,
- where:
-
- - *area* is the area underneath the peak
- - *centroid* is the peak x-coordinate
- - *fwhm* is the full-width at half maximum
-
- This implementation differs from :func:`sum_agauss` by the usage of a
- lookup table with precalculated exponential values. This might speed up
- the computation for large numbers of individual gaussian functions.
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of gaussian parameters (length must be a multiple
- of 3):
- *(area1, centroid1, fwhm1, area2, centroid2, fwhm2,...)*
- :return: Array of sum of gaussian functions at each ``x`` coordinate.
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No gaussian parameters specified. " +
- "At least 3 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_fastagauss(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_splitgauss(x, *params):
- """Return a sum of gaussian functions defined by *(area, centroid, fwhm1, fwhm2)*,
- where:
-
- - *height* is the peak amplitude
- - *centroid* is the peak x-coordinate
- - *fwhm1* is the full-width at half maximum for the distribution
- when ``x < centroid``
- - *fwhm2* is the full-width at half maximum for the distribution
- when ``x > centroid``
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of gaussian parameters (length must be a multiple
- of 4):
- *(height1, centroid1, fwhm11, fwhm21, height2, centroid2, fwhm12, fwhm22,...)*
- :return: Array of sum of split gaussian functions at each ``x`` coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No gaussian parameters specified. " +
- "At least 4 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_splitgauss(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_apvoigt(x, *params):
- """Return a sum of pseudo-Voigt functions, defined by *(area, centroid, fwhm,
- eta)*.
-
- The pseudo-Voigt profile ``PV(x)`` is an approximation of the Voigt
- profile using a linear combination of a Gaussian curve ``G(x)`` and a
- Lorentzian curve ``L(x)`` instead of their convolution.
-
- - *area* is the area underneath both G(x) and L(x)
- - *centroid* is the peak x-coordinate for both functions
- - *fwhm* is the full-width at half maximum of both functions
- - *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x)
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of pseudo-Voigt parameters (length must be a multiple
- of 4):
- *(area1, centroid1, fwhm1, eta1, area2, centroid2, fwhm2, eta2,...)*
- :return: Array of sum of pseudo-Voigt functions at each ``x`` coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 4 parameters are required.")
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_apvoigt(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_pvoigt(x, *params):
- """Return a sum of pseudo-Voigt functions, defined by *(height, centroid,
- fwhm, eta)*.
-
- The pseudo-Voigt profile ``PV(x)`` is an approximation of the Voigt
- profile using a linear combination of a Gaussian curve ``G(x)`` and a
- Lorentzian curve ``L(x)`` instead of their convolution.
-
- - *height* is the peak amplitude of G(x) and L(x)
- - *centroid* is the peak x-coordinate for both functions
- - *fwhm* is the full-width at half maximum of both functions
- - *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x)
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of pseudo-Voigt parameters (length must be a multiple
- of 4):
- *(height1, centroid1, fwhm1, eta1, height2, centroid2, fwhm2, eta2,...)*
- :return: Array of sum of pseudo-Voigt functions at each ``x`` coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 4 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_pvoigt(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_splitpvoigt(x, *params):
- """Return a sum of split pseudo-Voigt functions, defined by *(height,
- centroid, fwhm1, fwhm2, eta)*.
-
- The pseudo-Voigt profile ``PV(x)`` is an approximation of the Voigt
- profile using a linear combination of a Gaussian curve ``G(x)`` and a
- Lorentzian curve ``L(x)`` instead of their convolution.
-
- - *height* is the peak amplitudefor G(x) and L(x)
- - *centroid* is the peak x-coordinate for both functions
- - *fwhm1* is the full-width at half maximum of both functions
- when ``x < centroid``
- - *fwhm2* is the full-width at half maximum of both functions
- when ``x > centroid``
- - *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x)
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of pseudo-Voigt parameters (length must be a multiple
- of 5):
- *(height1, centroid1, fwhm11, fwhm21, eta1,...)*
- :return: Array of sum of split pseudo-Voigt functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 5 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_splitpvoigt(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_lorentz(x, *params):
- """Return a sum of Lorentz distributions, also known as Cauchy distribution,
- defined by *(height, centroid, fwhm)*.
-
- - *height* is the peak amplitude
- - *centroid* is the peak x-coordinate
- - *fwhm* is the full-width at half maximum
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of Lorentz parameters (length must be a multiple
- of 3):
- *(height1, centroid1, fwhm1,...)*
- :return: Array of sum Lorentz functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 3 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_lorentz(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_alorentz(x, *params):
- """Return a sum of Lorentz distributions, also known as Cauchy distribution,
- defined by *(area, centroid, fwhm)*.
-
- - *area* is the area underneath the peak
- - *centroid* is the peak x-coordinate for both functions
- - *fwhm* is the full-width at half maximum
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of Lorentz parameters (length must be a multiple
- of 3):
- *(area1, centroid1, fwhm1,...)*
- :return: Array of sum of Lorentz functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 3 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_alorentz(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_splitlorentz(x, *params):
- """Return a sum of split Lorentz distributions,
- defined by *(height, centroid, fwhm1, fwhm2)*.
-
- - *height* is the peak amplitude
- - *centroid* is the peak x-coordinate for both functions
- - *fwhm1* is the full-width at half maximum for ``x < centroid``
- - *fwhm2* is the full-width at half maximum for ``x > centroid``
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of Lorentz parameters (length must be a multiple
- of 4):
- *(height1, centroid1, fwhm11, fwhm21...)*
- :return: Array of sum of Lorentz functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 4 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_splitlorentz(
- &x_c[0], x.size,
- &params_c[0], params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_stepdown(x, *params):
- """Return a sum of stepdown functions.
- defined by *(height, centroid, fwhm)*.
-
- - *height* is the step's amplitude
- - *centroid* is the step's x-coordinate
- - *fwhm* is the full-width at half maximum for the derivative,
- which is a measure of the *sharpness* of the step-down's edge
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of stepdown parameters (length must be a multiple
- of 3):
- *(height1, centroid1, fwhm1,...)*
- :return: Array of sum of stepdown functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 3 parameters are required.")
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_stepdown(&x_c[0],
- x.size,
- &params_c[0],
- params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_stepup(x, *params):
- """Return a sum of stepup functions.
- defined by *(height, centroid, fwhm)*.
-
- - *height* is the step's amplitude
- - *centroid* is the step's x-coordinate
- - *fwhm* is the full-width at half maximum for the derivative,
- which is a measure of the *sharpness* of the step-up's edge
-
- :param x: Independent variable where the gaussians are calculated
- :type x: numpy.ndarray
- :param params: Array of stepup parameters (length must be a multiple
- of 3):
- *(height1, centroid1, fwhm1,...)*
- :return: Array of sum of stepup functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 3 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_stepup(&x_c[0],
- x.size,
- &params_c[0],
- params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_slit(x, *params):
- """Return a sum of slit functions.
- defined by *(height, position, fwhm, beamfwhm)*.
-
- - *height* is the slit's amplitude
- - *position* is the center of the slit's x-coordinate
- - *fwhm* is the full-width at half maximum of the slit
- - *beamfwhm* is the full-width at half maximum of the
- derivative, which is a measure of the *sharpness*
- of the edges of the slit
-
- :param x: Independent variable where the slits are calculated
- :type x: numpy.ndarray
- :param params: Array of slit parameters (length must be a multiple
- of 4):
- *(height1, centroid1, fwhm1, beamfwhm1,...)*
- :return: Array of sum of slit functions at each ``x``
- coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 4 parameters are required.")
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_slit(&x_c[0],
- x.size,
- &params_c[0],
- params_c.size,
- &y_c[0])
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_ahypermet(x, *params,
- gaussian_term=True, st_term=True, lt_term=True, step_term=True):
- """Return a sum of ahypermet functions.
- defined by *(area, position, fwhm, st_area_r, st_slope_r, lt_area_r,
- lt_slope_r, step_height_r)*.
-
- - *area* is the area underneath the gaussian peak
- - *position* is the center of the various peaks and the position of
- the step down
- - *fwhm* is the full-width at half maximum of the terms
- - *st_area_r* is factor between the gaussian area and the area of the
- short tail term
- - *st_slope_r* is a ratio related to the slope of the short tail
- in the low ``x`` values (the lower, the steeper)
- - *lt_area_r* is ratio between the gaussian area and the area of the
- long tail term
- - *lt_slope_r* is a ratio related to the slope of the long tail
- in the low ``x`` values (the lower, the steeper)
- - *step_height_r* is the ratio between the height of the step down
- and the gaussian height
-
- A hypermet function is a sum of four functions (terms):
-
- - a gaussian term
- - a long tail term
- - a short tail term
- - a step down term
-
- :param x: Independent variable where the hypermets are calculated
- :type x: numpy.ndarray
- :param params: Array of hypermet parameters (length must be a multiple
- of 8):
- *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1,
- lt_slope_r1, step_height_r1...)*
- :param gaussian_term: If ``True``, enable gaussian term. Default ``True``
- :param st_term: If ``True``, enable gaussian term. Default ``True``
- :param lt_term: If ``True``, enable gaussian term. Default ``True``
- :param step_term: If ``True``, enable gaussian term. Default ``True``
- :return: Array of sum of hypermet functions at each ``x`` coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 8 parameters are required.")
-
- # Sum binary flags to activate various terms of the equation
- tail_flags = 1 if gaussian_term else 0
- if st_term:
- tail_flags += 2
- if lt_term:
- tail_flags += 4
- if step_term:
- tail_flags += 8
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_ahypermet(&x_c[0],
- x.size,
- &params_c[0],
- params_c.size,
- &y_c[0],
- tail_flags)
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def sum_fastahypermet(x, *params,
- gaussian_term=True, st_term=True,
- lt_term=True, step_term=True):
- """Return a sum of hypermet functions defined by *(area, position, fwhm,
- st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r)*.
-
- - *area* is the area underneath the gaussian peak
- - *position* is the center of the various peaks and the position of
- the step down
- - *fwhm* is the full-width at half maximum of the terms
- - *st_area_r* is factor between the gaussian area and the area of the
- short tail term
- - *st_slope_r* is a parameter related to the slope of the short tail
- in the low ``x`` values (the lower, the steeper)
- - *lt_area_r* is factor between the gaussian area and the area of the
- long tail term
- - *lt_slope_r* is a parameter related to the slope of the long tail
- in the low ``x`` values (the lower, the steeper)
- - *step_height_r* is the factor between the height of the step down
- and the gaussian height
-
- A hypermet function is a sum of four functions (terms):
-
- - a gaussian term
- - a long tail term
- - a short tail term
- - a step down term
-
- This function differs from :func:`sum_ahypermet` by the use of a lookup
- table for calculating exponentials. This offers better performance when
- calculating many functions for large ``x`` arrays.
-
- :param x: Independent variable where the hypermets are calculated
- :type x: numpy.ndarray
- :param params: Array of hypermet parameters (length must be a multiple
- of 8):
- *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1,
- lt_slope_r1, step_height_r1...)*
- :param gaussian_term: If ``True``, enable gaussian term. Default ``True``
- :param st_term: If ``True``, enable gaussian term. Default ``True``
- :param lt_term: If ``True``, enable gaussian term. Default ``True``
- :param step_term: If ``True``, enable gaussian term. Default ``True``
- :return: Array of sum of hypermet functions at each ``x`` coordinate
- """
- cdef:
- double[::1] x_c
- double[::1] params_c
- double[::1] y_c
-
- if not len(params):
- raise IndexError("No parameters specified. " +
- "At least 8 parameters are required.")
-
- # Sum binary flags to activate various terms of the equation
- tail_flags = 1 if gaussian_term else 0
- if st_term:
- tail_flags += 2
- if lt_term:
- tail_flags += 4
- if step_term:
- tail_flags += 8
-
- # TODO (maybe):
- # Set flags according to params, to move conditional
- # branches out of the C code.
- # E.g., set st_term = False if any of the st_slope_r params
- # (params[8*i + 4]) is 0, to prevent division by 0. Same thing for
- # lt_slope_r (params[8*i + 6]) and lt_term.
-
- x_c = numpy.array(x,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- params_c = numpy.array(params,
- copy=False,
- dtype=numpy.float64,
- order='C').reshape(-1)
- y_c = numpy.empty(shape=(x.size,),
- dtype=numpy.float64)
-
- status = functions_wrapper.sum_fastahypermet(&x_c[0],
- x.size,
- &params_c[0],
- params_c.size,
- &y_c[0],
- tail_flags)
-
- if status:
- raise IndexError("Wrong number of parameters for function")
-
- return numpy.asarray(y_c).reshape(x.shape)
-
-
-def atan_stepup(x, a, b, c):
- """
- Step up function using an inverse tangent.
-
- :param x: Independent variable where the function is calculated
- :type x: numpy array
- :param a: Height of the step up
- :param b: Center of the step up
- :param c: Parameter related to the slope of the step. A lower ``c``
- value yields a sharper step.
- :return: ``a * (0.5 + (arctan((x - b) / c) / pi))``
- :rtype: numpy array
- """
- if not hasattr(x, "shape"):
- x = numpy.array(x)
- return a * (0.5 + (numpy.arctan((1.0 * x - b) / c) / numpy.pi))
-
-
-def periodic_gauss(x, *pars):
- """
- Return a sum of gaussian functions defined by
- *(npeaks, delta, height, centroid, fwhm)*,
- where:
-
- - *npeaks* is the number of gaussians peaks
- - *delta* is the constant distance between 2 peaks
- - *height* is the peak amplitude of all the gaussians
- - *centroid* is the peak x-coordinate of the first gaussian
- - *fwhm* is the full-width at half maximum for all the gaussians
-
- :param x: Independent variable where the function is calculated
- :param pars: *(npeaks, delta, height, centroid, fwhm)*
- :return: Sum of ``npeaks`` gaussians
- """
-
- if not len(pars):
- raise IndexError("No parameters specified. " +
- "At least 5 parameters are required.")
-
- newpars = numpy.zeros((pars[0], 3), numpy.float64)
- for i in range(int(pars[0])):
- newpars[i, 0] = pars[2]
- newpars[i, 1] = pars[3] + i * pars[1]
- newpars[:, 2] = pars[4]
- return sum_gauss(x, newpars)
diff --git a/silx/math/fit/functions/include/functions.h b/silx/math/fit/functions/include/functions.h
deleted file mode 100644
index de4209b..0000000
--- a/silx/math/fit/functions/include/functions.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#ifndef FITFUNCTIONS_H
-#define FITFUNCTIONS_H
-
-/* Helper functions */
-int test_params(int len_params, int len_params_one_function, char* fun_name, char* param_names);
-double myerfc(double x);
-double myerf(double x);
-int erfc_array(double* x, int len_x, double* y);
-int erf_array(double* x, int len_x, double* y);
-
-/* Background functions */
-void snip1d(double *data, int size, int width);
-//void snip1d_multiple(double *data, int n_channels, int snip_width, int n_spectra);
-void snip2d(double *data, int nrows, int ncolumns, int width);
-void snip3d(double *data, int nx, int ny, int nz, int width);
-
-int strip(double* input, long len_input, double c, long niter, int deltai,
- long* anchors, long len_anchors, double* output);
-
-/* Smoothing functions */
-
-int SavitskyGolay(double* input, long len_input, int npoints, double* output);
-
-/* Fit functions */
-int sum_gauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y);
-int sum_agauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y);
-int sum_fastagauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y);
-int sum_splitgauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y);
-
-int sum_apvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y);
-int sum_pvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y);
-int sum_splitpvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y);
-
-int sum_lorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y);
-int sum_alorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y);
-int sum_splitlorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y);
-
-int sum_stepdown(double* x, int len_x, double* pdstep, int len_pdstep, double* y);
-int sum_stepup(double* x, int len_x, double* pustep, int len_pustep, double* y);
-int sum_slit(double* x, int len_x, double* pslit, int len_pslit, double* y);
-
-int sum_ahypermet(double* x, int len_x, double* phypermet, int len_phypermet, double* y, int tail_flags);
-int sum_fastahypermet(double* x, int len_x, double* phypermet, int len_phypermet, double* y, int tail_flags);
-
-#endif /* #define FITFUNCTIONS_H */
diff --git a/silx/math/fit/functions/src/funs.c b/silx/math/fit/functions/src/funs.c
deleted file mode 100644
index aae173f..0000000
--- a/silx/math/fit/functions/src/funs.c
+++ /dev/null
@@ -1,1265 +0,0 @@
-#/*##########################################################################
-# Copyright (c) 2004-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-/*
- This file provides fit functions.
-
- It is adapted from PyMca source file "SpecFitFuns.c". The main difference
- with the original code is that this code does not handle the python
- wrapping, which is done elsewhere using cython.
-
- Authors: V.A. Sole, P. Knobel
- License: MIT
- Last modified: 17/06/2016
-*/
-#include <math.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "functions.h"
-
-#ifndef M_PI
-#define M_PI 3.1415926535
-#endif
-
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-
-#if defined(_WIN32)
-#define erf myerf
-#define erfc myerfc
-#endif
-
-#define LOG2 0.69314718055994529
-
-
-int test_params(int len_params,
- int len_params_one_function,
- char* fun_name,
- char* param_names)
-{
- if (len_params % len_params_one_function) {
- printf("[%s]Error: Number of parameters must be a multiple of %d.",
- fun_name, len_params_one_function);
- printf("\nParameters expected for %s: %s\n",
- fun_name, param_names);
- return(1);
- }
- if (len_params == 0) {
- printf("[%s]Error: No parameters specified.", fun_name);
- printf("\nParameters expected for %s: %s\n",
- fun_name, param_names);
- return(1);
- }
- return(0);
-}
-
-/* Complementary error function for a single value*/
-double myerfc(double x)
-{
- double z;
- double t;
- double r;
-
- z=fabs(x);
- t=1.0/(1.0+0.5*z);
- r=t * exp(-z * z - 1.26551223 + t * (1.00002368 + t * (0.3740916 +
- t * (0.09678418 + t * (-0.18628806 + t * (0.27886807 + t * (-1.13520398 +
- t * (1.48851587 + t * (-0.82215223+t*0.17087277)))))))));
- if (x<0)
- r=2.0-r;
- return (r);
-}
-
-/* Gauss error function for a single value*/
-double myerf(double x)
-{
- return (1.0 - myerfc(x));
-}
-
-/* Gauss error function for an array
- y[i]=erf(x[i])
- returns status code 0
-*/
-int erf_array(double* x, int len_x, double* y)
-{
- int j;
- for (j=0; j<len_x; j++) {
- y[j] = erf(x[j]);
- }
- return(0);
-}
-
-/* Complementary error function for an array
- y[i]=erfc(x[i])
- returns status code 0*/
-int erfc_array(double* x, int len_x, double* y)
-{
- int j;
- for (j=0; j<len_x; j++) {
- y[j] = erfc(x[j]);
- }
- return(0);
-}
-
-/* Use lookup table for fast exp computation */
-double fastexp(double x)
-{
- int expindex;
- static double EXP[5000] = {0.0};
- int i;
-
-/*initialize */
- if (EXP[0] < 1){
- for (i=0;i<5000;i++){
- EXP[i] = exp(-0.01 * i);
- }
- }
-/*calculate*/
- if (x < 0){
- x = -x;
- if (x < 50){
- expindex = (int) (x * 100);
- return EXP[expindex]*(1.0 - (x - 0.01 * expindex)) ;
- }else if (x < 100) {
- expindex = (int) (x * 10);
- return pow(EXP[expindex]*(1.0 - (x - 0.1 * expindex)),10) ;
- }else if (x < 1000){
- expindex = (int) x;
- return pow(EXP[expindex]*(1.0 - (x - expindex)),20) ;
- }else if (x < 10000){
- expindex = (int) (x * 0.1);
- return pow(EXP[expindex]*(1.0 - (x - 10.0 * expindex)),30) ;
- }else{
- return 0;
- }
- }else{
- if (x < 50){
- expindex = (int) (x * 100);
- return 1.0/EXP[expindex]*(1.0 - (x - 0.01 * expindex)) ;
- }else if (x < 100) {
- expindex = (int) (x * 10);
- return pow(EXP[expindex]*(1.0 - (x - 0.1 * expindex)),-10) ;
- }else{
- return exp(x);
- }
- }
-}
-
-
-/* sum_gauss
- Sum of gaussian functions, defined by (height, centroid, fwhm)
-
- *height* is the peak amplitude
- *centroid* is the peak x-coordinate
- *fwhm* is the full-width at half maximum
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pvoigt: Array of gaussian parameters:
- (height1, centroid1, fwhm1, height2, centroid2, fwhm2,...)
- - len_pgauss: Number of elements in the pgauss array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_gauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y)
-{
- int i, j;
- double dhelp, inv_two_sqrt_two_log2, sigma;
- double fwhm, centroid, height;
-
- if (test_params(len_pgauss, 3, "sum_gauss", "height, centroid, fwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pgauss/3; i++) {
- height = pgauss[3*i];
- centroid = pgauss[3*i+1];
- fwhm = pgauss[3*i+2];
-
- sigma = fwhm * inv_two_sqrt_two_log2;
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid) / sigma;
- if (dhelp <= 20) {
- y[j] += height * exp (-0.5 * dhelp * dhelp);
- }
- }
- }
- return(0);
-}
-
-/* sum_agauss
- Sum of gaussian functions defined by (area, centroid, fwhm)
-
- *area* is the area underneath the peak
- *centroid* is the peak x-coordinate
- *fwhm* is the full-width at half maximum
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pgauss: Array of gaussian parameters:
- (area1, centroid1, fwhm1, area2, centroid2, fwhm2,...)
- - len_pgauss: Number of elements in the pgauss array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_agauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y)
-{
- int i, j;
- double dhelp, height, sqrt2PI, sigma, inv_two_sqrt_two_log2;
- double fwhm, centroid, area;
-
- if (test_params(len_pgauss, 3, "sum_agauss", "area, centroid, fwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
- sqrt2PI = sqrt(2.0*M_PI);
-
- for (i=0; i<len_pgauss/3; i++) {
- area = pgauss[3*i];
- centroid = pgauss[3*i+1];
- fwhm = pgauss[3*i+2];
-
- sigma = fwhm * inv_two_sqrt_two_log2;
- height = area / (sigma * sqrt2PI);
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid)/sigma;
- if (dhelp <= 35) {
- y[j] += height * exp (-0.5 * dhelp * dhelp);
- }
- }
- }
- return(0);
-}
-
-
-/* sum_fastagauss
- Sum of gaussian functions defined by (area, centroid, fwhm).
- This implementation uses a lookup table of precalculated exp values
- and a limited development (exp(-x) = 1 - x for small values of x)
-
- *area* is the area underneath the peak
- *centroid* is the peak x-coordinate
- *fwhm* is the full-width at half maximum
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pgauss: Array of gaussian parameters:
- (area1, centroid1, fwhm1, area2, centroid2, fwhm2,...)
- - len_pgauss: Number of elements in the pgauss array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-
-int sum_fastagauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y)
-{
- int i, j, expindex;
- double dhelp, height, sqrt2PI, sigma, inv_two_sqrt_two_log2;
- double fwhm, centroid, area;
- static double EXP[5000];
-
- if (test_params(len_pgauss, 3, "sum_fastagauss", "area, centroid, fwhm")) {
- return(1);
- }
-
- if (EXP[0] < 1){
- for (i=0; i<5000; i++){
- EXP[i] = exp(-0.01 * i);
- }
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
- sqrt2PI = sqrt(2.0*M_PI);
-
- for (i=0; i<len_pgauss/3; i++) {
- area = pgauss[3*i];
- centroid = pgauss[3*i+1];
- fwhm = pgauss[3*i+2];
-
- sigma = fwhm * inv_two_sqrt_two_log2;
- height = area / (sigma * sqrt2PI);
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid)/sigma;
- if (dhelp <= 15){
- dhelp = 0.5 * dhelp * dhelp;
- if (dhelp < 50){
- expindex = (int) (dhelp * 100);
- y[j] += height * EXP[expindex] * (1.0 - (dhelp - 0.01 * expindex));
- }
- else if (dhelp < 100) {
- expindex = (int) (dhelp * 10);
- y[j] += height * pow(EXP[expindex] * (1.0 - (dhelp - 0.1 * expindex)), 10);
- }
- else if (dhelp < 1000){
- expindex = (int) (dhelp);
- y[j] += height * pow(EXP[expindex] * (1.0 - (dhelp - expindex)), 20);
- }
- }
- }
- }
- return(0);
-}
-
-/* sum_splitgauss
- Sum of split gaussian functions, defined by (height, centroid, fwhm1, fwhm2)
-
- *height* is the peak amplitude
- *centroid* is the peak x-coordinate
- *fwhm1* is the full-width at half maximum of the left half of the curve (x < centroid)
- *fwhm1* is the full-width at half maximum of the right half of the curve (x > centroid)
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pgauss: Array of gaussian parameters:
- (height1, centroid1, fwhm11, fwhm21, height2, centroid2, fwhm12, fwhm22,...)
- - len_pgauss: Number of elements in the pgauss array. Must be
- a multiple of 4.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_splitgauss(double* x, int len_x, double* pgauss, int len_pgauss, double* y)
-{
- int i, j;
- double dhelp, inv_two_sqrt_two_log2, sigma1, sigma2;
- double fwhm1, fwhm2, centroid, height;
-
- if (test_params(len_pgauss, 4, "sum_splitgauss", "height, centroid, fwhm1, fwhm2")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pgauss/4; i++) {
- height = pgauss[4*i];
- centroid = pgauss[4*i+1];
- fwhm1 = pgauss[4*i+2];
- fwhm2 = pgauss[4*i+3];
-
- sigma1 = fwhm1 * inv_two_sqrt_two_log2;
- sigma2 = fwhm2 * inv_two_sqrt_two_log2;
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid);
- if (dhelp > 0) {
- /* Use fwhm2 when x > centroid */
- dhelp = dhelp / sigma2;
- }
- else {
- /* Use fwhm1 when x < centroid */
- dhelp = dhelp / sigma1;
- }
-
- if (dhelp <= 20) {
- y[j] += height * exp (-0.5 * dhelp * dhelp);
- }
- }
- }
- return(0);
-}
-
-/* sum_apvoigt
- Sum of pseudo-Voigt functions, defined by (area, centroid, fwhm, eta).
-
- The pseudo-Voigt profile PV(x) is an approximation of the Voigt profile
- using a linear combination of a Gaussian curve G(x) and a Lorentzian curve
- L(x) instead of their convolution.
-
- *area* is the area underneath both G(x) and L(x)
- *centroid* is the peak x-coordinate for both functions
- *fwhm* is the full-width at half maximum of both functions
- *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x)
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pvoigt: Array of Voigt function parameters:
- (area1, centroid1, fwhm1, eta1, area2, centroid2, fwhm2, eta2,...)
- - len_voigt: Number of elements in the pvoigt array. Must be
- a multiple of 4.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_apvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y)
-{
- int i, j;
- double dhelp, inv_two_sqrt_two_log2, sqrt2PI, sigma, height;
- double area, centroid, fwhm, eta;
-
- if (test_params(len_pvoigt, 4, "sum_apvoigt", "area, centroid, fwhm, eta")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
- sqrt2PI = sqrt(2.0*M_PI);
-
-
- for (i=0; i<len_pvoigt/4; i++) {
- area = pvoigt[4*i];
- centroid = pvoigt[4*i+1];
- fwhm = pvoigt[4*i+2];
- eta = pvoigt[4*i+3];
-
- sigma = fwhm * inv_two_sqrt_two_log2;
- height = area / (sigma * sqrt2PI);
-
- for (j=0; j<len_x; j++) {
- /* Lorentzian term */
- dhelp = (x[j] - centroid) / (0.5 * fwhm);
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += eta * (area / (0.5 * M_PI * fwhm * dhelp));
-
- /* Gaussian term */
- dhelp = (x[j] - centroid) / sigma;
- if (dhelp <= 35) {
- y[j] += (1.0 - eta) * height * exp (-0.5 * dhelp * dhelp);
- }
- }
- }
- return(0);
-}
-
-/* sum_pvoigt
- Sum of pseudo-Voigt functions, defined by (height, centroid, fwhm, eta).
-
- The pseudo-Voigt profile PV(x) is an approximation of the Voigt profile
- using a linear combination of a Gaussian curve G(x) and a Lorentzian curve
- L(x) instead of their convolution.
-
- *height* is the peak amplitude of G(x) and L(x)
- *centroid* is the peak x-coordinate for both functions
- *fwhm* is the full-width at half maximum of both functions
- *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x)
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pvoigt: Array of Voigt function parameters:
- (height1, centroid1, fwhm1, eta1, height2, centroid2, fwhm2, eta2,...)
- - len_voigt: Number of elements in the pvoigt array. Must be
- a multiple of 4.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_pvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y)
-{
- int i, j;
- double dhelp, inv_two_sqrt_two_log2, sigma;
- double height, centroid, fwhm, eta;
-
- if (test_params(len_pvoigt, 4, "sum_pvoigt", "height, centroid, fwhm, eta")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pvoigt/4; i++) {
- height = pvoigt[4*i];
- centroid = pvoigt[4*i+1];
- fwhm = pvoigt[4*i+2];
- eta = pvoigt[4*i+3];
-
- sigma = fwhm * inv_two_sqrt_two_log2;
-
- for (j=0; j<len_x; j++) {
- /* Lorentzian term */
- dhelp = (x[j] - centroid) / (0.5 * fwhm);
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += eta * height / dhelp;
-
- /* Gaussian term */
- dhelp = (x[j] - centroid) / sigma;
- if (dhelp <= 35) {
- y[j] += (1.0 - eta) * height * exp (-0.5 * dhelp * dhelp);
- }
- }
- }
- return(0);
-}
-
-/* sum_splitpvoigt
- Sum of split pseudo-Voigt functions, defined by
- (height, centroid, fwhm1, fwhm2, eta).
-
- The pseudo-Voigt profile PV(x) is an approximation of the Voigt profile
- using a linear combination of a Gaussian curve G(x) and a Lorentzian curve
- L(x) instead of their convolution.
-
- *height* is the peak amplitude of G(x) and L(x)
- *centroid* is the peak x-coordinate for both functions
- *fwhm1* is the full-width at half maximum of both functions for x < centroid
- *fwhm2* is the full-width at half maximum of both functions for x > centroid
- *eta* is the Lorentz factor: PV(x) = eta * L(x) + (1 - eta) * G(x)
-
- Parameters:
- -----------
-
- - x: Independant variable where the gaussians are calculated.
- - len_x: Number of elements in the x array.
- - pvoigt: Array of Voigt function parameters:
- (height1, centroid1, fwhm11, fwhm21, eta1, ...)
- - len_voigt: Number of elements in the pvoigt array. Must be
- a multiple of 5.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_splitpvoigt(double* x, int len_x, double* pvoigt, int len_pvoigt, double* y)
-{
- int i, j;
- double dhelp, inv_two_sqrt_two_log2, x_minus_centroid, sigma1, sigma2;
- double height, centroid, fwhm1, fwhm2, eta;
-
- if (test_params(len_pvoigt, 5, "sum_splitpvoigt", "height, centroid, fwhm1, fwhm2, eta")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- inv_two_sqrt_two_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pvoigt/5; i++) {
- height = pvoigt[5*i];
- centroid = pvoigt[5*i+1];
- fwhm1 = pvoigt[5*i+2];
- fwhm2 = pvoigt[5*i+3];
- eta = pvoigt[5*i+4];
-
- sigma1 = fwhm1 * inv_two_sqrt_two_log2;
- sigma2 = fwhm2 * inv_two_sqrt_two_log2;
-
- for (j=0; j<len_x; j++) {
- x_minus_centroid = (x[j] - centroid);
-
- /* Use fwhm2 when x > centroid */
- if (x_minus_centroid > 0) {
- /* Lorentzian term */
- dhelp = x_minus_centroid / (0.5 * fwhm2);
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += eta * height / dhelp;
-
- /* Gaussian term */
- dhelp = x_minus_centroid / sigma2;
- if (dhelp <= 35) {
- y[j] += (1.0 - eta) * height * exp (-0.5 * dhelp * dhelp);
- }
- }
- /* Use fwhm1 when x < centroid */
- else {
- /* Lorentzian term */
- dhelp = x_minus_centroid / (0.5 * fwhm1);
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += eta * height / dhelp;
-
- /* Gaussian term */
- dhelp = x_minus_centroid / sigma1;
- if (dhelp <= 35) {
- y[j] += (1.0 - eta) * height * exp (-0.5 * dhelp * dhelp);
- }
- }
- }
- }
- return(0);
-}
-
-/* sum_lorentz
- Sum of Lorentz functions, defined by (height, centroid, fwhm).
-
- *height* is the peak amplitude
- *centroid* is the peak's x-coordinate
- *fwhm* is the full-width at half maximum
-
- Parameters:
- -----------
-
- - x: Independant variable where the Lorentzians are calculated.
- - len_x: Number of elements in the x array.
- - plorentz: Array of lorentz function parameters:
- (height1, centroid1, fwhm1, ...)
- - len_lorentz: Number of elements in the plorentz array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_lorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y)
-{
- int i, j;
- double dhelp;
- double height, centroid, fwhm;
-
- if (test_params(len_plorentz, 3, "sum_lorentz", "height, centroid, fwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- for (i=0; i<len_plorentz/3; i++) {
- height = plorentz[3*i];
- centroid = plorentz[3*i+1];
- fwhm = plorentz[3*i+2];
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid) / (0.5 * fwhm);
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += height / dhelp;
- }
- }
- return(0);
-}
-
-
-/* sum_alorentz
- Sum of Lorentz functions, defined by (area, centroid, fwhm).
-
- *area* is the area underneath the peak
- *centroid* is the peak's x-coordinate
- *fwhm* is the full-width at half maximum
-
- Parameters:
- -----------
-
- - x: Independant variable where the Lorentzians are calculated.
- - len_x: Number of elements in the x array.
- - plorentz: Array of lorentz function parameters:
- (area1, centroid1, fwhm1, ...)
- - len_lorentz: Number of elements in the plorentz array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_alorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y)
-{
- int i, j;
- double dhelp;
- double area, centroid, fwhm;
-
- if (test_params(len_plorentz, 3, "sum_alorentz", "area, centroid, fwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- for (i=0; i<len_plorentz/3; i++) {
- area = plorentz[3*i];
- centroid = plorentz[3*i+1];
- fwhm = plorentz[3*i+2];
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid) / (0.5 * fwhm);
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += area / (0.5 * M_PI * fwhm * dhelp);
- }
- }
- return(0);
-}
-
-
-/* sum_splitlorentz
- Sum of Lorentz functions, defined by (height, centroid, fwhm1, fwhm2).
-
- *height* is the peak amplitude
- *centroid* is the peak's x-coordinate
- *fwhm1* is the full-width at half maximum for x < centroid
- *fwhm2* is the full-width at half maximum for x > centroid
-
- Parameters:
- -----------
-
- - x: Independant variable where the Lorentzians are calculated.
- - len_x: Number of elements in the x array.
- - plorentz: Array of lorentz function parameters:
- (height1, centroid1, fwhm11, fwhm21 ...)
- - len_lorentz: Number of elements in the plorentz array. Must be
- a multiple of 4.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_splitlorentz(double* x, int len_x, double* plorentz, int len_plorentz, double* y)
-{
- int i, j;
- double dhelp;
- double height, centroid, fwhm1, fwhm2;
-
- if (test_params(len_plorentz, 4, "sum_splitlorentz", "height, centroid, fwhm1, fwhm2")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- for (i=0; i<len_plorentz/4; i++) {
- height = plorentz[4*i];
- centroid = plorentz[4*i+1];
- fwhm1 = plorentz[4*i+2];
- fwhm2 = plorentz[4*i+3];
-
- for (j=0; j<len_x; j++) {
- dhelp = (x[j] - centroid);
- if (dhelp>0) {
- dhelp = dhelp / (0.5 * fwhm2);
- }
- else {
- dhelp = dhelp / (0.5 * fwhm1);
- }
- dhelp = 1.0 + (dhelp * dhelp);
- y[j] += height / dhelp;
- }
- }
- return(0);
-}
-
-/* sum_stepdown
- Sum of stepdown functions, defined by (height, centroid, fwhm).
-
- *height* is the step amplitude
- *centroid* is the step's x-coordinate
- *fwhm* is the full-width at half maximum of the derivative
-
- Parameters:
- -----------
-
- - x: Independant variable where the stepdown functions are calculated.
- - len_x: Number of elements in the x array.
- - pdstep: Array of downstpe function parameters:
- (height1, centroid1, fwhm1, ...)
- - len_pdstep: Number of elements in the pdstep array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_stepdown(double* x, int len_x, double* pdstep, int len_pdstep, double* y)
-{
- int i, j;
- double dhelp, sqrt2_inv_2_sqrt_two_log2 ;
- double height, centroid, fwhm;
-
- if (test_params(len_pdstep, 3, "sum_stepdown", "height, centroid, fwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- sqrt2_inv_2_sqrt_two_log2 = sqrt(2.0) / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pdstep/3; i++) {
- height = pdstep[3*i];
- centroid = pdstep[3*i+1];
- fwhm = pdstep[3*i+2];
-
- for (j=0; j<len_x; j++) {
- dhelp = fwhm * sqrt2_inv_2_sqrt_two_log2;
- dhelp = (x[j] - centroid) / dhelp;
- y[j] += height * 0.5 * erfc(dhelp);
- }
- }
- return(0);
-}
-
-/* sum_stepup
- Sum of stepup functions, defined by (height, centroid, fwhm).
-
- *height* is the step amplitude
- *centroid* is the step's x-coordinate
- *fwhm* is the full-width at half maximum of the derivative
-
- Parameters:
- -----------
-
- - x: Independant variable where the stepup functions are calculated.
- - len_x: Number of elements in the x array.
- - pustep: Array of stepdown function parameters:
- (height1, centroid1, fwhm1, ...)
- - len_pustep: Number of elements in the pustep array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_stepup(double* x, int len_x, double* pustep, int len_pustep, double* y)
-{
- int i, j;
- double dhelp, sqrt2_inv_2_sqrt_two_log2 ;
- double height, centroid, fwhm;
-
- if (test_params(len_pustep, 3, "sum_stepup", "height, centroid, fwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- sqrt2_inv_2_sqrt_two_log2 = sqrt(2.0) / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pustep/3; i++) {
- height = pustep[3*i];
- centroid = pustep[3*i+1];
- fwhm = pustep[3*i+2];
-
- for (j=0; j<len_x; j++) {
- dhelp = fwhm * sqrt2_inv_2_sqrt_two_log2;
- dhelp = (x[j] - centroid) / dhelp;
- y[j] += height * 0.5 * (1.0 + erf(dhelp));
- }
- }
- return(0);
-}
-
-
-/* sum_slit
- Sum of slit functions, defined by (height, position, fwhm, beamfwhm).
-
- *height* is the slit height
- *position* is the slit's center x-coordinate
- *fwhm* is the full-width at half maximum of the slit
- *beamfwhm* is the full-width at half maximum of derivative's peaks
-
- Parameters:
- -----------
-
- - x: Independant variable where the slit functions are calculated.
- - len_x: Number of elements in the x array.
- - pslit: Array of slit function parameters:
- (height1, centroid1, fwhm1, beamfwhm1 ...)
- - len_pslit: Number of elements in the pslit array. Must be
- a multiple of 3.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_slit(double* x, int len_x, double* pslit, int len_pslit, double* y)
-{
- int i, j;
- double dhelp, dhelp1, dhelp2, sqrt2_inv_2_sqrt_two_log2, centroid1, centroid2;
- double height, position, fwhm, beamfwhm;
-
- if (test_params(len_pslit, 4, "sum_slit", "height, centroid, fwhm, beamfwhm")) {
- return(1);
- }
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- sqrt2_inv_2_sqrt_two_log2 = sqrt(2.0) / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_pslit/4; i++) {
- height = pslit[4*i];
- position = pslit[4*i+1];
- fwhm = pslit[4*i+2];
- beamfwhm = pslit[4*i+3];
-
- centroid1 = position - 0.5 * fwhm;
- centroid2 = position + 0.5 * fwhm;
-
- for (j=0; j<len_x; j++) {
- dhelp = beamfwhm * sqrt2_inv_2_sqrt_two_log2;
- dhelp1 = (x[j] - centroid1) / dhelp;
- dhelp2 = (x[j] - centroid2) / dhelp;
- y[j] += height * 0.25 * (1.0 + erf(dhelp1)) * erfc(dhelp2);
- }
- }
- return(0);
-}
-
-
-/* sum_ahypermet
- Sum of hypermet functions, defined by
- (area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r).
-
- - *area* is the area underneath the gaussian peak
- - *position* is the center of the various peaks and the position of
- the step down
- - *fwhm* is the full-width at half maximum of the terms
- - *st_area_r* is factor between the gaussian area and the area of the
- short tail term
- - *st_slope_r* is a parameter related to the slope of the short tail
- in the low ``x`` values (the lower, the steeper)
- - *lt_area_r* is factor between the gaussian area and the area of the
- long tail term
- - *lt_slope_r* is a parameter related to the slope of the long tail
- in the low ``x`` values (the lower, the steeper)
- - *step_height_r* is the factor between the height of the step down
- and the gaussian height
-
- Parameters:
- -----------
-
- - x: Independant variable where the functions are calculated.
- - len_x: Number of elements in the x array.
- - phypermet: Array of hypermet function parameters:
- *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1,
- lt_slope_r1, step_height_r1, ...)*
- - len_phypermet: Number of elements in the phypermet array. Must be
- a multiple of 8.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
- - tail_flags: sum of binary flags to activate the various terms of the
- function:
-
- - 1 (b0001): Gaussian term
- - 2 (b0010): st term
- - 4 (b0100): lt term
- - 8 (b1000): step term
-
- E.g., to activate all termsof the hypermet, use ``tail_flags = 1 + 2 + 4 + 8 = 15``
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_ahypermet(double* x, int len_x, double* phypermet, int len_phypermet, double* y, int tail_flags)
-{
- int i, j;
- int g_term_flag, st_term_flag, lt_term_flag, step_term_flag;
- double c1, c2, sigma, height, sigma_sqrt2, sqrt2PI, inv_2_sqrt_2_log2, x_minus_position, epsilon;
- double area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r;
-
- if (test_params(len_phypermet, 8, "sum_hypermet",
- "height, centroid, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r")) {
- return(1);
- }
-
- g_term_flag = tail_flags & 1;
- st_term_flag = (tail_flags>>1) & 1;
- lt_term_flag = (tail_flags>>2) & 1;
- step_term_flag = (tail_flags>>3) & 1;
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- /* define epsilon to compare floating point values with 0. */
- epsilon = 0.00000000001;
-
- sqrt2PI= sqrt(2.0 * M_PI);
- inv_2_sqrt_2_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_phypermet/8; i++) {
- area = phypermet[8*i];
- position = phypermet[8*i+1];
- fwhm = phypermet[8*i+2];
- st_area_r = phypermet[8*i+3];
- st_slope_r = phypermet[8*i+4];
- lt_area_r = phypermet[8*i+5];
- lt_slope_r = phypermet[8*i+6];
- step_height_r = phypermet[8*i+7];
-
- sigma = fwhm * inv_2_sqrt_2_log2;
- height = area / (sigma * sqrt2PI);
-
- /* Prevent division by 0 */
- if (sigma == 0) {
- printf("fwhm must not be equal to 0");
- return(1);
- }
- sigma_sqrt2 = sigma * 1.4142135623730950488;
-
- for (j=0; j<len_x; j++) {
- x_minus_position = x[j] - position;
- c2 = (0.5 * x_minus_position * x_minus_position) / (sigma * sigma);
- /* gaussian term */
- if (g_term_flag) {
- y[j] += exp(-c2) * height;
- }
-
- /* st term */
- if (st_term_flag) {
- if (fabs(st_slope_r) > epsilon) {
- c1 = st_area_r * 0.5 * \
- erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / st_slope_r);
- y[j] += ((area * c1) / st_slope_r) * \
- exp(0.5 * (sigma / st_slope_r) * (sigma / st_slope_r) + \
- (x_minus_position / st_slope_r));
- }
- }
-
- /* lt term */
- if (lt_term_flag) {
- if (fabs(lt_slope_r) > epsilon) {
- c1 = lt_area_r * \
- 0.5 * erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / lt_slope_r);
- y[j] += ((area * c1) / lt_slope_r) * \
- exp(0.5 * (sigma / lt_slope_r) * (sigma / lt_slope_r) + \
- (x_minus_position / lt_slope_r));
- }
- }
-
- /* step term flag */
- if (step_term_flag) {
- y[j] += step_height_r * (area / (sigma * sqrt2PI)) * \
- 0.5 * erfc(x_minus_position / sigma_sqrt2);
- }
- }
- }
- return(0);
-}
-
-/* sum_fastahypermet
-
- Sum of hypermet functions, defined by
- (area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r).
-
- - *area* is the area underneath the gaussian peak
- - *position* is the center of the various peaks and the position of
- the step down
- - *fwhm* is the full-width at half maximum of the terms
- - *st_area_r* is factor between the gaussian area and the area of the
- short tail term
- - *st_slope_r* is a parameter related to the slope of the short tail
- in the low ``x`` values (the lower, the steeper)
- - *lt_area_r* is factor between the gaussian area and the area of the
- long tail term
- - *lt_slope_r* is a parameter related to the slope of the long tail
- in the low ``x`` values (the lower, the steeper)
- - *step_height_r* is the factor between the height of the step down
- and the gaussian height
-
- Parameters:
- -----------
-
- - x: Independant variable where the functions are calculated.
- - len_x: Number of elements in the x array.
- - phypermet: Array of hypermet function parameters:
- *(area1, position1, fwhm1, st_area_r1, st_slope_r1, lt_area_r1,
- lt_slope_r1, step_height_r1, ...)*
- - len_phypermet: Number of elements in the phypermet array. Must be
- a multiple of 8.
- - y: Output array. Must have memory allocated for the same number
- of elements as x (len_x).
- - tail_flags: sum of binary flags to activate the various terms of the
- function:
-
- - 1 (b0001): Gaussian term
- - 2 (b0010): st term
- - 4 (b0100): lt term
- - 8 (b1000): step term
-
- E.g., to activate all termsof the hypermet, use ``tail_flags = 1 + 2 + 4 + 8 = 15``
-
- Adapted from PyMca module SpecFitFuns
-*/
-int sum_fastahypermet(double* x, int len_x, double* phypermet, int len_phypermet, double* y, int tail_flags)
-{
- int i, j;
- int g_term_flag, st_term_flag, lt_term_flag, step_term_flag;
- double c1, c2, sigma, height, sigma_sqrt2, sqrt2PI, inv_2_sqrt_2_log2, x_minus_position, epsilon;
- double area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r;
-
- if (test_params(len_phypermet, 8, "sum_hypermet",
- "height, centroid, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r")) {
- return(1);
- }
-
- g_term_flag = tail_flags & 1;
- st_term_flag = (tail_flags>>1) & 1;
- lt_term_flag = (tail_flags>>2) & 1;
- step_term_flag = (tail_flags>>3) & 1;
-
- /* Initialize output array */
- for (j=0; j<len_x; j++) {
- y[j] = 0.;
- }
-
- /* define epsilon to compare floating point values with 0. */
- epsilon = 0.00000000001;
-
- sqrt2PI= sqrt(2.0 * M_PI);
- inv_2_sqrt_2_log2 = 1.0 / (2.0 * sqrt(2.0 * LOG2));
-
- for (i=0; i<len_phypermet/8; i++) {
- area = phypermet[8*i];
- position = phypermet[8*i+1];
- fwhm = phypermet[8*i+2];
- st_area_r = phypermet[8*i+3];
- st_slope_r = phypermet[8*i+4];
- lt_area_r = phypermet[8*i+5];
- lt_slope_r = phypermet[8*i+6];
- step_height_r = phypermet[8*i+7];
-
- sigma = fwhm * inv_2_sqrt_2_log2;
- height = area / (sigma * sqrt2PI);
-
- /* Prevent division by 0 */
- if (sigma == 0) {
- printf("fwhm must not be equal to 0");
- return(1);
- }
- sigma_sqrt2 = sigma * 1.4142135623730950488;
-
- for (j=0; j<len_x; j++) {
- x_minus_position = x[j] - position;
- c2 = (0.5 * x_minus_position * x_minus_position) / (sigma * sigma);
- /* gaussian term */
- if (g_term_flag && c2 < 100) {
- y[j] += fastexp(-c2) * height;
- }
-
- /* st term */
- if (st_term_flag && (fabs(st_slope_r) > epsilon) && (x_minus_position / st_slope_r) <= 612) {
- c1 = st_area_r * 0.5 * \
- erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / st_slope_r);
- y[j] += ((area * c1) / st_slope_r) * \
- fastexp(0.5 * (sigma / st_slope_r) * (sigma / st_slope_r) +\
- (x_minus_position / st_slope_r));
- }
-
- /* lt term */
- if (lt_term_flag && (fabs(lt_slope_r) > epsilon) && (x_minus_position / lt_slope_r) <= 612) {
- c1 = lt_area_r * \
- 0.5 * erfc((x_minus_position/sigma_sqrt2) + 0.5 * sigma_sqrt2 / lt_slope_r);
- y[j] += ((area * c1) / lt_slope_r) * \
- fastexp(0.5 * (sigma / lt_slope_r) * (sigma / lt_slope_r) +\
- (x_minus_position / lt_slope_r));
-
- }
-
- /* step term flag */
- if (step_term_flag) {
- y[j] += step_height_r * (area / (sigma * sqrt2PI)) *\
- 0.5 * erfc(x_minus_position / sigma_sqrt2);
- }
- }
- }
- return(0);
-}
-
-void pileup(double* x, long len_x, double* ret, int input2, double zero, double gain)
-{
- //int input2=0;
- //double zero=0.0;
- //double gain=1.0;
-
- int i, j, k;
- double *px, *pret, *pall;
-
- /* the pointer to the starting position of par data */
- px = x;
- pret = ret;
-
- *pret = 0;
- k = (int )(zero/gain);
- for (i=input2; i<len_x; i++){
- pall = x;
- if ((i+k) >= 0)
- {
- pret = (double *) ret+(i+k);
- for (j=0; j<len_x-i-k ;j++){
- *pret += *px * (*pall);
- pall++;
- pret++;
- }
- }
- px++;
- }
-}
diff --git a/silx/math/fit/functions_wrapper.pxd b/silx/math/fit/functions_wrapper.pxd
deleted file mode 100644
index 780116c..0000000
--- a/silx/math/fit/functions_wrapper.pxd
+++ /dev/null
@@ -1,170 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "14/06/2016"
-
-cimport cython
-
-cdef extern from "functions.h":
- int erfc_array(double* x,
- int len_x,
- double* y)
-
- int erf_array(double* x,
- int len_x,
- double* y);
-
- void snip1d(double *data,
- int size,
- int width)
-
- void snip2d(double *data,
- int nrows,
- int ncolumns,
- int width)
-
- void snip3d(double *data,
- int nx,
- int ny,
- int nz,
- int width)
-
- int strip(double* input,
- long len_input,
- double c,
- long niter,
- int deltai,
- long* anchors,
- long len_anchors,
- double* output)
-
- int sum_gauss(double* x,
- int len_x,
- double* pgauss,
- int len_pgauss,
- double* y)
-
- int sum_agauss(double* x,
- int len_x,
- double* pgauss,
- int len_pgauss,
- double* y)
-
- int sum_fastagauss(double* x,
- int len_x,
- double* pgauss,
- int len_pgauss,
- double* y)
-
- int sum_splitgauss(double* x,
- int len_x,
- double* pgauss,
- int len_pgauss,
- double* y)
-
- int sum_apvoigt(double* x,
- int len_x,
- double* pvoigt,
- int len_pvoigt,
- double* y)
-
- int sum_pvoigt(double* x,
- int len_x,
- double* pvoigt,
- int len_pvoigt,
- double* y)
-
- int sum_splitpvoigt(double* x,
- int len_x,
- double* pvoigt,
- int len_pvoigt,
- double* y)
-
- int sum_lorentz(double* x,
- int len_x,
- double* plorentz,
- int len_plorentz,
- double* y)
-
- int sum_alorentz(double* x,
- int len_x,
- double* plorentz,
- int len_plorentz,
- double* y)
-
- int sum_splitlorentz(double* x,
- int len_x,
- double* plorentz,
- int len_plorentz,
- double* y)
-
- int sum_stepdown(double* x,
- int len_x,
- double* pdstep,
- int len_pdstep,
- double* y)
-
- int sum_stepup(double* x,
- int len_x,
- double* pustep,
- int len_pustep,
- double* y)
-
- int sum_slit(double* x,
- int len_x,
- double* pslit,
- int len_pslit,
- double* y)
-
- int sum_ahypermet(double* x,
- int len_x,
- double* phypermet,
- int len_phypermet,
- double* y,
- int tail_flags)
-
- int sum_fastahypermet(double* x,
- int len_x,
- double* phypermet,
- int len_phypermet,
- double* y,
- int tail_flags)
-
- long seek(long begin_index,
- long end_index,
- long nsamples,
- double fwhm,
- double sensitivity,
- double debug_info,
- long max_npeaks,
- double * data,
- double * peaks,
- double * relevances)
-
- int SavitskyGolay(double* input,
- long len_input,
- int npoints,
- double* output)
diff --git a/silx/math/fit/leastsq.py b/silx/math/fit/leastsq.py
deleted file mode 100644
index 3df1a35..0000000
--- a/silx/math/fit/leastsq.py
+++ /dev/null
@@ -1,901 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-This module implements a Levenberg-Marquardt algorithm with constraints on the
-fitted parameters without introducing any other dependendency than numpy.
-
-If scipy dependency is not an issue, and no constraints are applied to the fitting
-parameters, there is no real gain compared to the use of scipy.optimize.curve_fit
-other than a more conservative calculation of uncertainties on fitted parameters.
-
-This module is a refactored version of PyMca Gefit.py module.
-"""
-__authors__ = ["V.A. Sole"]
-__license__ = "MIT"
-__date__ = "15/05/2017"
-__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
-
-import numpy
-from numpy.linalg import inv
-from numpy.linalg.linalg import LinAlgError
-import time
-import logging
-import copy
-
-_logger = logging.getLogger(__name__)
-
-# codes understood by the routine
-CFREE = 0
-CPOSITIVE = 1
-CQUOTED = 2
-CFIXED = 3
-CFACTOR = 4
-CDELTA = 5
-CSUM = 6
-CIGNORED = 7
-
-def leastsq(model, xdata, ydata, p0, sigma=None,
- constraints=None, model_deriv=None, epsfcn=None,
- deltachi=None, full_output=None,
- check_finite=True,
- left_derivative=False,
- max_iter=100):
- """
- Use non-linear least squares Levenberg-Marquardt algorithm to fit a function, f, to
- data with optional constraints on the fitted parameters.
-
- Assumes ``ydata = f(xdata, *params) + eps``
-
- :param model: callable
- The model function, f(x, ...). It must take the independent
- variable as the first argument and the parameters to fit as
- separate remaining arguments.
- The returned value is a one dimensional array of floats.
-
- :param xdata: An M-length sequence.
- The independent variable where the data is measured.
-
- :param ydata: An M-length sequence
- The dependent data --- nominally f(xdata, ...)
-
- :param p0: N-length sequence
- Initial guess for the parameters.
-
- :param sigma: None or M-length sequence, optional
- If not None, the uncertainties in the ydata array. These are used as
- weights in the least-squares problem
- i.e. minimising ``np.sum( ((f(xdata, *popt) - ydata) / sigma)**2 )``
- If None, the uncertainties are assumed to be 1
-
- :param constraints:
- If provided, it is a 2D sequence of dimension (n_parameters, 3) where,
- for each parameter denoted by the index i, the meaning is
-
- - constraints[i][0]
-
- - 0 - Free (CFREE)
- - 1 - Positive (CPOSITIVE)
- - 2 - Quoted (CQUOTED)
- - 3 - Fixed (CFIXED)
- - 4 - Factor (CFACTOR)
- - 5 - Delta (CDELTA)
- - 6 - Sum (CSUM)
-
-
- - constraints[i][1]
-
- - Ignored if constraints[i][0] is 0, 1, 3
- - Min value of the parameter if constraints[i][0] is CQUOTED
- - Index of fitted parameter to which it is related
-
- - constraints[i][2]
-
- - Ignored if constraints[i][0] is 0, 1, 3
- - Max value of the parameter if constraints[i][0] is CQUOTED
- - Factor to apply to related parameter with index constraints[i][1]
- - Difference with parameter with index constraints[i][1]
- - Sum obtained when adding parameter with index constraints[i][1]
- :type constraints: *optional*, None or 2D sequence
-
- :param model_deriv:
- None (default) or function providing the derivatives of the fitting function respect to the fitted parameters.
- It will be called as model_deriv(xdata, parameters, index) where parameters is a sequence with the current
- values of the fitting parameters, index is the fitting parameter index for which the the derivative has
- to be provided in the supplied array of xdata points.
- :type model_deriv: *optional*, None or callable
-
-
- :param epsfcn: float
- A variable used in determining a suitable parameter variation when
- calculating the numerical derivatives (for model_deriv=None).
- Normally the actual step length will be sqrt(epsfcn)*x
- Original Gefit module was using epsfcn 1.0e-5 while default value
- is now numpy.finfo(numpy.float64).eps as in scipy
- :type epsfcn: *optional*, float
-
- :param deltachi: float
- A variable used to control the minimum change in chisq to consider the
- fitting process not worth to be continued. Default is 0.1 %.
- :type deltachi: *optional*, float
-
- :param full_output: bool, optional
- non-zero to return all optional outputs. The default is None what will give a warning in case
- of a constrained fit without having set this kweyword.
-
- :param check_finite: bool, optional
- If True, check that the input arrays do not contain nans of infs,
- and raise a ValueError if they do. Setting this parameter to
- False will ignore input arrays values containing nans.
- Default is True.
-
- :param left_derivative:
- This parameter only has an influence if no derivative function
- is provided. When True the left and right derivatives of the
- model will be calculated for each fitted parameters thus leading to
- the double number of function evaluations. Default is False.
- Original Gefit module was always using left_derivative as True.
- :type left_derivative: *optional*, bool
-
- :param max_iter: Maximum number of iterations (default is 100)
-
- :return: Returns a tuple of length 2 (or 3 if full_ouput is True) with the content:
-
- ``popt``: array
- Optimal values for the parameters so that the sum of the squared error
- of ``f(xdata, *popt) - ydata`` is minimized
- ``pcov``: 2d array
- If no constraints are applied, this array contains the estimated covariance
- of popt. The diagonal provides the variance of the parameter estimate.
- To compute one standard deviation errors use ``perr = np.sqrt(np.diag(pcov))``.
- If constraints are applied, this array does not contain the estimated covariance of
- the parameters actually used during the fitting process but the uncertainties after
- recalculating the covariance if all the parameters were free.
- To get the actual uncertainties following error propagation of the actually fitted
- parameters one should set full_output to True and access the uncertainties key.
- ``infodict``: dict
- a dictionary of optional outputs with the keys:
-
- ``uncertainties``
- The actual uncertainty on the optimized parameters.
- ``nfev``
- The number of function calls
- ``fvec``
- The function evaluated at the output
- ``niter``
- The number of iterations performed
- ``chisq``
- The chi square ``np.sum( ((f(xdata, *popt) - ydata) / sigma)**2 )``
- ``reduced_chisq``
- The chi square ``np.sum( ((f(xdata, *popt) - ydata) / sigma)**2 )`` divided
- by the number of degrees of freedom ``(M - number_of_free_parameters)``
- """
- function_call_counter = 0
- if numpy.isscalar(p0):
- p0 = [p0]
- parameters = numpy.array(p0, dtype=numpy.float64, copy=False)
- if deltachi is None:
- deltachi = 0.001
-
- # NaNs can not be handled
- if check_finite:
- xdata = numpy.asarray_chkfinite(xdata)
- ydata = numpy.asarray_chkfinite(ydata)
- if sigma is not None:
- sigma = numpy.asarray_chkfinite(sigma)
- else:
- sigma = numpy.ones((ydata.shape), dtype=numpy.float64)
- ydata.shape = -1
- sigma.shape = -1
- else:
- ydata = numpy.asarray(ydata)
- xdata = numpy.asarray(xdata)
- ydata.shape = -1
- if sigma is not None:
- sigma = numpy.asarray(sigma)
- else:
- sigma = numpy.ones((ydata.shape), dtype=numpy.float64)
- sigma.shape = -1
- # get rid of NaN in input data
- idx = numpy.isfinite(ydata)
- if False in idx:
- # xdata must have a shape able to be understood by the user function
- # in principle, one should not need to change it, however, if there are
- # points to be excluded, one has to be able to exclude them.
- # We can only hope that the sequence is properly arranged
- if xdata.size == ydata.size:
- if len(xdata.shape) != 1:
- msg = "Need to reshape input xdata."
- _logger.warning(msg)
- xdata.shape = -1
- else:
- raise ValueError("Cannot reshape xdata to deal with NaN in ydata")
- ydata = ydata[idx]
- xdata = xdata[idx]
- sigma = sigma[idx]
- idx = numpy.isfinite(sigma)
- if False in idx:
- # xdata must have a shape able to be understood by the user function
- # in principle, one should not need to change it, however, if there are
- # points to be excluded, one has to be able to exclude them.
- # We can only hope that the sequence is properly arranged
- ydata = ydata[idx]
- xdata = xdata[idx]
- sigma = sigma[idx]
- idx = numpy.isfinite(xdata)
- filter_xdata = False
- if False in idx:
- # What to do?
- try:
- # Let's see if the function is able to deal with non-finite data
- msg = "Checking if function can deal with non-finite data"
- _logger.debug(msg)
- evaluation = model(xdata, *parameters)
- function_call_counter += 1
- if evaluation.shape != ydata.shape:
- if evaluation.size == ydata.size:
- msg = "Supplied function does not return a proper array of floats."
- msg += "\nFunction should be rewritten to return a 1D array of floats."
- msg += "\nTrying to reshape output."
- _logger.warning(msg)
- evaluation.shape = ydata.shape
- if False in numpy.isfinite(evaluation):
- msg = "Supplied function unable to handle non-finite x data"
- msg += "\nAttempting to filter out those x data values."
- _logger.warning(msg)
- filter_xdata = True
- else:
- filter_xdata = False
- evaluation = None
- except:
- # function cannot handle input data
- filter_xdata = True
- if filter_xdata:
- if xdata.size != ydata.size:
- raise ValueError("xdata contains non-finite data that cannot be filtered")
- else:
- # we leave the xdata as they where
- old_shape = xdata.shape
- xdata.shape = ydata.shape
- idx0 = numpy.isfinite(xdata)
- xdata.shape = old_shape
- ydata = ydata[idx0]
- xdata = xdata[idx]
- sigma = sigma[idx0]
- weight = 1.0 / (sigma + numpy.equal(sigma, 0))
- weight0 = weight * weight
-
- nparameters = len(parameters)
-
- if epsfcn is None:
- epsfcn = numpy.finfo(numpy.float64).eps
- else:
- epsfcn = max(epsfcn, numpy.finfo(numpy.float64).eps)
-
- # check if constraints have been passed as text
- constrained_fit = False
- if constraints is not None:
- # make sure we work with a list of lists
- input_constraints = constraints
- tmp_constraints = [None] * len(input_constraints)
- for i in range(nparameters):
- tmp_constraints[i] = list(input_constraints[i])
- constraints = tmp_constraints
- for i in range(nparameters):
- if hasattr(constraints[i][0], "upper"):
- txt = constraints[i][0].upper()
- if txt == "FREE":
- constraints[i][0] = CFREE
- elif txt == "POSITIVE":
- constraints[i][0] = CPOSITIVE
- elif txt == "QUOTED":
- constraints[i][0] = CQUOTED
- elif txt == "FIXED":
- constraints[i][0] = CFIXED
- elif txt == "FACTOR":
- constraints[i][0] = CFACTOR
- constraints[i][1] = int(constraints[i][1])
- elif txt == "DELTA":
- constraints[i][0] = CDELTA
- constraints[i][1] = int(constraints[i][1])
- elif txt == "SUM":
- constraints[i][0] = CSUM
- constraints[i][1] = int(constraints[i][1])
- elif txt in ["IGNORED", "IGNORE"]:
- constraints[i][0] = CIGNORED
- else:
- #I should raise an exception
- raise ValueError("Unknown constraint %s" % constraints[i][0])
- if constraints[i][0] > 0:
- constrained_fit = True
- if constrained_fit:
- if full_output is None:
- _logger.info("Recommended to set full_output to True when using constraints")
-
- # Levenberg-Marquardt algorithm
- fittedpar = parameters.__copy__()
- flambda = 0.001
- iiter = max_iter
- #niter = 0
- last_evaluation=None
- x = xdata
- y = ydata
- chisq0 = -1
- iteration_counter = 0
- while (iiter > 0):
- weight = weight0
- """
- I cannot evaluate the initial chisq here because I do not know
- if some parameters are to be ignored, otherways I could do it as follows:
- if last_evaluation is None:
- yfit = model(x, *fittedpar)
- last_evaluation = yfit
- chisq0 = (weight * pow(y-yfit, 2)).sum()
- and chisq would not need to be recalculated.
- Passing the last_evaluation assumes that there are no parameters being
- ignored or not between calls.
- """
- iteration_counter += 1
- chisq0, alpha0, beta, internal_output = chisq_alpha_beta(
- model, fittedpar,
- x, y, weight, constraints=constraints,
- model_deriv=model_deriv,
- epsfcn=epsfcn,
- left_derivative=left_derivative,
- last_evaluation=last_evaluation,
- full_output=True)
- n_free = internal_output["n_free"]
- free_index = internal_output["free_index"]
- noigno = internal_output["noigno"]
- fitparam = internal_output["fitparam"]
- function_calls = internal_output["function_calls"]
- function_call_counter += function_calls
- #print("chisq0 = ", chisq0, n_free, fittedpar)
- #raise
- nr, nc = alpha0.shape
- flag = 0
- #lastdeltachi = chisq0
- while flag == 0:
- alpha = alpha0 * (1.0 + flambda * numpy.identity(nr))
- deltapar = numpy.dot(beta, inv(alpha))
- if constraints is None:
- newpar = fitparam + deltapar [0]
- else:
- newpar = parameters.__copy__()
- pwork = numpy.zeros(deltapar.shape, numpy.float64)
- for i in range(n_free):
- if constraints is None:
- pwork [0] [i] = fitparam [i] + deltapar [0] [i]
- elif constraints [free_index[i]][0] == CFREE:
- pwork [0] [i] = fitparam [i] + deltapar [0] [i]
- elif constraints [free_index[i]][0] == CPOSITIVE:
- #abs method
- pwork [0] [i] = fitparam [i] + deltapar [0] [i]
- #square method
- #pwork [0] [i] = (numpy.sqrt(fitparam [i]) + deltapar [0] [i]) * \
- # (numpy.sqrt(fitparam [i]) + deltapar [0] [i])
- elif constraints[free_index[i]][0] == CQUOTED:
- pmax = max(constraints[free_index[i]][1],
- constraints[free_index[i]][2])
- pmin = min(constraints[free_index[i]][1],
- constraints[free_index[i]][2])
- A = 0.5 * (pmax + pmin)
- B = 0.5 * (pmax - pmin)
- if B != 0:
- pwork [0] [i] = A + \
- B * numpy.sin(numpy.arcsin((fitparam[i] - A)/B)+ \
- deltapar [0] [i])
- else:
- txt = "Error processing constrained fit\n"
- txt += "Parameter limits are %g and %g\n" % (pmin, pmax)
- txt += "A = %g B = %g" % (A, B)
- raise ValueError("Invalid parameter limits")
- newpar[free_index[i]] = pwork [0] [i]
- newpar = numpy.array(_get_parameters(newpar, constraints))
- workpar = numpy.take(newpar, noigno)
- yfit = model(x, *workpar)
- if last_evaluation is None:
- if len(yfit.shape) > 1:
- msg = "Supplied function does not return a 1D array of floats."
- msg += "\nFunction should be rewritten."
- msg += "\nTrying to reshape output."
- _logger.warning(msg)
- yfit.shape = -1
- function_call_counter += 1
- chisq = (weight * pow(y-yfit, 2)).sum()
- absdeltachi = chisq0 - chisq
- if absdeltachi < 0:
- flambda *= 10.0
- if flambda > 1000:
- flag = 1
- iiter = 0
- else:
- flag = 1
- fittedpar = newpar.__copy__()
- lastdeltachi = 100 * (absdeltachi / (chisq + (chisq == 0)))
- if iteration_counter < 2:
- # ignore any limit, the fit *has* to be improved
- pass
- elif (lastdeltachi) < deltachi:
- iiter = 0
- elif absdeltachi < numpy.sqrt(epsfcn):
- iiter = 0
- _logger.info("Iteration finished due to too small absolute chi decrement")
- chisq0 = chisq
- flambda = flambda / 10.0
- last_evaluation = yfit
- iiter = iiter - 1
- # this is the covariance matrix of the actually fitted parameters
- cov0 = inv(alpha0)
- if constraints is None:
- cov = cov0
- else:
- # yet another call needed with all the parameters being free except those
- # that are FIXED and that will be assigned a 100 % uncertainty.
- new_constraints = copy.deepcopy(constraints)
- flag_special = [0] * len(fittedpar)
- for idx, constraint in enumerate(constraints):
- if constraints[idx][0] in [CFIXED, CIGNORED]:
- flag_special[idx] = constraints[idx][0]
- else:
- new_constraints[idx][0] = CFREE
- new_constraints[idx][1] = 0
- new_constraints[idx][2] = 0
- chisq, alpha, beta, internal_output = chisq_alpha_beta(
- model, fittedpar,
- x, y, weight, constraints=new_constraints,
- model_deriv=model_deriv,
- epsfcn=epsfcn,
- left_derivative=left_derivative,
- last_evaluation=last_evaluation,
- full_output=True)
- # obtained chisq should be identical to chisq0
- try:
- cov = inv(alpha)
- except LinAlgError:
- _logger.critical("Error calculating covariance matrix after successful fit")
- cov = None
- if cov is not None:
- for idx, value in enumerate(flag_special):
- if value in [CFIXED, CIGNORED]:
- cov = numpy.insert(numpy.insert(cov, idx, 0, axis=1), idx, 0, axis=0)
- cov[idx, idx] = fittedpar[idx] * fittedpar[idx]
-
- if not full_output:
- return fittedpar, cov
- else:
- sigma0 = numpy.sqrt(abs(numpy.diag(cov0)))
- sigmapar = _get_sigma_parameters(fittedpar, sigma0, constraints)
- ddict = {}
- ddict["chisq"] = chisq0
- ddict["reduced_chisq"] = chisq0 / (len(yfit)-n_free)
- ddict["covariance"] = cov0
- ddict["uncertainties"] = sigmapar
- ddict["fvec"] = last_evaluation
- ddict["nfev"] = function_call_counter
- ddict["niter"] = iteration_counter
- return fittedpar, cov, ddict #, chisq/(len(yfit)-len(sigma0)), sigmapar,niter,lastdeltachi
-
-def chisq_alpha_beta(model, parameters, x, y, weight, constraints=None,
- model_deriv=None, epsfcn=None, left_derivative=False,
- last_evaluation=None, full_output=False):
-
- """
- Get chi square, the curvature matrix alpha and the matrix beta according to the input parameters.
- If all the parameters are unconstrained, the covariance matrix is the inverse of the alpha matrix.
-
- :param model: callable
- The model function, f(x, ...). It must take the independent
- variable as the first argument and the parameters to fit as
- separate remaining arguments.
- The returned value is a one dimensional array of floats.
-
- :param parameters: N-length sequence
- Values of parameters at which function and derivatives are to be calculated.
-
- :param x: An M-length sequence.
- The independent variable where the data is measured.
-
- :param y: An M-length sequence
- The dependent data --- nominally f(xdata, ...)
-
- :param weight: M-length sequence
- Weights to be applied in the calculation of chi square
- As a reminder ``chisq = np.sum(weigth * (model(x, *parameters) - y)**2)``
-
- :param constraints:
- If provided, it is a 2D sequence of dimension (n_parameters, 3) where,
- for each parameter denoted by the index i, the meaning is
-
- - constraints[i][0]
-
- - 0 - Free (CFREE)
- - 1 - Positive (CPOSITIVE)
- - 2 - Quoted (CQUOTED)
- - 3 - Fixed (CFIXED)
- - 4 - Factor (CFACTOR)
- - 5 - Delta (CDELTA)
- - 6 - Sum (CSUM)
-
-
- - constraints[i][1]
-
- - Ignored if constraints[i][0] is 0, 1, 3
- - Min value of the parameter if constraints[i][0] is CQUOTED
- - Index of fitted parameter to which it is related
-
- - constraints[i][2]
-
- - Ignored if constraints[i][0] is 0, 1, 3
- - Max value of the parameter if constraints[i][0] is CQUOTED
- - Factor to apply to related parameter with index constraints[i][1]
- - Difference with parameter with index constraints[i][1]
- - Sum obtained when adding parameter with index constraints[i][1]
- :type constraints: *optional*, None or 2D sequence
-
- :param model_deriv:
- None (default) or function providing the derivatives of the fitting function respect to the fitted parameters.
- It will be called as model_deriv(xdata, parameters, index) where parameters is a sequence with the current
- values of the fitting parameters, index is the fitting parameter index for which the the derivative has
- to be provided in the supplied array of xdata points.
- :type model_deriv: *optional*, None or callable
-
-
- :param epsfcn: float
- A variable used in determining a suitable parameter variation when
- calculating the numerical derivatives (for model_deriv=None).
- Normally the actual step length will be sqrt(epsfcn)*x
- Original Gefit module was using epsfcn 1.0e-10 while default value
- is now numpy.finfo(numpy.float64).eps as in scipy
- :type epsfcn: *optional*, float
-
- :param left_derivative:
- This parameter only has an influence if no derivative function
- is provided. When True the left and right derivatives of the
- model will be calculated for each fitted parameters thus leading to
- the double number of function evaluations. Default is False.
- Original Gefit module was always using left_derivative as True.
- :type left_derivative: *optional*, bool
-
- :param last_evaluation: An M-length array
- Used for optimization purposes. If supplied, this array will be taken as the result of
- evaluating the function, that is as the result of ``model(x, *parameters)`` thus avoiding
- the evaluation call.
-
- :param full_output: bool, optional
- Additional output used for internal purposes with the keys:
- ``function_calls``
- The number of model function calls performed.
- ``fitparam``
- A sequence with the actual free parameters
- ``free_index``
- Sequence with the indices of the free parameters in input parameters sequence.
- ``noigno``
- Sequence with the indices of the original parameters considered in the calculations.
- """
- if epsfcn is None:
- epsfcn = numpy.finfo(numpy.float64).eps
- else:
- epsfcn = max(epsfcn, numpy.finfo(numpy.float64).eps)
- #nr0, nc = data.shape
- n_param = len(parameters)
- if constraints is None:
- derivfactor = numpy.ones((n_param, ))
- n_free = n_param
- noigno = numpy.arange(n_param)
- free_index = noigno * 1
- fitparam = parameters * 1
- else:
- n_free = 0
- fitparam = []
- free_index = []
- noigno = []
- derivfactor = []
- for i in range(n_param):
- if constraints[i][0] != CIGNORED:
- noigno.append(i)
- if constraints[i][0] == CFREE:
- fitparam.append(parameters [i])
- derivfactor.append(1.0)
- free_index.append(i)
- n_free += 1
- elif constraints[i][0] == CPOSITIVE:
- fitparam.append(abs(parameters[i]))
- derivfactor.append(1.0)
- #fitparam.append(numpy.sqrt(abs(parameters[i])))
- #derivfactor.append(2.0*numpy.sqrt(abs(parameters[i])))
- free_index.append(i)
- n_free += 1
- elif constraints[i][0] == CQUOTED:
- pmax = max(constraints[i][1], constraints[i][2])
- pmin =min(constraints[i][1], constraints[i][2])
- if ((pmax-pmin) > 0) & \
- (parameters[i] <= pmax) & \
- (parameters[i] >= pmin):
- A = 0.5 * (pmax + pmin)
- B = 0.5 * (pmax - pmin)
- fitparam.append(parameters[i])
- derivfactor.append(B*numpy.cos(numpy.arcsin((parameters[i] - A)/B)))
- free_index.append(i)
- n_free += 1
- elif (pmax-pmin) > 0:
- print("WARNING: Quoted parameter outside boundaries")
- print("Initial value = %f" % parameters[i])
- print("Limits are %f and %f" % (pmin, pmax))
- print("Parameter will be kept at its starting value")
- fitparam = numpy.array(fitparam, numpy.float64)
- alpha = numpy.zeros((n_free, n_free), numpy.float64)
- beta = numpy.zeros((1, n_free), numpy.float64)
- #delta = (fitparam + numpy.equal(fitparam, 0.0)) * 0.00001
- delta = (fitparam + numpy.equal(fitparam, 0.0)) * numpy.sqrt(epsfcn)
- nr = y.size
- ##############
- # Prior to each call to the function one has to re-calculate the
- # parameters
- pwork = parameters.__copy__()
- for i in range(n_free):
- pwork [free_index[i]] = fitparam [i]
- if n_free == 0:
- raise ValueError("No free parameters to fit")
- function_calls = 0
- if not left_derivative:
- if last_evaluation is not None:
- f2 = last_evaluation
- else:
- f2 = model(x, *parameters)
- f2.shape = -1
- function_calls += 1
- for i in range(n_free):
- if model_deriv is None:
- #pwork = parameters.__copy__()
- pwork[free_index[i]] = fitparam [i] + delta [i]
- newpar = _get_parameters(pwork.tolist(), constraints)
- newpar = numpy.take(newpar, noigno)
- f1 = model(x, *newpar)
- f1.shape = -1
- function_calls += 1
- if left_derivative:
- pwork[free_index[i]] = fitparam [i] - delta [i]
- newpar = _get_parameters(pwork.tolist(), constraints)
- newpar=numpy.take(newpar, noigno)
- f2 = model(x, *newpar)
- function_calls += 1
- help0 = (f1 - f2) / (2.0 * delta[i])
- else:
- help0 = (f1 - f2) / (delta[i])
- help0 = help0 * derivfactor[i]
- pwork[free_index[i]] = fitparam [i]
- #removed I resize outside the loop:
- #help0 = numpy.resize(help0, (1, nr))
- else:
- help0 = model_deriv(x, pwork, free_index[i])
- help0 = help0 * derivfactor[i]
-
- if i == 0:
- deriv = help0
- else:
- deriv = numpy.concatenate((deriv, help0), 0)
-
- #line added to resize outside the loop
- deriv = numpy.resize(deriv, (n_free, nr))
- if last_evaluation is None:
- if constraints is None:
- yfit = model(x, *fitparam)
- yfit.shape = -1
- else:
- newpar = _get_parameters(pwork.tolist(), constraints)
- newpar = numpy.take(newpar, noigno)
- yfit = model(x, *newpar)
- yfit.shape = -1
- function_calls += 1
- else:
- yfit = last_evaluation
- deltay = y - yfit
- help0 = weight * deltay
- for i in range(n_free):
- derivi = numpy.resize(deriv[i, :], (1, nr))
- help1 = numpy.resize(numpy.sum((help0 * derivi), 1), (1, 1))
- if i == 0:
- beta = help1
- else:
- beta = numpy.concatenate((beta, help1), 1)
- help1 = numpy.inner(deriv, weight*derivi)
- if i == 0:
- alpha = help1
- else:
- alpha = numpy.concatenate((alpha, help1), 1)
- chisq = (help0 * deltay).sum()
- if full_output:
- ddict = {}
- ddict["n_free"] = n_free
- ddict["free_index"] = free_index
- ddict["noigno"] = noigno
- ddict["fitparam"] = fitparam
- ddict["derivfactor"] = derivfactor
- ddict["function_calls"] = function_calls
- return chisq, alpha, beta, ddict
- else:
- return chisq, alpha, beta
-
-
-def _get_parameters(parameters, constraints):
- """
- Apply constraints to input parameters.
-
- Parameters not depending on other parameters, they are returned as the input.
-
- Parameters depending on other parameters, return the value after applying the
- relation to the parameter wo which they are related.
- """
- # 0 = Free 1 = Positive 2 = Quoted
- # 3 = Fixed 4 = Factor 5 = Delta
- if constraints is None:
- return parameters * 1
- newparam = []
- #first I make the free parameters
- #because the quoted ones put troubles
- for i in range(len(constraints)):
- if constraints[i][0] == CFREE:
- newparam.append(parameters[i])
- elif constraints[i][0] == CPOSITIVE:
- #newparam.append(parameters[i] * parameters[i])
- newparam.append(abs(parameters[i]))
- elif constraints[i][0] == CQUOTED:
- newparam.append(parameters[i])
- elif abs(constraints[i][0]) == CFIXED:
- newparam.append(parameters[i])
- else:
- newparam.append(parameters[i])
- for i in range(len(constraints)):
- if constraints[i][0] == CFACTOR:
- newparam[i] = constraints[i][2] * newparam[int(constraints[i][1])]
- elif constraints[i][0] == CDELTA:
- newparam[i] = constraints[i][2] + newparam[int(constraints[i][1])]
- elif constraints[i][0] == CIGNORED:
- # The whole ignored stuff should not be documented because setting
- # a parameter to 0 is not the same as being ignored.
- # Being ignored should imply the parameter is simply not accounted for
- # and should be stripped out of the list of parameters by the program
- # using this module
- newparam[i] = 0
- elif constraints[i][0] == CSUM:
- newparam[i] = constraints[i][2]-newparam[int(constraints[i][1])]
- return newparam
-
-
-def _get_sigma_parameters(parameters, sigma0, constraints):
- """
- Internal function propagating the uncertainty on the actually fitted parameters and related parameters to the
- final parameters considering the applied constraints.
-
- Parameters
- ----------
- parameters : 1D sequence of length equal to the number of free parameters N
- The parameters actually used in the fitting process.
- sigma0 : 1D sequence of length N
- Uncertainties calculated as the square-root of the diagonal of
- the covariance matrix
- constraints : The set of constraints applied in the fitting process
- """
- # 0 = Free 1 = Positive 2 = Quoted
- # 3 = Fixed 4 = Factor 5 = Delta
- if constraints is None:
- return sigma0
- n_free = 0
- sigma_par = numpy.zeros(parameters.shape, numpy.float64)
- for i in range(len(constraints)):
- if constraints[i][0] == CFREE:
- sigma_par [i] = sigma0[n_free]
- n_free += 1
- elif constraints[i][0] == CPOSITIVE:
- #sigma_par [i] = 2.0 * sigma0[n_free]
- sigma_par [i] = sigma0[n_free]
- n_free += 1
- elif constraints[i][0] == CQUOTED:
- pmax = max(constraints [i][1], constraints [i][2])
- pmin = min(constraints [i][1], constraints [i][2])
- # A = 0.5 * (pmax + pmin)
- B = 0.5 * (pmax - pmin)
- if (B > 0) & (parameters [i] < pmax) & (parameters [i] > pmin):
- sigma_par [i] = abs(B * numpy.cos(parameters[i]) * sigma0[n_free])
- n_free += 1
- else:
- sigma_par [i] = parameters[i]
- elif abs(constraints[i][0]) == CFIXED:
- sigma_par[i] = parameters[i]
- for i in range(len(constraints)):
- if constraints[i][0] == CFACTOR:
- sigma_par [i] = constraints[i][2]*sigma_par[int(constraints[i][1])]
- elif constraints[i][0] == CDELTA:
- sigma_par [i] = sigma_par[int(constraints[i][1])]
- elif constraints[i][0] == CSUM:
- sigma_par [i] = sigma_par[int(constraints[i][1])]
- return sigma_par
-
-
-def main(argv=None):
- if argv is None:
- npoints = 10000
- elif hasattr(argv, "__len__"):
- if len(argv) > 1:
- npoints = int(argv[1])
- else:
- print("Usage:")
- print("fit [npoints]")
- else:
- # expected a number
- npoints = argv
-
- def gauss(t0, *param0):
- param = numpy.array(param0)
- t = numpy.array(t0)
- dummy = 2.3548200450309493 * (t - param[3]) / param[4]
- return param[0] + param[1] * t + param[2] * myexp(-0.5 * dummy * dummy)
-
-
- def myexp(x):
- # put a (bad) filter to avoid over/underflows
- # with no python looping
- return numpy.exp(x * numpy.less(abs(x), 250)) -\
- 1.0 * numpy.greater_equal(abs(x), 250)
-
- xx = numpy.arange(npoints, dtype=numpy.float64)
- yy = gauss(xx, *[10.5, 2, 1000.0, 20., 15])
- sy = numpy.sqrt(abs(yy))
- parameters = [0.0, 1.0, 900.0, 25., 10]
- stime = time.time()
-
- fittedpar, cov, ddict = leastsq(gauss, xx, yy, parameters,
- sigma=sy,
- left_derivative=False,
- full_output=True,
- check_finite=True)
- etime = time.time()
- sigmapars = numpy.sqrt(numpy.diag(cov))
- print("Took ", etime - stime, "seconds")
- print("Function calls = ", ddict["nfev"])
- print("chi square = ", ddict["chisq"])
- print("Fitted pars = ", fittedpar)
- print("Sigma pars = ", sigmapars)
- try:
- from scipy.optimize import curve_fit as cfit
- SCIPY = True
- except ImportError:
- SCIPY = False
- if SCIPY:
- counter = 0
- stime = time.time()
- scipy_fittedpar, scipy_cov = cfit(gauss,
- xx,
- yy,
- parameters,
- sigma=sy)
- etime = time.time()
- print("Scipy Took ", etime - stime, "seconds")
- print("Counter = ", counter)
- print("scipy = ", scipy_fittedpar)
- print("Sigma = ", numpy.sqrt(numpy.diag(scipy_cov)))
-
-if __name__ == "__main__":
- main()
diff --git a/silx/math/fit/peaks.pyx b/silx/math/fit/peaks.pyx
deleted file mode 100644
index a4fce89..0000000
--- a/silx/math/fit/peaks.pyx
+++ /dev/null
@@ -1,175 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-"""This module provides a peak search function and tools related to peak
-analysis.
-"""
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "15/05/2017"
-
-import logging
-import numpy
-
-from silx.math.fit import filters
-
-_logger = logging.getLogger(__name__)
-
-cimport cython
-from libc.stdlib cimport free
-
-cimport silx.math.fit.peaks_wrapper as peaks_wrapper
-
-
-def peak_search(y, fwhm, sensitivity=3.5,
- begin_index=None, end_index=None,
- debug=False, relevance_info=False):
- """Find peaks in a curve.
-
- :param y: Data array
- :type y: numpy.ndarray
- :param fwhm: Estimated full width at half maximum of the typical peaks we
- are interested in (expressed in number of samples)
- :param sensitivity: Threshold factor used for peak detection. Only peaks
- with amplitudes higher than ``σ * sensitivity`` - where ``σ`` is the
- standard deviation of the noise - qualify as peaks.
- :param begin_index: Index of the first sample of the region of interest
- in the ``y`` array. If ``None``, start from the first sample.
- :param end_index: Index of the last sample of the region of interest in
- the ``y`` array. If ``None``, process until the last sample.
- :param debug: If ``True``, print debug messages. Default: ``False``
- :param relevance_info: If ``True``, add a second dimension with relevance
- information to the output array. Default: ``False``
- :return: 1D sequence with indices of peaks in the data
- if ``relevance_info`` is ``False``.
- Else, sequence of ``(peak_index, peak_relevance)`` tuples (one tuple
- per peak).
- :raise: ``IndexError`` if the number of peaks is too large to fit in the
- output array.
- """
- cdef:
- int i
- double[::1] y_c
- double* peaks_c
- double* relevances_c
-
- y_c = numpy.array(y,
- copy=True,
- dtype=numpy.float64,
- order='C').reshape(-1)
- if debug:
- debug = 1
- else:
- debug = 0
-
- if begin_index is None:
- begin_index = 0
- if end_index is None:
- end_index = y_c.size - 1
-
- n_peaks = peaks_wrapper.seek(begin_index, end_index, y_c.size,
- fwhm, sensitivity, debug,
- &y_c[0], &peaks_c, &relevances_c)
-
-
- # A negative return value means that peaks were found but not enough
- # memory could be allocated for all
- if n_peaks < 0 and n_peaks != -123456:
- msg = "Before memory allocation error happened, "
- msg += "we found %d peaks.\n" % abs(n_peaks)
- _logger.debug(msg)
- msg = ""
- for i in range(abs(n_peaks)):
- msg += "peak index %f, " % peaks_c[i]
- msg += "relevance %f\n" % relevances_c[i]
- _logger.debug(msg)
- free(peaks_c)
- free(relevances_c)
- raise MemoryError("Failed to reallocate memory for output arrays")
- # Special value -123456 is returned if the initial memory allocation
- # fails, before any search could be performed
- elif n_peaks == -123456:
- raise MemoryError("Failed to allocate initial memory for " +
- "output arrays")
-
- peaks = numpy.empty(shape=(n_peaks,),
- dtype=numpy.float64)
- relevances = numpy.empty(shape=(n_peaks,),
- dtype=numpy.float64)
-
- for i in range(n_peaks):
- peaks[i] = peaks_c[i]
- relevances[i] = relevances_c[i]
-
- free(peaks_c)
- free(relevances_c)
-
- if not relevance_info:
- return peaks
- else:
- return list(zip(peaks, relevances))
-
-
-def guess_fwhm(y):
- """Return the full-width at half maximum for the largest peak in
- the data array.
-
- The algorithm removes the background, then finds a global maximum
- and its corresponding FWHM.
-
- This value can be used as an initial fit parameter, used as input for
- an iterative fit function.
-
- :param y: Data to be used for guessing the fwhm.
- :return: Estimation of full-width at half maximum, based on fwhm of
- the global maximum.
- """
- # set at a minimum value for the fwhm
- fwhm_min = 4
-
- # remove data background (computed with a strip filter)
- background = filters.strip(y, w=1, niterations=1000)
- yfit = y - background
-
- # basic peak search: find the global maximum
- maximum = max(yfit)
- # find indices of all values == maximum
- idx = numpy.nonzero(yfit == maximum)[0]
- # take the last one (if any)
- if not len(idx):
- return 0
- posindex = idx[-1]
- height = yfit[posindex]
-
- # now find the width of the peak at half maximum
- imin = posindex
- while yfit[imin] > 0.5 * height and imin > 0:
- imin -= 1
- imax = posindex
- while yfit[imax] > 0.5 * height and imax < len(yfit) - 1:
- imax += 1
-
- fwhm = max(imax - imin - 1, fwhm_min)
-
- return fwhm
diff --git a/silx/math/fit/peaks/include/peaks.h b/silx/math/fit/peaks/include/peaks.h
deleted file mode 100644
index bd25d96..0000000
--- a/silx/math/fit/peaks/include/peaks.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#ifndef PEAKS_H
-#define PEAKS_H
-
-/* Smoothing functions */
-
-long seek(long begin_index, long end_index, long nsamples, double fwhm, double sensitivity,
- double debug_info, double *data, double **peaks, double **relevances);
-
-#endif /* #define PEAKS_H */
diff --git a/silx/math/fit/peaks/src/peaks.c b/silx/math/fit/peaks/src/peaks.c
deleted file mode 100644
index 65cb4f6..0000000
--- a/silx/math/fit/peaks/src/peaks.c
+++ /dev/null
@@ -1,255 +0,0 @@
-#/*##########################################################################
-# Copyright (c) 2004-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-#include <math.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "peaks.h"
-
-
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-
-/* Peak search function, adapted from PyMca SpecFitFuns
-
- This uses a convolution with the second-derivative of a gaussian curve, to
- smooth the data.
-
- Arguments:
-
- - begin_index: First index of the region of interest in the input data
- array
- - end_index: Last index of the region of interest in the input data
- array
- - nsamples: Number of samples in the input array
- - fwhm: Full width at half maximum for the gaussian used for smoothing.
- - sensitivity:
- - debug_info: If different from 0, print debugging messages
- - data: input array of 1D data
- - peaks: pointer to output array of peak indices
- - relevances: pointer to output array of peak relevances
-*/
-long seek(long begin_index,
- long end_index,
- long nsamples,
- double fwhm,
- double sensitivity,
- double debug_info,
- double *data,
- double **peaks,
- double **relevances)
-{
- /* local variables */
- double *peaks0, *relevances0;
- double *realloc_peaks, *realloc_relevances;
- double sigma, sigma2, sigma4;
- long max_gfactor = 100;
- double gfactor[100];
- long nr_factor;
- double lowthreshold;
- double data2[2];
- double nom;
- double den2;
- long channel1;
- long lld;
- long cch;
- long cfac, cfac2, max_cfac;
- long ihelp1, ihelp2;
- long i;
- long max_npeaks = 100;
- long n_peaks = 0;
- double peakstarted = 0;
-
- peaks0 = malloc(100 * sizeof(double));
- relevances0 = malloc(100 * sizeof(double));
- if (peaks0 == NULL || relevances0 == NULL) {
- printf("Error: failed to allocate memory for peaks array.");
- return(-123456);
- }
- /* Make sure the peaks matrix is filled with zeros */
- for (i=0;i<100;i++){
- peaks0[i] = 0.0;
- relevances0[i] = 0.0;
- }
- /* Output pointers */
- *peaks = peaks0;
- *relevances = relevances0;
-
- /* prepare the calculation of the Gaussian scaling factors */
-
- sigma = fwhm / 2.35482;
- sigma2 = sigma * sigma;
- sigma4 = sigma2 * sigma2;
- lowthreshold = 0.01 / sigma2;
-
- /* calculate the factors until lower threshold reached */
- nr_factor = 0;
- max_cfac = MIN(max_gfactor, ((end_index - begin_index - 2) / 2) - 1);
- for (cfac=0; cfac < max_cfac; cfac++) {
- nr_factor++;
- cfac2 = (cfac+1) * (cfac+1);
- gfactor[cfac] = (sigma2 - cfac2) * exp(-cfac2/(sigma2*2.0)) / sigma4;
-
- if ((gfactor[cfac] < lowthreshold)
- && (gfactor[cfac] > (-lowthreshold))){
- break;
- }
- }
-
- /* What comes now is specific to MCA spectra ... */
- lld = 0;
- while (data[lld] == 0) {
- lld++;
- }
- lld = lld + (int) (0.5 * fwhm);
-
- channel1 = begin_index - nr_factor - 1;
- channel1 = MAX (channel1, lld);
- if(debug_info){
- printf("nrfactor = %ld\n", nr_factor);
- }
- /* calculates smoothed value and variance at begincalc */
- cch = MAX(begin_index, 0);
- nom = data[cch] / sigma2;
- den2 = data[cch] / sigma4;
- for (cfac = 0; cfac < nr_factor; cfac++){
- ihelp1 = cch-cfac;
- if (ihelp1 < 0){
- ihelp1 = 0;
- }
- ihelp2 = cch+cfac;
- if (ihelp2 >= nsamples){
- ihelp2 = nsamples-1;
- }
- nom += gfactor[cfac] * (data[ihelp2] + data[ihelp1]);
- den2 += gfactor[cfac] * gfactor[cfac] *
- (data[ihelp2] + data[ihelp1]);
- }
-
- /* now normalize the smoothed value to the standard deviation */
- if (den2 <= 0.0) {
- data2[1] = 0.0;
- }else{
- data2[1] = nom / sqrt(den2);
- }
- data[0] = data[1];
-
- while (cch <= MIN(end_index,nsamples-2)){
- /* calculate gaussian smoothed values */
- data2[0] = data2[1];
- cch++;
- nom = data[cch]/sigma2;
- den2 = data[cch] / sigma4;
- for (cfac = 1; cfac < nr_factor; cfac++){
- ihelp1 = cch-cfac;
- if (ihelp1 < 0){
- ihelp1 = 0;
- }
- ihelp2 = cch+cfac;
- if (ihelp2 >= nsamples){
- ihelp2 = nsamples-1;
- }
- nom += gfactor[cfac-1] * (data[ihelp2] + data[ihelp1]);
- den2 += gfactor[cfac-1] * gfactor[cfac-1] *
- (data[ihelp2] + data[ihelp1]);
- }
- /* now normalize the smoothed value to the standard deviation */
- if (den2 <= 0) {
- data2[1] = 0;
- }else{
- data2[1] = nom / sqrt(den2);
- }
- /* look if the current point falls in a peak */
- if (data2[1] > sensitivity) {
- if(peakstarted == 0){
- if (data2[1] > data2[0]){
- /* this second test is to prevent a peak from outside
- the region from being detected at the beginning of the search */
- peakstarted=1;
- }
- }
- /* there is a peak */
- if (debug_info){
- printf("At cch = %ld y[cch] = %g\n", cch, data[cch]);
- printf("data2[0] = %g\n", data2[0]);
- printf("data2[1] = %g\n", data2[1]);
- printf("sensitivity = %g\n", sensitivity);
- }
- if(peakstarted == 1){
- /* look for the top of the peak */
- if (data2[1] < data2[0]) {
- /* we are close to the top of the peak */
- if (debug_info){
- printf("we are close to the top of the peak\n");
- }
- if (n_peaks == max_npeaks) {
- max_npeaks = max_npeaks + 100;
- realloc_peaks = realloc(peaks0, max_npeaks * sizeof(double));
- realloc_relevances = realloc(relevances0, max_npeaks * sizeof(double));
- if (realloc_peaks == NULL || realloc_relevances == NULL) {
- printf("Error: failed to extend memory for peaks array.");
- *peaks = peaks0;
- *relevances = relevances0;
- return(-n_peaks);
- }
- else {
- peaks0 = realloc_peaks;
- relevances0 = realloc_relevances;
- }
- }
- peaks0[n_peaks] = cch-1;
- relevances0[n_peaks] = data2[0];
- n_peaks++;
- peakstarted=2;
- }
- }
- /* Doublet case */
- if(peakstarted == 2){
- if ((cch-peaks0[n_peaks-1]) > 0.6 * fwhm) {
- if (data2[1] > data2[0]){
- if(debug_info){
- printf("We may have a doublet\n");
- }
- peakstarted=1;
- }
- }
- }
- }else{
- if (peakstarted==1){
- /* We were on a peak but we did not find the top */
- if(debug_info){
- printf("We were on a peak but we did not find the top\n");
- }
- }
- peakstarted=0;
- }
- }
- if(debug_info){
- for (i=0;i< n_peaks;i++){
- printf("Peak %ld found at ",i+1);
- printf("index %g with y = %g\n", peaks0[i],data[(long ) peaks0[i]]);
- }
- }
- *peaks = peaks0;
- *relevances = relevances0;
- return (n_peaks);
-}
diff --git a/silx/math/fit/peaks_wrapper.pxd b/silx/math/fit/peaks_wrapper.pxd
deleted file mode 100644
index 4c77dc6..0000000
--- a/silx/math/fit/peaks_wrapper.pxd
+++ /dev/null
@@ -1,41 +0,0 @@
-# coding: utf-8
-#/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-#############################################################################*/
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "22/06/2016"
-
-cimport cython
-
-cdef extern from "peaks.h":
- long seek(long begin_index,
- long end_index,
- long nsamples,
- double fwhm,
- double sensitivity,
- double debug_info,
- double * data,
- double ** peaks,
- double ** relevances)
-
diff --git a/silx/math/fit/setup.py b/silx/math/fit/setup.py
deleted file mode 100644
index 649387f..0000000
--- a/silx/math/fit/setup.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "22/06/2016"
-
-
-import os.path
-
-from numpy.distutils.misc_util import Configuration
-
-
-def configuration(parent_package='', top_path=None):
- config = Configuration('fit', parent_package, top_path)
- config.add_subpackage('test')
-
- # =====================================
- # fit functions
- # =====================================
- fun_src = [os.path.join('functions', "src", "funs.c"),
- "functions.pyx"]
- fun_inc = [os.path.join('functions', 'include')]
-
- config.add_extension('functions',
- sources=fun_src,
- include_dirs=fun_inc,
- language='c')
-
- # =====================================
- # fit filters
- # =====================================
- filt_src = [os.path.join('filters', "src", srcf)
- for srcf in ["smoothnd.c", "snip1d.c",
- "snip2d.c", "snip3d.c", "strip.c"]]
- filt_src.append("filters.pyx")
- filt_inc = [os.path.join('filters', 'include')]
-
- config.add_extension('filters',
- sources=filt_src,
- include_dirs=filt_inc,
- language='c')
-
- # =====================================
- # peaks
- # =====================================
- peaks_src = [os.path.join('peaks', "src", "peaks.c"),
- "peaks.pyx"]
- peaks_inc = [os.path.join('peaks', 'include')]
-
- config.add_extension('peaks',
- sources=peaks_src,
- include_dirs=peaks_inc,
- language='c')
- # =====================================
- # =====================================
- return config
-
-
-if __name__ == "__main__":
- from numpy.distutils.core import setup
-
- setup(configuration=configuration)
diff --git a/silx/math/fit/test/__init__.py b/silx/math/fit/test/__init__.py
deleted file mode 100644
index d3d8ce8..0000000
--- a/silx/math/fit/test/__init__.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "22/06/2016"
-
-import unittest
-
-from .test_fit import suite as test_curve_fit
-from .test_functions import suite as test_fitfuns
-from .test_filters import suite as test_fitfilters
-from .test_peaks import suite as test_peaks
-from .test_fitmanager import suite as test_fitmanager
-from .test_bgtheories import suite as test_bgtheories
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(test_curve_fit())
- test_suite.addTest(test_fitfuns())
- test_suite.addTest(test_fitfilters())
- test_suite.addTest(test_peaks())
- test_suite.addTest(test_fitmanager())
- test_suite.addTest(test_bgtheories())
- return test_suite
diff --git a/silx/math/fit/test/test_bgtheories.py b/silx/math/fit/test/test_bgtheories.py
deleted file mode 100644
index e9fea37..0000000
--- a/silx/math/fit/test/test_bgtheories.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-import copy
-import unittest
-import numpy
-import random
-
-from silx.math.fit import bgtheories
-from silx.math.fit.functions import sum_gauss
-
-
-class TestBgTheories(unittest.TestCase):
- """
- """
- def setUp(self):
- self.x = numpy.arange(100)
- self.y = 10 + 0.05 * self.x + sum_gauss(self.x, 10., 45., 15.)
- # add a very narrow high amplitude peak to test strip and snip
- self.y += sum_gauss(self.x, 100., 75., 2.)
- self.narrow_peak_index = list(self.x).index(75)
- random.seed()
-
- def tearDown(self):
- pass
-
- def testTheoriesAttrs(self):
- for theory_name in bgtheories.THEORY:
- self.assertIsInstance(theory_name, str)
- self.assertTrue(hasattr(bgtheories.THEORY[theory_name],
- "function"))
- self.assertTrue(hasattr(bgtheories.THEORY[theory_name].function,
- "__call__"))
- # Ensure legacy functions are not renamed accidentally
- self.assertTrue(
- {"No Background", "Constant", "Linear", "Strip", "Snip"}.issubset(
- set(bgtheories.THEORY)))
-
- def testNoBg(self):
- nobgfun = bgtheories.THEORY["No Background"].function
- self.assertTrue(numpy.array_equal(nobgfun(self.x, self.y),
- numpy.zeros_like(self.x)))
- # default estimate
- self.assertEqual(bgtheories.THEORY["No Background"].estimate(self.x, self.y),
- ([], []))
-
- def testConstant(self):
- consfun = bgtheories.THEORY["Constant"].function
- c = random.random() * 100
- self.assertTrue(numpy.array_equal(consfun(self.x, self.y, c),
- c * numpy.ones_like(self.x)))
- # default estimate
- esti_par, cons = bgtheories.THEORY["Constant"].estimate(self.x, self.y)
- self.assertEqual(cons,
- [[0, 0, 0]])
- self.assertAlmostEqual(esti_par,
- min(self.y))
-
- def testLinear(self):
- linfun = bgtheories.THEORY["Linear"].function
- a = random.random() * 100
- b = random.random() * 100
- self.assertTrue(numpy.array_equal(linfun(self.x, self.y, a, b),
- a + b * self.x))
- # default estimate
- esti_par, cons = bgtheories.THEORY["Linear"].estimate(self.x, self.y)
-
- self.assertEqual(cons,
- [[0, 0, 0], [0, 0, 0]])
- self.assertAlmostEqual(esti_par[0], 10, places=3)
- self.assertAlmostEqual(esti_par[1], 0.05, places=3)
-
- def testStrip(self):
- stripfun = bgtheories.THEORY["Strip"].function
- anchors = sorted(random.sample(list(self.x), 4))
- anchors_indices = [list(self.x).index(a) for a in anchors]
-
- # we really want to strip away the narrow peak
- anchors_indices_copy = copy.deepcopy(anchors_indices)
- for idx in anchors_indices_copy:
- if abs(idx - self.narrow_peak_index) < 5:
- anchors_indices.remove(idx)
- anchors.remove(self.x[idx])
-
- width = 2
- niter = 1000
- bgtheories.THEORY["Strip"].configure(AnchorsList=anchors, AnchorsFlag=True)
-
- bg = stripfun(self.x, self.y, width, niter)
-
- # assert peak amplitude has been decreased
- self.assertLess(bg[self.narrow_peak_index],
- self.y[self.narrow_peak_index])
-
- # default estimate
- for i in anchors_indices:
- self.assertEqual(bg[i], self.y[i])
-
- # estimated parameters are equal to the default ones in the config dict
- bgtheories.THEORY["Strip"].configure(StripWidth=7, StripIterations=8)
- esti_par, cons = bgtheories.THEORY["Strip"].estimate(self.x, self.y)
- self.assertTrue(numpy.array_equal(cons, [[3, 0, 0], [3, 0, 0]]))
- self.assertEqual(esti_par, [7, 8])
-
- def testSnip(self):
- snipfun = bgtheories.THEORY["Snip"].function
- anchors = sorted(random.sample(list(self.x), 4))
- anchors_indices = [list(self.x).index(a) for a in anchors]
-
- # we want to strip away the narrow peak, so remove nearby anchors
- anchors_indices_copy = copy.deepcopy(anchors_indices)
- for idx in anchors_indices_copy:
- if abs(idx - self.narrow_peak_index) < 5:
- anchors_indices.remove(idx)
- anchors.remove(self.x[idx])
-
- width = 16
- bgtheories.THEORY["Snip"].configure(AnchorsList=anchors, AnchorsFlag=True)
- bg = snipfun(self.x, self.y, width)
-
- # assert peak amplitude has been decreased
- self.assertLess(bg[self.narrow_peak_index],
- self.y[self.narrow_peak_index],
- "Snip didn't decrease the peak amplitude.")
-
- # anchored data must remain fixed
- for i in anchors_indices:
- self.assertEqual(bg[i], self.y[i])
-
- # estimated parameters are equal to the default ones in the config dict
- bgtheories.THEORY["Snip"].configure(SnipWidth=7)
- esti_par, cons = bgtheories.THEORY["Snip"].estimate(self.x, self.y)
- self.assertTrue(numpy.array_equal(cons, [[3, 0, 0]]))
- self.assertEqual(esti_par, [7])
-
-
-test_cases = (TestBgTheories,)
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/fit/test/test_filters.py b/silx/math/fit/test/test_filters.py
deleted file mode 100644
index 078b998..0000000
--- a/silx/math/fit/test/test_filters.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-import numpy
-import unittest
-from silx.math.fit import filters
-from silx.math.fit import functions
-from silx.test.utils import add_relative_noise
-
-
-class TestSmooth(unittest.TestCase):
- """
- Unit tests of smoothing functions.
-
- Test that the difference between a synthetic curve with 5% added random
- noise and the result of smoothing that signal is less than 5%. We compare
- the sum of all samples in each curve.
- """
- def setUp(self):
- x = numpy.arange(5000)
- # (height1, center1, fwhm1, beamfwhm...)
- slit_params = (50, 500, 200, 100,
- 50, 600, 80, 30,
- 20, 2000, 150, 150,
- 50, 2250, 110, 100,
- 40, 3000, 50, 10,
- 23, 4980, 250, 20)
-
- self.y1 = functions.sum_slit(x, *slit_params)
- # 5% noise
- self.y1 = add_relative_noise(self.y1, 5.)
-
- # (height1, center1, fwhm1...)
- step_params = (50, 500, 200,
- 50, 600, 80,
- 20, 2000, 150,
- 50, 2250, 110,
- 40, 3000, 50,
- 23, 4980, 250,)
-
- self.y2 = functions.sum_stepup(x, *step_params)
- # 5% noise
- self.y2 = add_relative_noise(self.y2, 5.)
-
- self.y3 = functions.sum_stepdown(x, *step_params)
- # 5% noise
- self.y3 = add_relative_noise(self.y3, 5.)
-
- def tearDown(self):
- pass
-
- def testSavitskyGolay(self):
- npts = 25
- for y in [self.y1, self.y2, self.y3]:
- smoothed_y = filters.savitsky_golay(y, npoints=npts)
-
- # we added +-5% of random noise. The difference must be much lower
- # than 5%.
- diff = abs(sum(smoothed_y) - sum(y)) / sum(y)
- self.assertLess(diff, 0.05,
- "Difference between data with 5%% noise and " +
- "smoothed data is > 5%% (%f %%)" % (diff * 100))
-
- # Try various smoothing levels
- npts += 25
-
- def testSmooth1d(self):
- """Test the 1D smoothing against the formula
- ys[i] = (y[i-1] + 2 * y[i] + y[i+1]) / 4 (for 1 < i < n-1)"""
- smoothed_y = filters.smooth1d(self.y1)
-
- for i in range(1, len(self.y1) - 1):
- self.assertAlmostEqual(4 * smoothed_y[i],
- self.y1[i-1] + 2 * self.y1[i] + self.y1[i+1])
-
- def testSmooth2d(self):
- """Test that a 2D smoothing is the same as two successive and
- orthogonal 1D smoothings"""
- x = numpy.arange(10000)
-
- noise = 2 * numpy.random.random(10000) - 1
- noise *= 0.05
- y = x * (1 + noise)
-
- y.shape = (100, 100)
-
- smoothed_y = filters.smooth2d(y)
-
- intermediate_smooth = numpy.zeros_like(y)
- expected_smooth = numpy.zeros_like(y)
- # smooth along first dimension
- for i in range(0, y.shape[0]):
- intermediate_smooth[i, :] = filters.smooth1d(y[i, :])
-
- # smooth along second dimension
- for j in range(0, y.shape[1]):
- expected_smooth[:, j] = filters.smooth1d(intermediate_smooth[:, j])
-
- for i in range(0, y.shape[0]):
- for j in range(0, y.shape[1]):
- self.assertAlmostEqual(smoothed_y[i, j],
- expected_smooth[i, j])
-
-
-test_cases = (TestSmooth,)
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/fit/test/test_fit.py b/silx/math/fit/test/test_fit.py
deleted file mode 100644
index 3fdf394..0000000
--- a/silx/math/fit/test/test_fit.py
+++ /dev/null
@@ -1,387 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Nominal tests of the leastsq function.
-"""
-
-import unittest
-
-import numpy
-import sys
-
-from silx.utils import testutils
-from silx.math.fit.leastsq import _logger as fitlogger
-
-
-class Test_leastsq(unittest.TestCase):
- """
- Unit tests of the leastsq function.
- """
-
- ndims = None
-
- def setUp(self):
- try:
- from silx.math.fit import leastsq
- self.instance = leastsq
- except ImportError:
- self.instance = None
-
- def myexp(x):
- # put a (bad) filter to avoid over/underflows
- # with no python looping
- return numpy.exp(x*numpy.less(abs(x), 250)) - \
- 1.0 * numpy.greater_equal(abs(x), 250)
-
- self.my_exp = myexp
-
- def gauss(x, *params):
- params = numpy.array(params, copy=False, dtype=numpy.float64)
- result = params[0] + params[1] * x
- for i in range(2, len(params), 3):
- p = params[i:(i+3)]
- dummy = 2.3548200450309493*(x - p[1])/p[2]
- result += p[0] * self.my_exp(-0.5 * dummy * dummy)
- return result
-
- self.gauss = gauss
-
- def gauss_derivative(x, params, idx):
- if idx == 0:
- return numpy.ones(len(x), numpy.float64)
- if idx == 1:
- return x
- gaussian_peak = (idx - 2) // 3
- gaussian_parameter = (idx - 2) % 3
- actual_idx = 2 + 3 * gaussian_peak
- p = params[actual_idx:(actual_idx+3)]
- if gaussian_parameter == 0:
- return self.gauss(x, *[0, 0, 1.0, p[1], p[2]])
- if gaussian_parameter == 1:
- tmp = self.gauss(x, *[0, 0, p[0], p[1], p[2]])
- tmp *= 2.3548200450309493*(x - p[1])/p[2]
- return tmp * 2.3548200450309493/p[2]
- if gaussian_parameter == 2:
- tmp = self.gauss(x, *[0, 0, p[0], p[1], p[2]])
- tmp *= 2.3548200450309493*(x - p[1])/p[2]
- return tmp * 2.3548200450309493*(x - p[1])/(p[2]*p[2])
-
- self.gauss_derivative = gauss_derivative
-
- def tearDown(self):
- self.instance = None
- self.gauss = None
- self.gauss_derivative = None
- self.my_exp = None
- self.model_function = None
- self.model_derivative = None
-
- def testImport(self):
- self.assertTrue(self.instance is not None,
- "Cannot import leastsq from silx.math.fit")
-
- def testUnconstrainedFitNoWeight(self):
- parameters_actual = [10.5, 2, 1000.0, 20., 15]
- x = numpy.arange(10000.)
- y = self.gauss(x, *parameters_actual)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10]
- model_function = self.gauss
-
- fittedpar, cov = self.instance(model_function, x, y, parameters_estimate)
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- def testUnconstrainedFitWeight(self):
- parameters_actual = [10.5,2,1000.0,20.,15]
- x = numpy.arange(10000.)
- y = self.gauss(x, *parameters_actual)
- sigma = numpy.sqrt(y)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10]
- model_function = self.gauss
-
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma)
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- def testDerivativeFunction(self):
- parameters_actual = [10.5, 2, 10000.0, 20., 150, 5000, 900., 300]
- x = numpy.arange(10000.)
- y = self.gauss(x, *parameters_actual)
- delta = numpy.sqrt(numpy.finfo(numpy.float64).eps)
- for i in range(len(parameters_actual)):
- p = parameters_actual * 1
- if p[i] == 0:
- delta_par = delta
- else:
- delta_par = p[i] * delta
- if i > 2:
- p[0] = 0.0
- p[1] = 0.0
- p[i] += delta_par
- yPlus = self.gauss(x, *p)
- p[i] = parameters_actual[i] - delta_par
- yMinus = self.gauss(x, *p)
- numerical_derivative = (yPlus - yMinus) / (2 * delta_par)
- #numerical_derivative = (self.gauss(x, *p) - y) / delta_par
- p[i] = parameters_actual[i]
- derivative = self.gauss_derivative(x, p, i)
- diff = numerical_derivative - derivative
- test_condition = numpy.allclose(numerical_derivative,
- derivative, atol=5.0e-6)
- if not test_condition:
- msg = "Error calculating derivative of parameter %d." % i
- msg += "\n diff min = %g diff max = %g" % (diff.min(), diff.max())
- self.assertTrue(test_condition, msg)
-
- def testConstrainedFit(self):
- CFREE = 0
- CPOSITIVE = 1
- CQUOTED = 2
- CFIXED = 3
- CFACTOR = 4
- CDELTA = 5
- CSUM = 6
- parameters_actual = [10.5, 2, 10000.0, 20., 150, 5000, 900., 300]
- x = numpy.arange(10000.)
- y = self.gauss(x, *parameters_actual)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10, 400, 850, 200]
- model_function = self.gauss
- model_deriv = self.gauss_derivative
- constraints_all_free = [[0, 0, 0]] * len(parameters_actual)
- constraints_all_positive = [[1, 0, 0]] * len(parameters_actual)
- constraints_delta_position = [[0, 0, 0]] * len(parameters_actual)
- constraints_delta_position[6] = [CDELTA, 3, 880]
- constraints_sum_position = constraints_all_positive * 1
- constraints_sum_position[6] = [CSUM, 3, 920]
- constraints_factor = constraints_delta_position * 1
- constraints_factor[2] = [CFACTOR, 5, 2]
- constraints_list = [None,
- constraints_all_free,
- constraints_all_positive,
- constraints_delta_position,
- constraints_sum_position]
-
- # for better code coverage, the warning recommending to set full_output
- # to True when using constraints should be shown at least once
- full_output = True
- for index, constraints in enumerate(constraints_list):
- if index == 2:
- full_output = None
- elif index == 3:
- full_output = 0
- for model_deriv in [None, self.gauss_derivative]:
- for sigma in [None, numpy.sqrt(y)]:
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- constraints=constraints,
- model_deriv=model_deriv,
- full_output=full_output)[:2]
- full_output = True
-
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- def testUnconstrainedFitAnalyticalDerivative(self):
- parameters_actual = [10.5, 2, 1000.0, 20., 15]
- x = numpy.arange(10000.)
- y = self.gauss(x, *parameters_actual)
- sigma = numpy.sqrt(y)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10]
- model_function = self.gauss
- model_deriv = self.gauss_derivative
-
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- model_deriv=model_deriv)
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- @testutils.test_logging(fitlogger.name, warning=2)
- def testBadlyShapedData(self):
- parameters_actual = [10.5, 2, 1000.0, 20., 15]
- x = numpy.arange(10000.).reshape(1000, 10)
- y = self.gauss(x, *parameters_actual)
- sigma = numpy.sqrt(y)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10]
- model_function = self.gauss
-
- for check_finite in [True, False]:
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- check_finite=check_finite)
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- @testutils.test_logging(fitlogger.name, warning=3)
- def testDataWithNaN(self):
- parameters_actual = [10.5, 2, 1000.0, 20., 15]
- x = numpy.arange(10000.).reshape(1000, 10)
- y = self.gauss(x, *parameters_actual)
- sigma = numpy.sqrt(y)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10]
- model_function = self.gauss
- x[500] = numpy.inf
- # check default behavior
- try:
- self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma)
- except ValueError:
- info = "%s" % sys.exc_info()[1]
- self.assertTrue("array must not contain inf" in info)
-
- # check requested behavior
- try:
- self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- check_finite=True)
- except ValueError:
- info = "%s" % sys.exc_info()[1]
- self.assertTrue("array must not contain inf" in info)
-
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- check_finite=False)
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- # testing now with ydata containing NaN
- x = numpy.arange(10000.).reshape(1000, 10)
- y[500] = numpy.nan
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- check_finite=False)
-
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- # testing now with sigma containing NaN
- sigma[300] = numpy.nan
- fittedpar, cov = self.instance(model_function, x, y,
- parameters_estimate,
- sigma=sigma,
- check_finite=False)
- test_condition = numpy.allclose(parameters_actual, fittedpar)
- if not test_condition:
- msg = "Unsuccessfull fit\n"
- for i in range(len(fittedpar)):
- msg += "Expected %g obtained %g\n" % (parameters_actual[i],
- fittedpar[i])
- self.assertTrue(test_condition, msg)
-
- def testUncertainties(self):
- """Test for validity of uncertainties in returned full-output
- dictionary. This is a non-regression test for pull request #197"""
- parameters_actual = [10.5, 2, 1000.0, 20., 15, 2001.0, 30.1, 16]
- x = numpy.arange(10000.)
- y = self.gauss(x, *parameters_actual)
- parameters_estimate = [0.0, 1.0, 900.0, 25., 10., 1500., 20., 2.0]
-
- # test that uncertainties are not 0.
- fittedpar, cov, infodict = self.instance(self.gauss, x, y, parameters_estimate,
- full_output=True)
- uncertainties = infodict["uncertainties"]
- self.assertEqual(len(uncertainties), len(parameters_actual))
- self.assertEqual(len(uncertainties), len(fittedpar))
- for uncertainty in uncertainties:
- self.assertNotAlmostEqual(uncertainty, 0.)
-
- # set constraint FIXED for half the parameters.
- # This should cause leastsq to return 100% uncertainty.
- parameters_estimate = [10.6, 2.1, 1000.1, 20.1, 15.1, 2001.1, 30.2, 16.1]
- CFIXED = 3
- CFREE = 0
- constraints = []
- for i in range(len(parameters_estimate)):
- if i % 2:
- constraints.append([CFIXED, 0, 0])
- else:
- constraints.append([CFREE, 0, 0])
- fittedpar, cov, infodict = self.instance(self.gauss, x, y, parameters_estimate,
- constraints=constraints,
- full_output=True)
- uncertainties = infodict["uncertainties"]
- for i in range(len(parameters_estimate)):
- if i % 2:
- # test that all FIXED parameters have 100% uncertainty
- self.assertAlmostEqual(uncertainties[i],
- parameters_estimate[i])
-
-
-test_cases = (Test_leastsq,)
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/fit/test/test_fitmanager.py b/silx/math/fit/test/test_fitmanager.py
deleted file mode 100644
index acac242..0000000
--- a/silx/math/fit/test/test_fitmanager.py
+++ /dev/null
@@ -1,513 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Tests for fitmanager module
-"""
-
-import unittest
-import numpy
-import os.path
-
-from silx.math.fit import fitmanager
-from silx.math.fit import fittheories
-from silx.math.fit import bgtheories
-from silx.math.fit.fittheory import FitTheory
-from silx.math.fit.functions import sum_gauss, sum_stepdown, sum_stepup
-
-from silx.utils.testutils import ParametricTestCase
-from silx.test.utils import temp_dir
-
-custom_function_definition = """
-import copy
-from silx.math.fit.fittheory import FitTheory
-
-CONFIG = {'d': 1.}
-
-def myfun(x, a, b, c):
- "Model function"
- return (a * x**2 + b * x + c) / CONFIG['d']
-
-def myesti(x, y):
- "Initial parameters for iterative fit (a, b, c) = (1, 1, 1)"
- return (1., 1., 1.), ((0, 0, 0), (0, 0, 0), (0, 0, 0))
-
-def myconfig(d=1., **kw):
- "This function can modify CONFIG"
- CONFIG["d"] = d
- return CONFIG
-
-def myderiv(x, parameters, index):
- "Custom derivative (does not work, causes singular matrix)"
- pars_plus = copy.copy(parameters)
- pars_plus[index] *= 1.0001
-
- pars_minus = parameters
- pars_minus[index] *= copy.copy(0.9999)
-
- delta_fun = myfun(x, *pars_plus) - myfun(x, *pars_minus)
- delta_par = parameters[index] * 0.0001 * 2
-
- return delta_fun / delta_par
-
-THEORY = {
- 'my fit theory':
- FitTheory(function=myfun,
- parameters=('A', 'B', 'C'),
- estimate=myesti,
- configure=myconfig,
- derivative=myderiv)
-}
-
-"""
-
-old_custom_function_definition = """
-CONFIG = {'d': 1.0}
-
-def myfun(x, a, b, c):
- "Model function"
- return (a * x**2 + b * x + c) / CONFIG['d']
-
-def myesti(x, y, bg, xscalinq, yscaling):
- "Initial parameters for iterative fit (a, b, c) = (1, 1, 1)"
- return (1., 1., 1.), ((0, 0, 0), (0, 0, 0), (0, 0, 0))
-
-def myconfig(**kw):
- "Update or complete CONFIG dictionary"
- for key in kw:
- CONFIG[key] = kw[key]
- return CONFIG
-
-THEORY = ['my fit theory']
-PARAMETERS = [('A', 'B', 'C')]
-FUNCTION = [myfun]
-ESTIMATE = [myesti]
-CONFIGURE = [myconfig]
-
-"""
-
-
-def _order_of_magnitude(x):
- return numpy.log10(x).round()
-
-
-class TestFitmanager(ParametricTestCase):
- """
- Unit tests of multi-peak functions.
- """
- def setUp(self):
- pass
-
- def tearDown(self):
- pass
-
- def testFitManager(self):
- """Test fit manager on synthetic data using a gaussian function
- and a linear background"""
- # Create synthetic data with a sum of gaussian functions
- x = numpy.arange(1000).astype(numpy.float64)
-
- p = [1000, 100., 250,
- 255, 650., 45,
- 1500, 800.5, 95]
- linear_bg = 2.65 * x + 13
- y = linear_bg + sum_gauss(x, *p)
-
- y_with_nans = numpy.array(y)
- y_with_nans[::10] = numpy.nan
-
- x_with_nans = numpy.array(x)
- x_with_nans[5::15] = numpy.nan
-
- tests = {
- 'all finite': (x, y),
- 'y with NaNs': (x, y_with_nans),
- 'x with NaNs': (x_with_nans, y),
- }
-
- for name, (xdata, ydata) in tests.items():
- with self.subTest(name=name):
- # Fitting
- fit = fitmanager.FitManager()
- fit.setdata(x=xdata, y=ydata)
- fit.loadtheories(fittheories)
- # Use one of the default fit functions
- fit.settheory('Gaussians')
- fit.setbackground('Linear')
- fit.estimate()
- fit.runfit()
-
- # fit.fit_results[]
-
- # first 2 parameters are related to the linear background
- self.assertEqual(fit.fit_results[0]["name"], "Constant")
- self.assertAlmostEqual(fit.fit_results[0]["fitresult"], 13)
- self.assertEqual(fit.fit_results[1]["name"], "Slope")
- self.assertAlmostEqual(fit.fit_results[1]["fitresult"], 2.65)
-
- for i, param in enumerate(fit.fit_results[2:]):
- param_number = i // 3 + 1
- if i % 3 == 0:
- self.assertEqual(param["name"],
- "Height%d" % param_number)
- elif i % 3 == 1:
- self.assertEqual(param["name"],
- "Position%d" % param_number)
- elif i % 3 == 2:
- self.assertEqual(param["name"],
- "FWHM%d" % param_number)
-
- self.assertAlmostEqual(param["fitresult"],
- p[i])
- self.assertAlmostEqual(_order_of_magnitude(param["estimation"]),
- _order_of_magnitude(p[i]))
-
- def testLoadCustomFitFunction(self):
- """Test FitManager using a custom fit function defined in an external
- file and imported with FitManager.loadtheories"""
- # Create synthetic data with a sum of gaussian functions
- x = numpy.arange(100).astype(numpy.float64)
-
- # a, b, c are the fit parameters
- # d is a known scaling parameter that is set using configure()
- a, b, c, d = 1.5, 2.5, 3.5, 4.5
- y = (a * x**2 + b * x + c) / d
-
- # Fitting
- fit = fitmanager.FitManager()
- fit.setdata(x=x, y=y)
-
- # Create a temporary function definition file, and import it
- with temp_dir() as tmpDir:
- tmpfile = os.path.join(tmpDir, 'customfun.py')
- # custom_function_definition
- fd = open(tmpfile, "w")
- fd.write(custom_function_definition)
- fd.close()
- fit.loadtheories(tmpfile)
- tmpfile_pyc = os.path.join(tmpDir, 'customfun.pyc')
- if os.path.exists(tmpfile_pyc):
- os.unlink(tmpfile_pyc)
- os.unlink(tmpfile)
-
- fit.settheory('my fit theory')
- # Test configure
- fit.configure(d=4.5)
- fit.estimate()
- fit.runfit()
-
- self.assertEqual(fit.fit_results[0]["name"],
- "A1")
- self.assertAlmostEqual(fit.fit_results[0]["fitresult"],
- 1.5)
- self.assertEqual(fit.fit_results[1]["name"],
- "B1")
- self.assertAlmostEqual(fit.fit_results[1]["fitresult"],
- 2.5)
- self.assertEqual(fit.fit_results[2]["name"],
- "C1")
- self.assertAlmostEqual(fit.fit_results[2]["fitresult"],
- 3.5)
-
- def testLoadOldCustomFitFunction(self):
- """Test FitManager using a custom fit function defined in an external
- file and imported with FitManager.loadtheories (legacy PyMca format)"""
- # Create synthetic data with a sum of gaussian functions
- x = numpy.arange(100).astype(numpy.float64)
-
- # a, b, c are the fit parameters
- # d is a known scaling parameter that is set using configure()
- a, b, c, d = 1.5, 2.5, 3.5, 4.5
- y = (a * x**2 + b * x + c) / d
-
- # Fitting
- fit = fitmanager.FitManager()
- fit.setdata(x=x, y=y)
-
- # Create a temporary function definition file, and import it
- with temp_dir() as tmpDir:
- tmpfile = os.path.join(tmpDir, 'oldcustomfun.py')
- # custom_function_definition
- fd = open(tmpfile, "w")
- fd.write(old_custom_function_definition)
- fd.close()
- fit.loadtheories(tmpfile)
- tmpfile_pyc = os.path.join(tmpDir, 'oldcustomfun.pyc')
- if os.path.exists(tmpfile_pyc):
- os.unlink(tmpfile_pyc)
- os.unlink(tmpfile)
-
- fit.settheory('my fit theory')
- fit.configure(d=4.5)
- fit.estimate()
- fit.runfit()
-
- self.assertEqual(fit.fit_results[0]["name"],
- "A1")
- self.assertAlmostEqual(fit.fit_results[0]["fitresult"],
- 1.5)
- self.assertEqual(fit.fit_results[1]["name"],
- "B1")
- self.assertAlmostEqual(fit.fit_results[1]["fitresult"],
- 2.5)
- self.assertEqual(fit.fit_results[2]["name"],
- "C1")
- self.assertAlmostEqual(fit.fit_results[2]["fitresult"],
- 3.5)
-
- def testAddTheory(self, estimate=True):
- """Test FitManager using a custom fit function imported with
- FitManager.addtheory"""
- # Create synthetic data with a sum of gaussian functions
- x = numpy.arange(100).astype(numpy.float64)
-
- # a, b, c are the fit parameters
- # d is a known scaling parameter that is set using configure()
- a, b, c, d = -3.14, 1234.5, 10000, 4.5
- y = (a * x**2 + b * x + c) / d
-
- # Fitting
- fit = fitmanager.FitManager()
- fit.setdata(x=x, y=y)
-
- # Define and add the fit theory
- CONFIG = {'d': 1.}
-
- def myfun(x_, a_, b_, c_):
- """"Model function"""
- return (a_ * x_**2 + b_ * x_ + c_) / CONFIG['d']
-
- def myesti(x_, y_):
- """"Initial parameters for iterative fit:
- (a, b, c) = (1, 1, 1)
- Constraints all set to 0 (FREE)"""
- return (1., 1., 1.), ((0, 0, 0), (0, 0, 0), (0, 0, 0))
-
- def myconfig(d_=1., **kw):
- """This function can modify CONFIG"""
- CONFIG["d"] = d_
- return CONFIG
-
- def myderiv(x_, parameters, index):
- """Custom derivative"""
- pars_plus = numpy.array(parameters, copy=True)
- pars_plus[index] *= 1.001
-
- pars_minus = numpy.array(parameters, copy=True)
- pars_minus[index] *= 0.999
-
- delta_fun = myfun(x_, *pars_plus) - myfun(x_, *pars_minus)
- delta_par = parameters[index] * 0.001 * 2
-
- return delta_fun / delta_par
-
- fit.addtheory("polynomial",
- FitTheory(function=myfun,
- parameters=["A", "B", "C"],
- estimate=myesti if estimate else None,
- configure=myconfig,
- derivative=myderiv))
-
- fit.settheory('polynomial')
- fit.configure(d_=4.5)
- fit.estimate()
- params1, sigmas, infodict = fit.runfit()
-
- self.assertEqual(fit.fit_results[0]["name"],
- "A1")
- self.assertAlmostEqual(fit.fit_results[0]["fitresult"],
- -3.14)
- self.assertEqual(fit.fit_results[1]["name"],
- "B1")
- # params1[1] is the same as fit.fit_results[1]["fitresult"]
- self.assertAlmostEqual(params1[1],
- 1234.5)
- self.assertEqual(fit.fit_results[2]["name"],
- "C1")
- self.assertAlmostEqual(params1[2],
- 10000)
-
- # change configuration scaling factor and check that the fit returns
- # different values
- fit.configure(d_=5.)
- fit.estimate()
- params2, sigmas, infodict = fit.runfit()
- for p1, p2 in zip(params1, params2):
- self.assertFalse(numpy.array_equal(p1, p2),
- "Fit parameters are equal even though the " +
- "configuration has been changed")
-
- def testNoEstimate(self):
- """Ensure that the in the absence of the estimation function,
- the default estimation function :meth:`FitTheory.default_estimate`
- is used."""
- self.testAddTheory(estimate=False)
-
- def testStep(self):
- """Test fit manager on a step function with a more complex estimate
- function than the gaussian (convolution filter)"""
- for theory_name, theory_fun in (('Step Down', sum_stepdown),
- ('Step Up', sum_stepup)):
- # Create synthetic data with a sum of gaussian functions
- x = numpy.arange(1000).astype(numpy.float64)
-
- # ('Height', 'Position', 'FWHM')
- p = [1000, 439, 250]
-
- constantbg = 13
- y = theory_fun(x, *p) + constantbg
-
- # Fitting
- fit = fitmanager.FitManager()
- fit.setdata(x=x, y=y)
- fit.loadtheories(fittheories)
- fit.settheory(theory_name)
- fit.setbackground('Constant')
-
- fit.estimate()
-
- params, sigmas, infodict = fit.runfit()
-
- # first parameter is the constant background
- self.assertAlmostEqual(params[0], 13, places=5)
- for i, param in enumerate(params[1:]):
- self.assertAlmostEqual(param, p[i], places=5)
- self.assertAlmostEqual(_order_of_magnitude(fit.fit_results[i+1]["estimation"]),
- _order_of_magnitude(p[i]))
-
-
-def quadratic(x, a, b, c):
- return a * x**2 + b * x + c
-
-
-def cubic(x, a, b, c, d):
- return a * x**3 + b * x**2 + c * x + d
-
-
-class TestPolynomials(unittest.TestCase):
- """Test polynomial fit theories and fit background"""
- def setUp(self):
- self.x = numpy.arange(100).astype(numpy.float64)
-
- def testQuadraticBg(self):
- gaussian_params = [100, 45, 8]
- poly_params = [0.05, -2, 3]
- p = numpy.poly1d(poly_params)
-
- y = p(self.x) + sum_gauss(self.x, *gaussian_params)
-
- fm = fitmanager.FitManager(self.x, y)
- fm.loadbgtheories(bgtheories)
- fm.loadtheories(fittheories)
- fm.settheory("Gaussians")
- fm.setbackground("Degree 2 Polynomial")
- esti_params = fm.estimate()
- fit_params = fm.runfit()[0]
-
- for p, pfit in zip(poly_params + gaussian_params, fit_params):
- self.assertAlmostEqual(p,
- pfit)
-
- def testCubicBg(self):
- gaussian_params = [1000, 45, 8]
- poly_params = [0.0005, -0.05, 3, -4]
- p = numpy.poly1d(poly_params)
-
- y = p(self.x) + sum_gauss(self.x, *gaussian_params)
-
- fm = fitmanager.FitManager(self.x, y)
- fm.loadtheories(fittheories)
- fm.settheory("Gaussians")
- fm.setbackground("Degree 3 Polynomial")
- esti_params = fm.estimate()
- fit_params = fm.runfit()[0]
-
- for p, pfit in zip(poly_params + gaussian_params, fit_params):
- self.assertAlmostEqual(p,
- pfit)
-
- def testQuarticcBg(self):
- gaussian_params = [10000, 69, 25]
- poly_params = [5e-10, 0.0005, 0.005, 2, 4]
- p = numpy.poly1d(poly_params)
-
- y = p(self.x) + sum_gauss(self.x, *gaussian_params)
-
- fm = fitmanager.FitManager(self.x, y)
- fm.loadtheories(fittheories)
- fm.settheory("Gaussians")
- fm.setbackground("Degree 4 Polynomial")
- esti_params = fm.estimate()
- fit_params = fm.runfit()[0]
-
- for p, pfit in zip(poly_params + gaussian_params, fit_params):
- self.assertAlmostEqual(p,
- pfit,
- places=5)
-
- def _testPoly(self, poly_params, theory, places=5):
- p = numpy.poly1d(poly_params)
-
- y = p(self.x)
-
- fm = fitmanager.FitManager(self.x, y)
- fm.loadbgtheories(bgtheories)
- fm.loadtheories(fittheories)
- fm.settheory(theory)
- esti_params = fm.estimate()
- fit_params = fm.runfit()[0]
-
- for p, pfit in zip(poly_params, fit_params):
- self.assertAlmostEqual(p, pfit, places=places)
-
- def testQuadratic(self):
- self._testPoly([0.05, -2, 3],
- "Degree 2 Polynomial")
-
- def testCubic(self):
- self._testPoly([0.0005, -0.05, 3, -4],
- "Degree 3 Polynomial")
-
- def testQuartic(self):
- self._testPoly([1, -2, 3, -4, -5],
- "Degree 4 Polynomial")
-
- def testQuintic(self):
- self._testPoly([1, -2, 3, -4, -5, 6],
- "Degree 5 Polynomial",
- places=4)
-
-
-test_cases = (TestFitmanager, TestPolynomials)
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/fit/test/test_functions.py b/silx/math/fit/test/test_functions.py
deleted file mode 100644
index ce7dbd6..0000000
--- a/silx/math/fit/test/test_functions.py
+++ /dev/null
@@ -1,272 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Tests for functions module
-"""
-
-import unittest
-import numpy
-import math
-
-from silx.math.fit import functions
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "21/07/2016"
-
-class Test_functions(unittest.TestCase):
- """
- Unit tests of multi-peak functions.
- """
- def setUp(self):
- self.x = numpy.arange(11)
-
- # height, center, sigma1, sigma2
- (h, c, s1, s2) = (7., 5., 3., 2.1)
- self.g_params = {
- "height": h,
- "center": c,
- #"sigma": s,
- "fwhm1": 2 * math.sqrt(2 * math.log(2)) * s1,
- "fwhm2": 2 * math.sqrt(2 * math.log(2)) * s2,
- "area1": h * s1 * math.sqrt(2 * math.pi)
- }
- # result of `7 * scipy.signal.gaussian(11, 3)`
- self.scipy_gaussian = numpy.array(
- [1.74546546, 2.87778603, 4.24571462, 5.60516182, 6.62171628,
- 7., 6.62171628, 5.60516182, 4.24571462, 2.87778603,
- 1.74546546]
- )
-
- # result of:
- # numpy.concatenate((7 * scipy.signal.gaussian(11, 3)[0:5],
- # 7 * scipy.signal.gaussian(11, 2.1)[5:11]))
- self.scipy_asym_gaussian = numpy.array(
- [1.74546546, 2.87778603, 4.24571462, 5.60516182, 6.62171628,
- 7., 6.24968751, 4.44773692, 2.52313452, 1.14093853, 0.41124877]
- )
-
- def tearDown(self):
- pass
-
- def testGauss(self):
- """Compare sum_gauss with scipy.signals.gaussian"""
- y = functions.sum_gauss(self.x,
- self.g_params["height"],
- self.g_params["center"],
- self.g_params["fwhm1"])
-
- for i in range(11):
- self.assertAlmostEqual(y[i], self.scipy_gaussian[i])
-
- def testAGauss(self):
- """Compare sum_agauss with scipy.signals.gaussian"""
- y = functions.sum_agauss(self.x,
- self.g_params["area1"],
- self.g_params["center"],
- self.g_params["fwhm1"])
- for i in range(11):
- self.assertAlmostEqual(y[i], self.scipy_gaussian[i])
-
- def testFastAGauss(self):
- """Compare sum_fastagauss with scipy.signals.gaussian
- Limit precision to 3 decimal places."""
- y = functions.sum_fastagauss(self.x,
- self.g_params["area1"],
- self.g_params["center"],
- self.g_params["fwhm1"])
- for i in range(11):
- self.assertAlmostEqual(y[i], self.scipy_gaussian[i], 3)
-
-
- def testSplitGauss(self):
- """Compare sum_splitgauss with scipy.signals.gaussian"""
- y = functions.sum_splitgauss(self.x,
- self.g_params["height"],
- self.g_params["center"],
- self.g_params["fwhm1"],
- self.g_params["fwhm2"])
- for i in range(11):
- self.assertAlmostEqual(y[i], self.scipy_asym_gaussian[i])
-
- def testErf(self):
- """Compare erf with math.erf"""
- # scalars
- self.assertAlmostEqual(functions.erf(0.14), math.erf(0.14), places=5)
- self.assertAlmostEqual(functions.erf(0), math.erf(0), places=5)
- self.assertAlmostEqual(functions.erf(-0.74), math.erf(-0.74), places=5)
-
- # lists
- x = [-5, -2, -1.5, -0.6, 0, 0.1, 2, 3]
- erfx = functions.erf(x)
- for i in range(len(x)):
- self.assertAlmostEqual(erfx[i],
- math.erf(x[i]),
- places=5)
-
- # ndarray
- x = numpy.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
- erfx = functions.erf(x)
- for i in range(x.shape[0]):
- for j in range(x.shape[1]):
- self.assertAlmostEqual(erfx[i, j],
- math.erf(x[i, j]),
- places=5)
-
- def testErfc(self):
- """Compare erf with math.erf"""
- # scalars
- self.assertAlmostEqual(functions.erfc(0.14), math.erfc(0.14), places=5)
- self.assertAlmostEqual(functions.erfc(0), math.erfc(0), places=5)
- self.assertAlmostEqual(functions.erfc(-0.74), math.erfc(-0.74), places=5)
-
- # lists
- x = [-5, -2, -1.5, -0.6, 0, 0.1, 2, 3]
- erfcx = functions.erfc(x)
- for i in range(len(x)):
- self.assertAlmostEqual(erfcx[i], math.erfc(x[i]), places=5)
-
- # ndarray
- x = numpy.array([[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]])
- erfcx = functions.erfc(x)
- for i in range(x.shape[0]):
- for j in range(x.shape[1]):
- self.assertAlmostEqual(erfcx[i, j], math.erfc(x[i, j]), places=5)
-
- def testAtanStepUp(self):
- """Compare atan_stepup with math.atan
-
- atan_stepup(x, a, b, c) = a * (0.5 + (arctan((x - b) / c) / pi))"""
- x0 = numpy.arange(100) / 6.33
- y0 = functions.atan_stepup(x0, 11.1, 22.2, 3.33)
-
- for x, y in zip(x0, y0):
- self.assertAlmostEqual(
- 11.1 * (0.5 + math.atan((x - 22.2) / 3.33) / math.pi),
- y
- )
-
- def testStepUp(self):
- """sanity check for step up:
-
- - derivative must be largest around the step center
- - max value must be close to height parameter
-
- """
- x0 = numpy.arange(1000)
- center = 444
- height = 1234
- fwhm = 210
- y0 = functions.sum_stepup(x0, height, center, fwhm)
-
- self.assertLess(max(y0), height)
- self.assertAlmostEqual(max(y0), height, places=1)
- self.assertAlmostEqual(min(y0), 0, places=1)
-
- deriv0 = _numerical_derivative(functions.sum_stepup, x0, [height, center, fwhm])
-
- # Test center position within +- 1 sample of max derivative
- index_max_deriv = numpy.argmax(deriv0)
- self.assertLess(abs(index_max_deriv - center),
- 1)
-
- def testStepDown(self):
- """sanity check for step down:
-
- - absolute value of derivative must be largest around the step center
- - max value must be close to height parameter
-
- """
- x0 = numpy.arange(1000)
- center = 444
- height = 1234
- fwhm = 210
- y0 = functions.sum_stepdown(x0, height, center, fwhm)
-
- self.assertLess(max(y0), height)
- self.assertAlmostEqual(max(y0), height, places=1)
- self.assertAlmostEqual(min(y0), 0, places=1)
-
- deriv0 = _numerical_derivative(functions.sum_stepdown, x0, [height, center, fwhm])
-
- # Test center position within +- 1 sample of max derivative
- index_min_deriv = numpy.argmax(-deriv0)
- self.assertLess(abs(index_min_deriv - center),
- 1)
-
- def testSlit(self):
- """sanity check for slit:
-
- - absolute value of derivative must be largest around the step center
- - max value must be close to height parameter
-
- """
- x0 = numpy.arange(1000)
- center = 444
- height = 1234
- fwhm = 210
- beamfwhm = 30
- y0 = functions.sum_slit(x0, height, center, fwhm, beamfwhm)
-
- self.assertAlmostEqual(max(y0), height, places=1)
- self.assertAlmostEqual(min(y0), 0, places=1)
-
- deriv0 = _numerical_derivative(functions.sum_slit, x0, [height, center, fwhm, beamfwhm])
-
- # Test step up center position (center - fwhm/2) within +- 1 sample of max derivative
- index_max_deriv = numpy.argmax(deriv0)
- self.assertLess(abs(index_max_deriv - (center - fwhm/2)),
- 1)
- # Test step down center position (center + fwhm/2) within +- 1 sample of min derivative
- index_min_deriv = numpy.argmin(deriv0)
- self.assertLess(abs(index_min_deriv - (center + fwhm/2)),
- 1)
-
-
-def _numerical_derivative(f, x, params=[], delta_factor=0.0001):
- """Compute the numerical derivative of ``f`` for all values of ``x``.
-
- :param f: function
- :param x: Array of evenly spaced abscissa values
- :param params: list of additional parameters
- :return: Array of derivative values
- """
- deltax = (x[1] - x[0]) * delta_factor
- y_plus = f(x + deltax, *params)
- y_minus = f(x - deltax, *params)
-
- return (y_plus - y_minus) / (2 * deltax)
-
-test_cases = (Test_functions,)
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/fit/test/test_peaks.py b/silx/math/fit/test/test_peaks.py
deleted file mode 100644
index 17eb75d..0000000
--- a/silx/math/fit/test/test_peaks.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Tests for peaks module
-"""
-
-import unittest
-import numpy
-import math
-
-from silx.math.fit import functions
-from silx.math.fit import peaks
-
-class Test_peak_search(unittest.TestCase):
- """
- Unit tests of peak_search on various types of multi-peak functions.
- """
- def setUp(self):
- self.x = numpy.arange(5000)
- # (height1, center1, fwhm1, ...)
- self.h_c_fwhm = (50, 500, 100,
- 50, 600, 80,
- 20, 2000, 100,
- 50, 2250, 110,
- 40, 3000, 99,
- 23, 4980, 80)
- # (height1, center1, fwhm1, eta1 ...)
- self.h_c_fwhm_eta = (50, 500, 100, 0.4,
- 50, 600, 80, 0.5,
- 20, 2000, 100, 0.6,
- 50, 2250, 110, 0.7,
- 40, 3000, 99, 0.8,
- 23, 4980, 80, 0.3,)
- # (height1, center1, fwhm11, fwhm21, ...)
- self.h_c_fwhm_fwhm = (50, 500, 100, 85,
- 50, 600, 80, 110,
- 20, 2000, 100, 100,
- 50, 2250, 110, 99,
- 40, 3000, 99, 110,
- 23, 4980, 80, 80,)
- # (height1, center1, fwhm11, fwhm21, eta1 ...)
- self.h_c_fwhm_fwhm_eta = (50, 500, 100, 85, 0.4,
- 50, 600, 80, 110, 0.5,
- 20, 2000, 100, 100, 0.6,
- 50, 2250, 110, 99, 0.7,
- 40, 3000, 99, 110, 0.8,
- 23, 4980, 80, 80, 0.3,)
- # (area1, center1, fwhm1, ...)
- self.a_c_fwhm = (2550, 500, 100,
- 2000, 600, 80,
- 500, 2000, 100,
- 4000, 2250, 110,
- 2300, 3000, 99,
- 3333, 4980, 80)
- # (area1, center1, fwhm1, eta1 ...)
- self.a_c_fwhm_eta = (500, 500, 100, 0.4,
- 500, 600, 80, 0.5,
- 200, 2000, 100, 0.6,
- 500, 2250, 110, 0.7,
- 400, 3000, 99, 0.8,
- 230, 4980, 80, 0.3,)
- # (area, position, fwhm, st_area_r, st_slope_r, lt_area_r, lt_slope_r, step_height_r)
- self.hypermet_params = (1000, 500, 200, 0.2, 100, 0.3, 100, 0.05,
- 1000, 1000, 200, 0.2, 100, 0.3, 100, 0.05,
- 1000, 2000, 200, 0.2, 100, 0.3, 100, 0.05,
- 1000, 2350, 200, 0.2, 100, 0.3, 100, 0.05,
- 1000, 3000, 200, 0.2, 100, 0.3, 100, 0.05,
- 1000, 4900, 200, 0.2, 100, 0.3, 100, 0.05,)
-
-
- def tearDown(self):
- pass
-
- def get_peaks(self, function, params):
- """
-
- :param function: Multi-peak function
- :param params: Parameter for this function
- :return: list of (peak, relevance) tuples
- """
- y = function(self.x, *params)
- return peaks.peak_search(y=y, fwhm=100, relevance_info=True)
-
- def testPeakSearch_various_functions(self):
- """Run peak search on a variety of synthetic functions, and
- check that result falls within +-25 samples of the actual peak
- (reasonable delta considering a fwhm of ~100 samples) and effects
- of overlapping peaks)."""
- f_p = ((functions.sum_gauss, self.h_c_fwhm ),
- (functions.sum_lorentz, self.h_c_fwhm),
- (functions.sum_pvoigt, self.h_c_fwhm_eta),
- (functions.sum_splitgauss, self.h_c_fwhm_fwhm),
- (functions.sum_splitlorentz, self.h_c_fwhm_fwhm),
- (functions.sum_splitpvoigt, self.h_c_fwhm_fwhm_eta),
- (functions.sum_agauss, self.a_c_fwhm),
- (functions.sum_fastagauss, self.a_c_fwhm),
- (functions.sum_alorentz, self.a_c_fwhm),
- (functions.sum_apvoigt, self.a_c_fwhm_eta),
- (functions.sum_ahypermet, self.hypermet_params),
- (functions.sum_fastahypermet, self.hypermet_params),)
-
- for function, params in f_p:
- peaks = self.get_peaks(function, params)
-
- self.assertEqual(len(peaks), 6,
- "Wrong number of peaks detected")
-
- for i in range(6):
- theoretical_peak_index = params[i*(len(params)//6) + 1]
- found_peak_index = peaks[i][0]
- self.assertLess(abs(found_peak_index - theoretical_peak_index), 25)
-
-
-test_cases = (Test_peak_search,)
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/histogram.py b/silx/math/histogram.py
deleted file mode 100644
index af9ee68..0000000
--- a/silx/math/histogram.py
+++ /dev/null
@@ -1,593 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-
-"""
-This module provides a function and a class to compute multidimensional
-histograms.
-
-
-Classes
-=======
-
-- :class:`Histogramnd` : multi dimensional histogram.
-- :class:`HistogramndLut` : optimized to compute several histograms from data sharing the same coordinates.
-
-Examples
-========
-
-Single histogram
-----------------
-
-Given some 3D data:
-
->>> import numpy as np
->>> shape = (10**7, 3)
->>> sample = np.random.random(shape) * 500
->>> weights = np.random.random((shape[0],))
-
-Computing the histogram with Histogramnd :
-
->>> from silx.math import Histogramnd
->>> n_bins = 35
->>> ranges = [[40., 150.], [-130., 250.], [0., 505]]
->>> histo, w_histo, edges = Histogramnd(sample, n_bins=n_bins, histo_range=ranges, weights=weights)
-
-Histogramnd can accumulate sets of data that don't have the same
-coordinates :
-
->>> from silx.math import Histogramnd
->>> histo_obj = Histogramnd(sample, n_bins=n_bins, histo_range=ranges, weights=weights)
->>> sample_2 = np.random.random(shape) * 200
->>> weights_2 = np.random.random((shape[0],))
->>> histo_obj.accumulate(sample_2, weights=weights_2)
-
-And then access the results:
-
->>> histo = histo_obj.histo
->>> weighted_histo = histo_obj.weighted_histo
-
-or even:
-
->>> histo, w_histo, edges = histo_obj
-
-Accumulating histograms (LUT)
------------------------------
-In some situations we need to compute the weighted histogram of several
-sets of data (weights) that have the same coordinates (sample).
-
-Again, some data (2 sets of weights) :
-
->>> import numpy as np
->>> shape = (10**7, 3)
->>> sample = np.random.random(shape) * 500
->>> weights_1 = np.random.random((shape[0],))
->>> weights_2 = np.random.random((shape[0],))
-
-And getting the result with HistogramLut :
-
->>> from silx.math import HistogramndLut
-
->>> n_bins = 35
->>> ranges = [[40., 150.], [-130., 250.], [0., 505]]
-
->>> histo_lut = HistogramndLut(sample, ranges, n_bins)
-
-First call, with weight_1 :
-
->>> histo_lut.accumulate(weights_1)
-
-Second call, with weight_2 :
-
->>> histo_lut.accumulate(weights_2)
-
-Retrieving the results (this is a copy of what's actually stored in
-this instance) :
-
->>> histo = histo_lut.histo
->>> w_histo = histo_lut.weighted_histo
-
-Note that the following code gives the same result, but the
-HistogramndLut instance does not store the accumulated weighted histogram.
-
-First call with weights_1
-
->>> histo, w_histo = histo_lut.apply_lut(weights_1)
-
-Second call with weights_2
-
->>> histo, w_histo = histo_lut.apply_lut(weights_2, histo=histo, weighted_histo=w_histo)
-
-Bin edges
----------
-When computing an histogram the caller is asked to provide the histogram
-range along each coordinates (parameter *histo_range*). This parameter must
-be given a [N, 2] array where N is the number of dimensions of the histogram.
-
-In other words, the caller must provide, for each dimension,
-the left edge of the first (*leftmost*) bin, and the right edge of the
-last (*rightmost*) bin.
-
-E.g. : for a 1D sample, for a histo_range equal to [0, 10] and n_bins=4, the
-bins ranges will be :
-
-* [0, 2.5[, [2.5, 5[, [5, 7.5[, [7.5, 10 **[** if last_bin_closed = **False**
-* [0, 2.5[, [2.5, 5[, [5, 7.5[, [7.5, 10 **]** if last_bin_closed = **True**
-
-....
-"""
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "02/10/2017"
-
-import numpy as np
-from .chistogramnd import chistogramnd as _chistogramnd # noqa
-from .chistogramnd_lut import histogramnd_get_lut as _histo_get_lut
-from .chistogramnd_lut import histogramnd_from_lut as _histo_from_lut
-
-
-class Histogramnd(object):
- """
- Computes the multidimensional histogram of some data.
- """
-
- def __init__(self,
- sample,
- histo_range,
- n_bins,
- weights=None,
- weight_min=None,
- weight_max=None,
- last_bin_closed=False,
- wh_dtype=None):
- """
- :param sample:
- The data to be histogrammed.
- Its shape must be either
- (N,) if it contains one dimensional coordinates,
- or an (N,D) array where the rows are the
- coordinates of points in a D dimensional space.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
-
- .. warning:: if sample is not a C_CONTIGUOUS ndarray (e.g : a non
- contiguous slice) then histogramnd will have to do make an internal
- copy.
- :type sample: :class:`numpy.array`
-
- :param histo_range:
- A (N, 2) array containing the histogram range along each dimension,
- where N is the sample's number of dimensions.
- :type histo_range: array_like
-
- :param n_bins:
- The number of bins :
- * a scalar (same number of bins for all dimensions)
- * a D elements array (number of bins for each dimensions)
- :type n_bins: scalar or array_like
-
- :param weights:
- A N elements numpy array of values associated with
- each sample.
- The values of the *weighted_histo* array
- returned by the function are equal to the sum of
- the weights associated with the samples falling
- into each bin.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
-
- .. note:: If None, the weighted histogram returned will be None.
- :type weights: *optional*, :class:`numpy.array`
-
- :param weight_min:
- Use this parameter to filter out all samples whose
- weights are lower than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_min: *optional*, scalar
-
- :param weight_max:
- Use this parameter to filter out all samples whose
- weights are higher than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
-
- :type weight_max: *optional*, scalar
-
- :param last_bin_closed:
- By default the last bin is half
- open (i.e.: [x,y) ; x included, y
- excluded), like all the other bins.
- Set this parameter to true if you want
- the LAST bin to be closed.
- :type last_bin_closed: *optional*, :class:`python.boolean`
-
- :param wh_dtype: type of the weighted histogram array.
- If not provided, the weighted histogram array will contain values
- of type numpy.double. Allowed values are : `numpy.double` and
- `numpy.float32`
- :type wh_dtype: *optional*, numpy data type
- """
-
- self.__histo_range = histo_range
- self.__n_bins = n_bins
- self.__last_bin_closed = last_bin_closed
- self.__wh_dtype = wh_dtype
-
- if sample is None:
- self.__data = [None, None, None]
- else:
- self.__data = _chistogramnd(sample,
- self.__histo_range,
- self.__n_bins,
- weights=weights,
- weight_min=weight_min,
- weight_max=weight_max,
- last_bin_closed=self.__last_bin_closed,
- wh_dtype=self.__wh_dtype)
-
- def __getitem__(self, key):
- """
- If necessary, results can be unpacked from an instance of Histogramnd :
- *histogram*, *weighted histogram*, *bins edge*.
-
- Example :
-
- .. code-block:: python
-
- histo, w_histo, edges = Histogramnd(sample, histo_range, n_bins, weights)
-
- """
- return self.__data[key]
-
- def accumulate(self,
- sample,
- weights=None,
- weight_min=None,
- weight_max=None):
- """
- Computes the multidimensional histogram of some data and accumulates it
- into the histogram held by this instance of Histogramnd.
-
- :param sample:
- The data to be histogrammed.
- Its shape must be either
- (N,) if it contains one dimensional coordinates,
- or an (N,D) array where the rows are the
- coordinates of points in a D dimensional space.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
-
- .. warning:: if sample is not a C_CONTIGUOUS ndarray (e.g : a non
- contiguous slice) then histogramnd will have to do make an internal
- copy.
- :type sample: :class:`numpy.array`
-
- :param weights:
- A N elements numpy array of values associated with
- each sample.
- The values of the *weighted_histo* array
- returned by the function are equal to the sum of
- the weights associated with the samples falling
- into each bin.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
-
- .. note:: If None, the weighted histogram returned will be None.
- :type weights: *optional*, :class:`numpy.array`
-
- :param weight_min:
- Use this parameter to filter out all samples whose
- weights are lower than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_min: *optional*, scalar
-
- :param weight_max:
- Use this parameter to filter out all samples whose
- weights are higher than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_max: *optional*, scalar
- """
- result = _chistogramnd(sample,
- self.__histo_range,
- self.__n_bins,
- weights=weights,
- weight_min=weight_min,
- weight_max=weight_max,
- last_bin_closed=self.__last_bin_closed,
- histo=self.__data[0],
- weighted_histo=self.__data[1],
- wh_dtype=self.__wh_dtype)
- if self.__data[0] is None:
- self.__data = result
- elif self.__data[1] is None and result[1] is not None:
- self.__data = result
-
- histo = property(lambda self: self[0])
- """ Histogram array, or None if this instance was initialized without
- <sample> and accumulate has not been called yet.
-
- .. note:: this is a **reference** to the array store in this
- Histogramnd instance, use with caution.
- """
- weighted_histo = property(lambda self: self[1])
- """ Weighted Histogram, or None if this instance was initialized without
- <sample>, or no weights have been passed to __init__ nor accumulate.
-
- .. note:: this is a **reference** to the array store in this
- Histogramnd instance, use with caution.
- """
- edges = property(lambda self: self[2])
- """ Bins edges, or None if this instance was initialized without
- <sample> and accumulate has not been called yet.
- """
-
-
-class HistogramndLut(object):
- """
- The HistogramndLut class allows you to bin data onto a regular grid.
- The use of HistogramndLut is interesting when several sets of data that
- share the same coordinates (*sample*) have to be mapped onto the same grid.
- """
-
- def __init__(self,
- sample,
- histo_range,
- n_bins,
- last_bin_closed=False,
- dtype=None):
- """
- :param sample:
- The coordinates of the data to be histogrammed.
- Its shape must be either (N,) if it contains one dimensional
- coordinates, or an (N, D) array where the rows are the
- coordinates of points in a D dimensional space.
- The following dtypes are supported : :class:`numpy.float64`,
- :class:`numpy.float32`, :class:`numpy.int32`.
- :type sample: :class:`numpy.array`
-
- :param histo_range:
- A (N, 2) array containing the histogram range along each dimension,
- where N is the sample's number of dimensions.
- :type histo_range: array_like
-
- :param n_bins:
- The number of bins :
- * a scalar (same number of bins for all dimensions)
- * a D elements array (number of bins for each dimensions)
- :type n_bins: scalar or array_like
-
- :param dtype: data type of the weighted histogram. If None, the data type
- will be the same as the first weights array provided (on first call of
- the instance).
- :type dtype: `numpy.dtype`
-
- :param last_bin_closed:
- By default the last bin is half
- open (i.e.: [x,y) ; x included, y
- excluded), like all the other bins.
- Set this parameter to true if you want
- the LAST bin to be closed.
- :type last_bin_closed: *optional*, :class:`python.boolean`
- """
- lut, histo, edges = _histo_get_lut(sample,
- histo_range,
- n_bins,
- last_bin_closed=last_bin_closed)
-
- self.__n_bins = np.array(histo.shape)
- self.__histo_range = histo_range
- self.__lut = lut
- self.__histo = None
- self.__weighted_histo = None
- self.__edges = edges
- self.__dtype = dtype
- self.__shape = histo.shape
- self.__last_bin_closed = last_bin_closed
- self.clear()
-
- def clear(self):
- """
- Resets the instance (zeroes the histograms).
- """
- self.__weighted_histo = None
- self.__histo = None
-
- @property
- def lut(self):
- """
- Copy of the Lut
- """
- return self.__lut.copy()
-
- def histo(self, copy=True):
- """
- Histogram (a copy of it), or None if `~accumulate` has not been called yet
- (or clear was just called).
- If *copy* is set to False then the actual reference to the array is
- returned *(use with caution)*.
- """
- if copy and self.__histo is not None:
- return self.__histo.copy()
- return self.__histo
-
- def weighted_histo(self, copy=True):
- """
- Weighted histogram (a copy of it), or None if `~accumulate` has not been called yet
- (or clear was just called). If *copy* is set to False then the actual
- reference to the array is returned *(use with caution)*.
- """
- if copy and self.__weighted_histo is not None:
- return self.__weighted_histo.copy()
- return self.__weighted_histo
-
- @property
- def histo_range(self):
- """
- Bins ranges.
- """
- return self.__histo_range.copy()
-
- @property
- def n_bins(self):
- """
- Number of bins in each direction.
- """
- return self.__n_bins.copy()
-
- @property
- def bins_edges(self):
- """
- Bins edges of the histograms, one array for each dimensions.
- """
- return tuple([edges[:] for edges in self.__edges])
-
- @property
- def last_bin_closed(self):
- """
- Returns True if the rightmost bin in each dimension is close (i.e :
- values equal to the rightmost bin edge is included in the bin).
- """
- return self.__last_bin_closed
-
- def accumulate(self,
- weights,
- weight_min=None,
- weight_max=None):
- """
- Computes the multidimensional histogram of some data and adds it to
- the current histogram stored by this instance. The results can be
- retrieved with the :attr:`~.histo` and :attr:`~.weighted_histo`
- properties.
-
- :param weights:
- A numpy array of values associated with each sample. The number of
- elements in the array must be the same as the number of samples
- provided at instantiation time.
- :type histo_range: array_like
-
- :param weight_min:
- Use this parameter to filter out all samples whose
- weights are lower than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_min: *optional*, scalar
-
- :param weight_max:
- Use this parameter to filter out all samples whose
- weights are higher than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
-
- :type weight_max: *optional*, scalar
- """
- if self.__dtype is None:
- self.__dtype = weights.dtype
-
- histo, w_histo = _histo_from_lut(weights,
- self.__lut,
- histo=self.__histo,
- weighted_histo=self.__weighted_histo,
- shape=self.__shape,
- dtype=self.__dtype,
- weight_min=weight_min,
- weight_max=weight_max)
-
- if self.__histo is None:
- self.__histo = histo
-
- if self.__weighted_histo is None:
- self.__weighted_histo = w_histo
-
- def apply_lut(self,
- weights,
- histo=None,
- weighted_histo=None,
- weight_min=None,
- weight_max=None):
- """
- Computes the multidimensional histogram of some data and returns the
- result (it is NOT added to the current histogram stored by this
- instance).
-
- :param weights:
- A numpy array of values associated with each sample. The number of
- elements in the array must be the same as the number of samples
- provided at instantiation time.
- :type histo_range: array_like
-
- :param histo:
- Use this parameter if you want to pass your
- own histogram array instead of the one
- created by this function. New values
- will be added to this array. The returned array
- will then be this one.
- :type histo: *optional*, :class:`numpy.array`
-
- :param weighted_histo:
- Use this parameter if you want to pass your
- own weighted histogram array instead of
- the created by this function. New
- values will be added to this array. The returned array
- will then be this one (same reference).
- :type weighted_histo: *optional*, :class:`numpy.array`
-
- :param weight_min:
- Use this parameter to filter out all samples whose
- weights are lower than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_min: *optional*, scalar
-
- :param weight_max:
- Use this parameter to filter out all samples whose
- weights are higher than this value.
-
- .. note:: This value will be cast to the same type
- as *weights*.
- :type weight_max: *optional*, scalar
- """
- histo, w_histo = _histo_from_lut(weights,
- self.__lut,
- histo=histo,
- weighted_histo=weighted_histo,
- shape=self.__shape,
- dtype=self.__dtype,
- weight_min=weight_min,
- weight_max=weight_max)
- self.__dtype = w_histo.dtype
- return histo, w_histo
-
-if __name__ == '__main__':
- pass
diff --git a/silx/math/histogramnd/include/histogramnd_c.h b/silx/math/histogramnd/include/histogramnd_c.h
deleted file mode 100644
index abe464f..0000000
--- a/silx/math/histogramnd/include/histogramnd_c.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#ifndef HISTOGRAMND_C_H
-#define HISTOGRAMND_C_H
-
-/* checking for MSVC version because VS 2008 doesnt fully support C99
- so inttypes.h and stdint.h are not provided with the compiler. */
-#if defined(_MSC_VER) && _MSC_VER < 1600
- #include "msvc/stdint.h"
-#else
- #include <inttypes.h>
-#endif
-
-#include "templates.h"
-
-/** Allowed flag values for the i_opt_flags arguments.
- */
-typedef enum {
- HISTO_NONE = 0, /**< No options. */
- HISTO_WEIGHT_MIN = 1, /**< Filter weights with i_weight_min. */
- HISTO_WEIGHT_MAX = 1<<1, /**< Filter weights with i_weight_max. */
- HISTO_LAST_BIN_CLOSED = 1<<2 /**< Last bin is closed. */
-} histo_opt_type;
-
-/** Return codees for the histogramnd function.
- */
-typedef enum {
- HISTO_OK = 0, /**< No error. */
- HISTO_ERR_ALLOC /**< Failed to allocate memory. */
-} histo_rc_t;
-
-/*=====================
- * double sample, double cumul
- * ====================
-*/
-
-int histogramnd_double_double_double(double *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max);
-
-int histogramnd_double_float_double(double *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max);
-
-int histogramnd_double_int32_t_double(double *i_sample,
- int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- int32_t i_weight_min,
- int32_t i_weight_max);
-
-/*=====================
- * float sample, double cumul
- * ====================
-*/
-int histogramnd_float_double_double(float *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max);
-
-int histogramnd_float_float_double(float *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max);
-
-int histogramnd_float_int32_t_double(float *i_sample,
- int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- int32_t i_weight_min,
- int32_t i_weight_max);
-
-/*=====================
- * int32_t sample, double cumul
- * ====================
-*/
-int histogramnd_int32_t_double_double(int32_t *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max);
-
-int histogramnd_int32_t_float_double(int32_t *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max);
-
-int histogramnd_int32_t_int32_t_double(int32_t *i_sample,
- int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- double *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- int32_t i_weight_min,
- int32_t i_weight_max);
-
-/*=====================
- * double sample, float cumul
- * ====================
-*/
-
-int histogramnd_double_double_float(double *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max);
-
-int histogramnd_double_float_float(double *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max);
-
-int histogramnd_double_int32_t_float(double *i_sample,
- int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- int32_t i_weight_min,
- int32_t i_weight_max);
-
-/*=====================
- * float sample, float cumul
- * ====================
-*/
-int histogramnd_float_double_float(float *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max);
-
-int histogramnd_float_float_float(float *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max);
-
-int histogramnd_float_int32_t_float(float *i_sample,
- int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- int32_t i_weight_min,
- int32_t i_weight_max);
-
-/*=====================
- * int32_t sample, double cumul
- * ====================
-*/
-int histogramnd_int32_t_double_float(int32_t *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max);
-
-int histogramnd_int32_t_float_float(int32_t *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max);
-
-int histogramnd_int32_t_int32_t_float(int32_t *i_sample,
- int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- uint32_t *o_histo,
- float *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- int32_t i_weight_min,
- int32_t i_weight_max);
-
-#endif /* #define HISTOGRAMND_C_H */
diff --git a/silx/math/histogramnd/include/msvc/stdint.h b/silx/math/histogramnd/include/msvc/stdint.h
deleted file mode 100644
index e236bb0..0000000
--- a/silx/math/histogramnd/include/msvc/stdint.h
+++ /dev/null
@@ -1,247 +0,0 @@
-// ISO C9x compliant stdint.h for Microsoft Visual Studio
-// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
-//
-// Copyright (c) 2006-2008 Alexander Chemeris
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// 3. The name of the author may be used to endorse or promote products
-// derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#ifndef _MSC_VER // [
-#error "Use this header only with Microsoft Visual C++ compilers!"
-#endif // _MSC_VER ]
-
-#ifndef _MSC_STDINT_H_ // [
-#define _MSC_STDINT_H_
-
-#if _MSC_VER > 1000
-#pragma once
-#endif
-
-#include <limits.h>
-
-// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
-// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
-// or compiler give many errors like this:
-// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
-#ifdef __cplusplus
-extern "C" {
-#endif
-# include <wchar.h>
-#ifdef __cplusplus
-}
-#endif
-
-// Define _W64 macros to mark types changing their size, like intptr_t.
-#ifndef _W64
-# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
-# define _W64 __w64
-# else
-# define _W64
-# endif
-#endif
-
-
-// 7.18.1 Integer types
-
-// 7.18.1.1 Exact-width integer types
-
-// Visual Studio 6 and Embedded Visual C++ 4 doesn't
-// realize that, e.g. char has the same size as __int8
-// so we give up on __intX for them.
-#if (_MSC_VER < 1300)
- typedef char int8_t;
- typedef short int16_t;
- typedef int int32_t;
- typedef unsigned char uint8_t;
- typedef unsigned short uint16_t;
- typedef unsigned int uint32_t;
-#else
- typedef __int8 int8_t;
- typedef __int16 int16_t;
- typedef __int32 int32_t;
- typedef unsigned __int8 uint8_t;
- typedef unsigned __int16 uint16_t;
- typedef unsigned __int32 uint32_t;
-#endif
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-
-
-// 7.18.1.2 Minimum-width integer types
-typedef int8_t int_least8_t;
-typedef int16_t int_least16_t;
-typedef int32_t int_least32_t;
-typedef int64_t int_least64_t;
-typedef uint8_t uint_least8_t;
-typedef uint16_t uint_least16_t;
-typedef uint32_t uint_least32_t;
-typedef uint64_t uint_least64_t;
-
-// 7.18.1.3 Fastest minimum-width integer types
-typedef int8_t int_fast8_t;
-typedef int16_t int_fast16_t;
-typedef int32_t int_fast32_t;
-typedef int64_t int_fast64_t;
-typedef uint8_t uint_fast8_t;
-typedef uint16_t uint_fast16_t;
-typedef uint32_t uint_fast32_t;
-typedef uint64_t uint_fast64_t;
-
-// 7.18.1.4 Integer types capable of holding object pointers
-#ifdef _WIN64 // [
- typedef __int64 intptr_t;
- typedef unsigned __int64 uintptr_t;
-#else // _WIN64 ][
- typedef _W64 int intptr_t;
- typedef _W64 unsigned int uintptr_t;
-#endif // _WIN64 ]
-
-// 7.18.1.5 Greatest-width integer types
-typedef int64_t intmax_t;
-typedef uint64_t uintmax_t;
-
-
-// 7.18.2 Limits of specified-width integer types
-
-#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
-
-// 7.18.2.1 Limits of exact-width integer types
-#define INT8_MIN ((int8_t)_I8_MIN)
-#define INT8_MAX _I8_MAX
-#define INT16_MIN ((int16_t)_I16_MIN)
-#define INT16_MAX _I16_MAX
-#define INT32_MIN ((int32_t)_I32_MIN)
-#define INT32_MAX _I32_MAX
-#define INT64_MIN ((int64_t)_I64_MIN)
-#define INT64_MAX _I64_MAX
-#define UINT8_MAX _UI8_MAX
-#define UINT16_MAX _UI16_MAX
-#define UINT32_MAX _UI32_MAX
-#define UINT64_MAX _UI64_MAX
-
-// 7.18.2.2 Limits of minimum-width integer types
-#define INT_LEAST8_MIN INT8_MIN
-#define INT_LEAST8_MAX INT8_MAX
-#define INT_LEAST16_MIN INT16_MIN
-#define INT_LEAST16_MAX INT16_MAX
-#define INT_LEAST32_MIN INT32_MIN
-#define INT_LEAST32_MAX INT32_MAX
-#define INT_LEAST64_MIN INT64_MIN
-#define INT_LEAST64_MAX INT64_MAX
-#define UINT_LEAST8_MAX UINT8_MAX
-#define UINT_LEAST16_MAX UINT16_MAX
-#define UINT_LEAST32_MAX UINT32_MAX
-#define UINT_LEAST64_MAX UINT64_MAX
-
-// 7.18.2.3 Limits of fastest minimum-width integer types
-#define INT_FAST8_MIN INT8_MIN
-#define INT_FAST8_MAX INT8_MAX
-#define INT_FAST16_MIN INT16_MIN
-#define INT_FAST16_MAX INT16_MAX
-#define INT_FAST32_MIN INT32_MIN
-#define INT_FAST32_MAX INT32_MAX
-#define INT_FAST64_MIN INT64_MIN
-#define INT_FAST64_MAX INT64_MAX
-#define UINT_FAST8_MAX UINT8_MAX
-#define UINT_FAST16_MAX UINT16_MAX
-#define UINT_FAST32_MAX UINT32_MAX
-#define UINT_FAST64_MAX UINT64_MAX
-
-// 7.18.2.4 Limits of integer types capable of holding object pointers
-#ifdef _WIN64 // [
-# define INTPTR_MIN INT64_MIN
-# define INTPTR_MAX INT64_MAX
-# define UINTPTR_MAX UINT64_MAX
-#else // _WIN64 ][
-# define INTPTR_MIN INT32_MIN
-# define INTPTR_MAX INT32_MAX
-# define UINTPTR_MAX UINT32_MAX
-#endif // _WIN64 ]
-
-// 7.18.2.5 Limits of greatest-width integer types
-#define INTMAX_MIN INT64_MIN
-#define INTMAX_MAX INT64_MAX
-#define UINTMAX_MAX UINT64_MAX
-
-// 7.18.3 Limits of other integer types
-
-#ifdef _WIN64 // [
-# define PTRDIFF_MIN _I64_MIN
-# define PTRDIFF_MAX _I64_MAX
-#else // _WIN64 ][
-# define PTRDIFF_MIN _I32_MIN
-# define PTRDIFF_MAX _I32_MAX
-#endif // _WIN64 ]
-
-#define SIG_ATOMIC_MIN INT_MIN
-#define SIG_ATOMIC_MAX INT_MAX
-
-#ifndef SIZE_MAX // [
-# ifdef _WIN64 // [
-# define SIZE_MAX _UI64_MAX
-# else // _WIN64 ][
-# define SIZE_MAX _UI32_MAX
-# endif // _WIN64 ]
-#endif // SIZE_MAX ]
-
-// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
-#ifndef WCHAR_MIN // [
-# define WCHAR_MIN 0
-#endif // WCHAR_MIN ]
-#ifndef WCHAR_MAX // [
-# define WCHAR_MAX _UI16_MAX
-#endif // WCHAR_MAX ]
-
-#define WINT_MIN 0
-#define WINT_MAX _UI16_MAX
-
-#endif // __STDC_LIMIT_MACROS ]
-
-
-// 7.18.4 Limits of other integer types
-
-#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
-
-// 7.18.4.1 Macros for minimum-width integer constants
-
-#define INT8_C(val) val##i8
-#define INT16_C(val) val##i16
-#define INT32_C(val) val##i32
-#define INT64_C(val) val##i64
-
-#define UINT8_C(val) val##ui8
-#define UINT16_C(val) val##ui16
-#define UINT32_C(val) val##ui32
-#define UINT64_C(val) val##ui64
-
-// 7.18.4.2 Macros for greatest-width integer constants
-#define INTMAX_C INT64_C
-#define UINTMAX_C UINT64_C
-
-#endif // __STDC_CONSTANT_MACROS ]
-
-
-#endif // _MSC_STDINT_H_ ]
diff --git a/silx/math/histogramnd/include/templates.h b/silx/math/histogramnd/include/templates.h
deleted file mode 100644
index 490eed3..0000000
--- a/silx/math/histogramnd/include/templates.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#ifndef TEMPLATES_H_
-#define TEMPLATES_H_
-
-#define CONCAT(X,Y,Z,T) X##_##Y##_##Z##_##T
-#define TEMPLATE(X,Y,Z,T) CONCAT(X,Y,Z,T)
-
-#endif
diff --git a/silx/math/histogramnd/src/histogramnd_c.c b/silx/math/histogramnd/src/histogramnd_c.c
deleted file mode 100644
index fc9d77e..0000000
--- a/silx/math/histogramnd/src/histogramnd_c.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#include "histogramnd_c.h"
-
-/*=====================
- * double sample, double cumul
- * =====================
-*/
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T double
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T double
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T double
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T float
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T double
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T int32_t
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-/*=====================
- * float sample, double cumul
- * =====================
-*/
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T float
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T double
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T float
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T float
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T float
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T int32_t
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-/*=====================
- * int32_t sample, double cumul
- * =====================
-*/
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T int32_t
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T double
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T int32_t
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T float
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T int32_t
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T int32_t
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T double
-#include "histogramnd_template.c"
-
-
-/*=====================
- * double sample, float cumul
- * =====================
-*/
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T double
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T double
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T double
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T float
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T double
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T int32_t
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-/*=====================
- * float sample, float cumul
- * =====================
-*/
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T float
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T double
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T float
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T float
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T float
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T int32_t
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-/*=====================
- * int32_t sample, float cumul
- * =====================
-*/
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T int32_t
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T double
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T int32_t
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T float
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
-
-#ifdef HISTO_SAMPLE_T
-#undef HISTO_SAMPLE_T
-#endif
-#define HISTO_SAMPLE_T int32_t
-#ifdef HISTO_WEIGHT_T
-#undef HISTO_WEIGHT_T
-#endif
-#define HISTO_WEIGHT_T int32_t
-#ifdef HISTO_CUMUL_T
-#undef HISTO_CUMUL_T
-#endif
-#define HISTO_CUMUL_T float
-#include "histogramnd_template.c"
diff --git a/silx/math/histogramnd/src/histogramnd_template.c b/silx/math/histogramnd/src/histogramnd_template.c
deleted file mode 100644
index 0276bb4..0000000
--- a/silx/math/histogramnd/src/histogramnd_template.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-#include "templates.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <stdarg.h>
-
-#ifdef HISTO_SAMPLE_T
-#ifdef HISTO_WEIGHT_T
-#ifdef HISTO_CUMUL_T
-
-int TEMPLATE(histogramnd, HISTO_SAMPLE_T, HISTO_WEIGHT_T, HISTO_CUMUL_T)
- (HISTO_SAMPLE_T *i_sample,
- HISTO_WEIGHT_T *i_weights,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bins,
- uint32_t *o_histo,
- HISTO_CUMUL_T *o_cumul,
- double *o_bin_edges,
- int i_opt_flags,
- HISTO_WEIGHT_T i_weight_min,
- HISTO_WEIGHT_T i_weight_max)
-{
- /* some counters */
- int i = 0, j = 0;
- long elem_idx = 0;
-
- HISTO_WEIGHT_T * weight_ptr = 0;
- HISTO_SAMPLE_T elem_coord = 0.;
-
- /* computed bin index (i_sample -> grid) */
- long bin_idx = 0;
-
- double * g_min = 0;
- double * g_max = 0;
- double * range = 0;
-
- /* ================================
- * Parsing options, if any.
- * ================================
- */
-
- int filt_min_weight = 0;
- int filt_max_weight = 0;
- int last_bin_closed = 0;
-
- /* Testing the option flags */
- if(i_opt_flags & HISTO_WEIGHT_MIN)
- {
- filt_min_weight = 1;
- }
-
- if(i_opt_flags & HISTO_WEIGHT_MAX)
- {
- filt_max_weight = 1;
- }
-
- if(i_opt_flags & HISTO_LAST_BIN_CLOSED)
- {
- last_bin_closed = 1;
- }
-
- /* storing the min & max bin coordinates in their own arrays because
- * i_bin_ranges = [[min0, max0], [min1, max1], ...]
- * (mostly for the sake of clarity)
- * (maybe faster access too?)
- */
- g_min = (double *) malloc(i_n_dim *sizeof(double));
- g_max = (double *) malloc(i_n_dim * sizeof(double));
- /* range used to convert from i_coords to bin indices in the grid */
- range = (double *) malloc(i_n_dim * sizeof(double));
-
- if(!g_min || !g_max || !range)
- {
- free(g_min);
- free(g_max);
- free(range);
- return HISTO_ERR_ALLOC;
- }
-
- j = 0;
- for(i=0; i<i_n_dim; i++)
- {
- g_min[i] = i_bin_ranges[i*2];
- g_max[i] = i_bin_ranges[i*2+1];
- range[i] = g_max[i]-g_min[i];
-
- for(bin_idx=0; bin_idx<i_n_bins[i]; j++, bin_idx++)
- {
- o_bin_edges[j] = g_min[i] +
- bin_idx * (range[i] / i_n_bins[i]);
- }
- o_bin_edges[j++] = g_max[i];
- }
-
- weight_ptr = i_weights;
-
- if(!i_weights)
- {
- /* if weights are not provided there no point in trying to filter them
- * (!! careful if you change this, some code below relies on it !!)
- */
- filt_min_weight = 0;
- filt_max_weight = 0;
-
- /* If the weights array is not provided then there is no point
- * updating the weighted histogram, only the bin counts (o_histo)
- * will be filled.
- * (!! careful if you change this, some code below relies on it !!)
- */
- o_cumul = 0;
- }
-
- /* tried to use pointers instead of indices here, but it didn't
- * seem any faster (probably because the compiler
- * optimizes stuff anyway),
- * so i'm keeping the "indices" version, for the sake of clarity
- */
- for(elem_idx=0;
- elem_idx<i_n_elem*i_n_dim;
- elem_idx+=i_n_dim, weight_ptr++)
- {
- /* no testing the validity of weight_ptr here, because if it is NULL
- * then filt_min_weight/filt_max_weight will be 0.
- * (see code above)
- */
- if(filt_min_weight && *weight_ptr<i_weight_min)
- {
- continue;
- }
- if(filt_max_weight && *weight_ptr>i_weight_max)
- {
- continue;
- }
-
- bin_idx = 0;
-
- for(i=0; i<i_n_dim; i++)
- {
- elem_coord = i_sample[elem_idx+i];
-
- /* =====================
- * Element is rejected if any of the following is NOT true :
- * 1. coordinate is >= than the minimum value
- * 2. coordinate is <= than the maximum value
- * 3. coordinate==maximum value and last_bin_closed is True
- * =====================
- */
- if(elem_coord<g_min[i])
- {
- bin_idx = -1;
- break;
- }
-
- /* Here we make the assumption that most of the time
- * there will be more coordinates inside the grid interval
- * (one test)
- * than coordinates higher or equal to the max
- * (two tests)
- */
- if(elem_coord<g_max[i])
- {
- /* Warning : the following factorization seems to
- * increase the effect of precision error.
- * bin_idx = (long)floor(
- * (bin_idx +
- * (elem_coord-g_min[i])/range[i]) *
- * i_n_bins[i]
- * );
- */
-
- /* Not using floor to speed up things.
- * We don't (?) need all the error checking provided by
- * the built-in floor().
- * Also the value is supposed to be always positive.
- */
- bin_idx = bin_idx * i_n_bins[i] +
- (long)(
- ((elem_coord-g_min[i]) * i_n_bins[i]) /
- range[i]
- );
- }
- else /* ===> elem_coord>=g_max[i] */
- {
- /* if equal and the last bin is closed :
- * put it in the last bin
- * else : discard
- */
- if(last_bin_closed && elem_coord==g_max[i])
- {
- bin_idx = (bin_idx + 1) * i_n_bins[i] - 1;
- }
- else
- {
- bin_idx = -1;
- break;
- }
- } /* if(elem_coord<g_max[i]) */
-
- } /* for(i=0; i<i_n_dim; i++) */
-
- /* element is out of the grid */
- if(bin_idx==-1)
- {
- continue;
- }
-
- if(o_histo)
- {
- o_histo[bin_idx] += 1;
- }
- if(o_cumul)
- {
- /* not testing the pointer since o_cumul is null if
- * i_weights is null.
- */
- o_cumul[bin_idx] += (HISTO_CUMUL_T) *weight_ptr;
- }
-
- } /* for(elem_idx=0; elem_idx<i_n_elem*i_n_dim; elem_idx+=i_n_dim) */
-
- free(g_min);
- free(g_max);
- free(range);
-
- /* For now just returning 0 (OK) since all the checks are done in
- * python. This might change later if people want to call this
- * function directly from C (might have to implement error codes).
- */
- return HISTO_OK;
-}
-
-#endif
-#endif
-#endif
diff --git a/silx/math/histogramnd_c.pxd b/silx/math/histogramnd_c.pxd
deleted file mode 100644
index 35db529..0000000
--- a/silx/math/histogramnd_c.pxd
+++ /dev/null
@@ -1,299 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "01/02/2016"
-
-cimport numpy as cnumpy
-
-cdef extern from "histogramnd_c.h":
-
- ctypedef enum histo_opt_type:
- HISTO_NONE
- HISTO_WEIGHT_MIN
- HISTO_WEIGHT_MAX
- HISTO_LAST_BIN_CLOSED
-
- ctypedef enum histo_rc_t:
- HISTO_OK
- HISTO_ERR_ALLOC
-
- # =====================
- # double sample, double cumul
- # =====================
-
- int histogramnd_double_double_double(double *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max) nogil
-
- int histogramnd_double_float_double(double *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max) nogil
-
- int histogramnd_double_int32_t_double(double *i_sample,
- cnumpy.int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- cnumpy.int32_t i_weight_min,
- cnumpy.int32_t i_weight_max) nogil
-
- # =====================
- # float sample, double cumul
- # =====================
-
- int histogramnd_float_double_double(float *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max) nogil
-
- int histogramnd_float_float_double(float *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max) nogil
-
- int histogramnd_float_int32_t_double(float *i_sample,
- cnumpy.int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- cnumpy.int32_t i_weight_min,
- cnumpy.int32_t i_weight_max) nogil
-
- # =====================
- # numpy.int32_t sample, double cumul
- # =====================
-
- int histogramnd_int32_t_double_double(cnumpy.int32_t *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max) nogil
-
- int histogramnd_int32_t_float_double(cnumpy.int32_t *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max) nogil
-
- int histogramnd_int32_t_int32_t_double(cnumpy.int32_t *i_sample,
- cnumpy.int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- double *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- cnumpy.int32_t i_weight_min,
- cnumpy.int32_t i_weight_max) nogil
-
- # =====================
- # double sample, float cumul
- # =====================
-
- int histogramnd_double_double_float(double *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max) nogil
-
- int histogramnd_double_float_float(double *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max) nogil
-
- int histogramnd_double_int32_t_float(double *i_sample,
- cnumpy.int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- cnumpy.int32_t i_weight_min,
- cnumpy.int32_t i_weight_max) nogil
-
- # =====================
- # float sample, float cumul
- # =====================
-
- int histogramnd_float_double_float(float *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max) nogil
-
- int histogramnd_float_float_float(float *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max) nogil
-
- int histogramnd_float_int32_t_float(float *i_sample,
- cnumpy.int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- cnumpy.int32_t i_weight_min,
- cnumpy.int32_t i_weight_max) nogil
-
- # =====================
- # numpy.int32_t sample, float cumul
- # =====================
-
- int histogramnd_int32_t_double_float(cnumpy.int32_t *i_sample,
- double *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- double i_weight_min,
- double i_weight_max) nogil
-
- int histogramnd_int32_t_float_float(cnumpy.int32_t *i_sample,
- float *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- float i_weight_min,
- float i_weight_max) nogil
-
- int histogramnd_int32_t_int32_t_float(cnumpy.int32_t *i_sample,
- cnumpy.int32_t *i_weigths,
- int i_n_dim,
- int i_n_elem,
- double *i_bin_ranges,
- int *i_n_bin,
- cnumpy.uint32_t *o_histo,
- float *o_cumul,
- double * bin_edges,
- int i_opt_flags,
- cnumpy.int32_t i_weight_min,
- cnumpy.int32_t i_weight_max) nogil
diff --git a/silx/math/include/math_compatibility.h b/silx/math/include/math_compatibility.h
deleted file mode 100644
index 3d69c0c..0000000
--- a/silx/math/include/math_compatibility.h
+++ /dev/null
@@ -1,53 +0,0 @@
-# /*##########################################################################
-#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-/* This header provides libc math functions and macros across platforms.
-
- Needed as VisualStudio 2008 (i.e., Python2.7) is missing some functions/macros.
-*/
-
-#ifndef __MATH_COMPATIBILITY_H__
-#define __MATH_COMPATIBILITY_H__
-
-#include <math.h>
-
-#ifndef INFINITY
-#define INFINITY (DBL_MAX+DBL_MAX)
-#endif
-
-#ifndef NAN
-#define NAN (INFINITY-INFINITY)
-#endif
-
-#if (defined (_MSC_VER) && _MSC_VER < 1800)
-#include <float.h>
-
-/* Make sure asinh returns -inf rather than NaN for v=-inf */
-#define asinh(v) (v == -INFINITY ? v : log((v) + sqrt((v)*(v) + 1)))
-
-#define isnan(v) _isnan(v)
-#define isfinite(v) _finite(v)
-#define lrint(v) ((long int) (v))
-#endif
-
-#endif /*__MATH_COMPATIBILITY_H__*/
diff --git a/silx/math/interpolate.pyx b/silx/math/interpolate.pyx
deleted file mode 100644
index c79224a..0000000
--- a/silx/math/interpolate.pyx
+++ /dev/null
@@ -1,165 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""This module provides :func:`interp3d` to perform trilinear interpolation.
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "11/07/2019"
-
-
-import cython
-from cython.parallel import prange
-import numpy
-
-cimport cython
-from libc.math cimport floor
-cimport numpy as cnumpy
-
-
-ctypedef fused _floating:
- float
- double
-
-ctypedef fused _floating_pts:
- float
- double
-
-
-@cython.initializedcheck(False)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-cdef inline double trilinear_interpolation(
- _floating[:, :, :] values,
- _floating_pts pos0,
- _floating_pts pos1,
- _floating_pts pos2,
- double fill_value) nogil:
- """Evaluate the trilinear interpolation at a given position
-
- :param values: 3D dataset from which to do the interpolation
- :param pos0: Dimension 0 coordinate at which to evaluate the interpolation
- :param pos1: Dimension 1 coordinate at which to evaluate the interpolation
- :param pos2: Dimension 2 coordinate at which to evaluate the interpolation
- :param fill_value: Value to return for points outside data
- """
- cdef:
- int i0, i1, i2 # Indices
- int i0_plus1, i1_plus1, i2_plus1 # Indices+1
- double delta
- double c00, c01, c10, c11, c0, c1
- double c
-
- if (pos0 < 0. or pos0 > (values.shape[0] -1) or
- pos1 < 0. or pos1 > (values.shape[1] -1) or
- pos2 < 0. or pos2 > (values.shape[2] -1)):
- return fill_value
-
- i0 = < int > floor(pos0)
- i1 = < int > floor(pos1)
- i2 = < int > floor(pos2)
-
- # Clip i+1 indices to data volume
- # In this case, corresponding dX is 0.
- i0_plus1 = min(i0 + 1, values.shape[0] - 1)
- i1_plus1 = min(i1 + 1, values.shape[1] - 1)
- i2_plus1 = min(i2 + 1, values.shape[2] - 1)
-
- if pos2 == i2: # Avoids multiplication by 0 (which yields to NaN with inf)
- c00 = <double> values[i0, i1, i2]
- c10 = <double> values[i0, i1_plus1, i2]
- c01 = <double> values[i0_plus1, i1, i2]
- c11 = <double> values[i0_plus1, i1_plus1, i2]
- else:
- delta = pos2 - i2
- c00 = (<double> values[i0, i1, i2]) * (1. - delta) + (<double> values[i0, i1, i2_plus1]) * delta
- c10 = (<double> values[i0, i1_plus1, i2]) * (1. - delta) + (<double> values[i0, i1_plus1, i2_plus1]) * delta
- c01 = (<double> values[i0_plus1, i1, i2]) * (1. - delta) + (<double> values[i0_plus1, i1, i2_plus1]) * delta
- c11 = (<double> values[i0_plus1, i1_plus1, i2]) * (1. - delta) + (<double> values[i0_plus1, i1_plus1, i2_plus1]) * delta
-
- if pos1 == i1: # Avoids multiplication by 0 (which yields to NaN with inf)
- c0 = c00
- c1 = c01
- else:
- delta = pos1 - i1
- c0 = c00 * (1. - delta) + c10 * delta
- c1 = c01 * (1. - delta) + c11 * delta
-
- if pos0 == i0: # Avoids multiplication by 0 (which yields to NaN with inf)
- c = c0
- else:
- delta = pos0 - i0
- c = c0 * (1 - delta) + c1 * delta
-
- return c
-
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def interp3d(_floating[:, :, :] values not None,
- _floating_pts[:, :] xi not None,
- str method='linear',
- double fill_value=numpy.nan):
- """Trilinear interpolation in a regular grid.
-
- Perform trilinear interpolation of the 3D dataset at given points
-
- :param numpy.ndarray values: 3D dataset of floating point values
- :param numpy.ndarray xi: (N, 3) sampling points
- :param str method: Interpolation method to use in:
- - 'linear': Trilinear interpolation
- - 'linear_omp': Trilinear interpolation with OpenMP parallelism
- :param float fill_value:
- Value to use for points outside the volume (default: nan)
- :return: Values evaluated at given input points.
- :rtype: numpy.ndarray
- """
- if _floating is cnumpy.float32_t:
- dtype = numpy.float32
- elif _floating is cnumpy.float64_t:
- dtype = numpy.float64
- else: # This should not happen
- raise ValueError("Unsupported input dtype")
-
- cdef:
- int npoints = xi.shape[0]
- _floating[:] result = numpy.empty((npoints,), dtype=dtype)
- int index
- double c_fill_value = fill_value
-
- if method == 'linear':
- with nogil:
- for index in range(npoints):
- result[index] = < _floating > trilinear_interpolation(
- values, xi[index, 0], xi[index, 1], xi[index, 2], c_fill_value)
-
- elif method == 'linear_omp':
- for index in prange(npoints, nogil=True):
- result[index] = < _floating > trilinear_interpolation(
- values, xi[index, 0], xi[index, 1], xi[index, 2], c_fill_value)
- else:
- raise ValueError("Unsupported method: %s" % method)
-
- return numpy.array(result, copy=False) \ No newline at end of file
diff --git a/silx/math/marchingcubes.pyx b/silx/math/marchingcubes.pyx
deleted file mode 100644
index 0409691..0000000
--- a/silx/math/marchingcubes.pyx
+++ /dev/null
@@ -1,246 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2015-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides marching cubes implementation.
-
-It provides a :class:`MarchingCubes` class allowing to build an isosurface
-from data provided as a 3D data set or slice by slice.
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "16/08/2017"
-
-
-import numpy
-cimport numpy as cnumpy
-cimport cython
-
-cimport silx.math.mc as mc
-
-
-# From numpy_common.pxi to avoid warnings while compiling C code
-# See this thread:
-# https://mail.python.org/pipermail//cython-devel/2012-March/002137.html
-cdef extern from *:
- bint FALSE "0"
- void import_array()
- void import_umath()
-
-if FALSE:
- import_array()
- import_umath()
-
-
-cdef class MarchingCubes:
- """Compute isosurface using marching cubes algorithm.
-
- It builds a surface from a 3D scalar dataset as a 3D contour at a
- given value.
- The resulting surface is not topologically correct.
-
- See: http://paulbourke.net/geometry/polygonise/
-
- Lorensen, W. E. and Cline, H. E. Marching cubes: A high resolution 3D
- surface construction algorithm. Computer Graphics, 21, 4 (July 1987).
- ACM, 163-169.
-
- Generated vertex and normal coordinates are in the same order
- as input array, i.e., (dim 0, dim 1, dim 2).
-
- Expected indices in memory of a (2, 2, 2) dataset:
-
- dim 0 (depth)
- |
- |
- 4 +------+ 5
- /| /|
- / | / |
- 6 +------+ 7|
- | | | |
- |0 +---|--+ 1 --- dim 2 (width)
- | / | /
- |/ |/
- 2 +------+ 3
- /
- /
- dim 1 (height)
-
- Example with a 3D data set:
-
- >>> vertices, normals, indices = MarchingCubes(data, isolevel=1.)
-
- Example of code for processing a list of images:
-
- >>> mc = MarchingCubes(isolevel=1.) # Create object with iso-level=1
- >>> previous_image = images[0]
- >>> for image in images[1:]:
- ... mc.process_image(previous_image, image) # Process one slice
- ... previous_image = image
-
- >>> vertices = mc.get_vertices() # Array of vertex positions
- >>> normals = mc.get_normals() # Array of normals
- >>> triangle_indices = mc.get_indices() # Array of indices of vertices
-
- :param data: 3D dataset of float32 or None
- :type data: numpy.ndarray of float32 of dimension 3
- :param float isolevel: The value for which to generate the isosurface
- :param bool invert_normals:
- True (default) for normals oriented in direction of gradient descent
- :param sampling: Sampling along each dimension (depth, height, width)
- """
- cdef mc.MarchingCubes[float, float] * c_mc # Pointer to the C++ instance
-
- def __cinit__(self, data=None, isolevel=None,
- invert_normals=True, sampling=(1, 1, 1)):
- self.c_mc = new mc.MarchingCubes[float, float](isolevel)
- self.c_mc.invert_normals = bool(invert_normals)
- self.c_mc.sampling[0] = sampling[0]
- self.c_mc.sampling[1] = sampling[1]
- self.c_mc.sampling[2] = sampling[2]
-
- if data is not None:
- self.process(data)
-
- def __dealloc__(self):
- del self.c_mc
-
- def __getitem__(self, key):
- """Allows one to unpack object as a single liner:
-
- vertices, normals, indices = MarchingCubes(...)
- """
- if key == 0:
- return self.get_vertices()
- elif key == 1:
- return self.get_normals()
- elif key == 2:
- return self.get_indices()
- else:
- raise IndexError("Index out of range")
-
- def process(self, data):
- """Compute an isosurface from a 3D scalar field.
-
- This builds vertices, normals and indices arrays.
- Vertices and normals coordinates are in the same order as input array,
- i.e., (dim 0, dim 1, dim 2).
-
- :param numpy.ndarray data: 3D scalar field
- """
- # Make sure data is a 3D contiguous array of native endian float32
- data = numpy.ascontiguousarray(data, dtype='=f4')
- assert data.ndim == 3
- cdef float[:] c_data = numpy.ravel(data)
- cdef unsigned int depth, height, width
-
- depth = data.shape[0]
- height = data.shape[1]
- width = data.shape[2]
-
- self.c_mc.process(&c_data[0], depth, height, width)
-
- def process_slice(self, slice0, slice1):
- """Process a new slice to build the isosurface.
-
- :param numpy.ndarray slice0: Slice previously provided as slice1.
- :param numpy.ndarray slice1: Slice to process.
- """
- # Make sure slices are 2D contiguous arrays of native endian float32
- slice0 = numpy.ascontiguousarray(slice0, dtype='=f4')
- assert slice0.ndim == 2
- slice1 = numpy.ascontiguousarray(slice1, dtype='=f4')
- assert slice1.ndim == 2
-
- assert slice0.shape[0] == slice1.shape[0]
- assert slice0.shape[1] == slice1.shape[1]
-
- cdef float[:] c_slice0 = numpy.ravel(slice0)
- cdef float[:] c_slice1 = numpy.ravel(slice1)
-
- if self.c_mc.depth == 0:
- # Starts a new isosurface, bootstrap with slice size
- self.c_mc.set_slice_size(slice1.shape[0], slice1.shape[1])
-
- assert slice1.shape[0] == self.c_mc.height
- assert slice1.shape[1] == self.c_mc.width
-
- self.c_mc.process_slice(&c_slice0[0], &c_slice1[0])
-
- def finish_process(self):
- """Clear internal cache after processing slice by slice."""
- self.c_mc.finish_process()
-
- def reset(self):
- """Reset internal resources including computed isosurface info."""
- self.c_mc.reset()
-
- @cython.embedsignature(False)
- @property
- def shape(self):
- """The shape of the processed scalar field (depth, height, width)."""
- return self.c_mc.depth, self.c_mc.height, self.c_mc.width
-
- @cython.embedsignature(False)
- @property
- def sampling(self):
- """The sampling over each dimension (depth, height, width).
-
- Default: 1, 1, 1
- """
- return (self.c_mc.sampling[0],
- self.c_mc.sampling[1],
- self.c_mc.sampling[2])
-
- @cython.embedsignature(False)
- @property
- def isolevel(self):
- """The iso-level at which to generate the isosurface"""
- return self.c_mc.isolevel
-
- @cython.embedsignature(False)
- @property
- def invert_normals(self):
- """True to use gradient descent as normals."""
- return self.c_mc.invert_normals
-
- def get_vertices(self):
- """Vertices currently computed (ndarray of dim NbVertices x 3)
-
- Order is dim0, dim1, dim2 (i.e., z, y, x if dim0 is depth).
- """
- return numpy.array(self.c_mc.vertices).reshape(-1, 3)
-
- def get_normals(self):
- """Normals currently computed (ndarray of dim NbVertices x 3)
-
- Order is dim0, dim1, dim2 (i.e., z, y, x if dim0 is depth).
- """
- return numpy.array(self.c_mc.normals).reshape(-1, 3)
-
- def get_indices(self):
- """Triangle indices currently computed (ndarray of dim NbTriangles x 3)
- """
- return numpy.array(self.c_mc.indices,
- dtype=numpy.uint32).reshape(-1, 3)
diff --git a/silx/math/marchingcubes/mc.hpp b/silx/math/marchingcubes/mc.hpp
deleted file mode 100644
index 82eced9..0000000
--- a/silx/math/marchingcubes/mc.hpp
+++ /dev/null
@@ -1,724 +0,0 @@
-/*##########################################################################
-#
-# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-#ifndef __mc_HPP__
-#define __mc_HPP__
-
-#include <iostream>
-#include <cmath>
-#include <map>
-#include <stdexcept>
-#include <vector>
-#include <assert.h>
-
-
-extern const int MCTriangleTable[256][16];
-extern const unsigned int MCEdgeIndexToCoordOffsets[12][4];
-
-#define DEPTH_IDX 0
-#define HEIGHT_IDX 1
-#define WIDTH_IDX 2
-
-/** Class Marching cubes
- *
- * Implements the marching cube algorithm and provides an API to process
- * data image by image.
- *
- * Dimension convention used is (dim0, dim1, dim2) denoted as
- * (depth, height, width) with dim2 (= width) being contiguous in memory.
- *
- * If data is provided as (depth, height, width), resulting vertices
- * and normals will be stored as (z, y, x)
- *
- * Indices in memory for a single cube:
- *
- * dim 0 (depth)
- * |
- * |
- * 4 +------+ 5
- * /| /|
- * / | / |
- * 6 +------+ 7|
- * | | | |
- * |0 +---|--+ 1 --- dim 2 (width)
- * | / | /
- * |/ |/
- * 2 +------+ 3
- * /
- * /
- * dim 1 (height)
- */
-template <typename FloatIn, typename FloatOut>
-class MarchingCubes {
-public:
- /** Create a marching cube object.
- *
- * @param level Level at which to build the isosurface
- */
- MarchingCubes(const FloatIn level);
-
- ~MarchingCubes();
-
- /** Process a 3D scalar field
- *
- * @param data Pointer to the data set
- * @param depth The 1st dimension of the data set
- * @param height The 2nd dimension of the data set
- * @param width The 3rd dimension of the data set
- * (tightly packed in memory)
- */
- void process(const FloatIn * data,
- const unsigned int depth,
- const unsigned int height,
- const unsigned int width);
-
- /** Init dimension of slices
- *
- * @param height Height in pixels of the slices
- * @param width Width in pixels of the slices
- */
- void set_slice_size(const unsigned int height,
- const unsigned int width);
-
- /** Process a slice (i.e., an image)
- *
- * The size of the images MUST match height and width provided to
- * set_slice_size.
- *
- * The marching cube process 2 consecutive images at a time.
- * A slice provided as next parameter MUST be provided as current
- * parameter for the next call.
- * Example with 3 images:
- *
- * float * img1;
- * float * img2;
- * float * img3;
- * ...
- * mc = MarchingCubes<float>(100.);
- * mc.set_slice_size(10, 10);
- * mc.process_slice(img1, img2);
- * mc.process_slice(img2, img3);
- * mc.finish_process();
- *
- * @param slice0 Pointer to the nth slice data
- * @param slice1 Pointer to the (n+1)th slice of data
- */
- void process_slice(const FloatIn * slice0,
- const FloatIn * slice1);
-
- /** Clear marching cube processing internal cache. */
- void finish_process();
-
- /** Reset all internal data and counters. */
- void reset();
-
- /** Vertices of the isosurface (x, y, z) */
- std::vector<FloatOut> vertices;
-
- /** Approximation of normals at the vertices (nx, ny, nz)
- *
- * Current implementation provides coarse (but fast) normals computation
- */
- std::vector<FloatOut> normals;
-
- /** Triangle indices */
- std::vector<unsigned int> indices;
-
- unsigned int depth; /**< Number of images currently processed */
- unsigned int height; /**< Images height in pixels */
- unsigned int width; /**< Images width in pixels */
-
- /** Sampling of the data (depth, height, width)
- *
- * Default: 1, 1, 1
- */
- unsigned int sampling[3];
-
- FloatIn isolevel; /**< Iso level to use */
- bool invert_normals; /**< True to inverse gradient as normals */
-
-private:
-
- /** Start to build isosurface starting with first slice
- *
- * Bootstrap cache edge_indices
- *
- * @param slice The first slice of the data
- * @param next The second slice
- */
- void first_slice(const FloatIn * slice,
- const FloatIn * next);
-
- /** Process an edge
- *
- * @param value0 Data at 'begining' of edge
- * @param value Data at 'end' of edge
- * @param depth Depth coordinate of the edge position
- * @param row Row coordinate of the edge
- * @param col Column coordinate of the edge
- * @param direction Direction of the edge: 0 for x, 1 for y and 2 for z
- * @param previous
- * @param current
- * @param next
- */
- void process_edge(const FloatIn value0,
- const FloatIn value,
- const unsigned int depth,
- const unsigned int row,
- const unsigned int col,
- const unsigned int direction,
- const FloatIn * previous,
- const FloatIn * current,
- const FloatIn * next);
-
- /** Return the bit mask of cube corners <= the iso-value.
- *
- * @param slice1 1st slice of the cube to consider
- * @param slice2 2nd slice of the cube to consider
- * @param row Row of the cube to consider
- * @param col Column of the cube to consider
- * @return The bit mask of cube corners <= the iso-value
- */
- unsigned char get_cell_code(const FloatIn * slice1,
- const FloatIn * slice2,
- const unsigned int row,
- const unsigned int col);
-
- /** Compute an edge index from position and edge direction.
- *
- * @param depth Depth of the origin of the edge
- * @param row Row of the origin of the edge
- * @param col Column of the origin of the edge
- * @param direction 0 for x, 1 for y, 2 for z
- * @return The (4D) index of the edge
- */
- unsigned int edge_index(const unsigned int depth,
- const unsigned int row,
- const unsigned int col,
- const unsigned int direction);
-
- /** For each dimension, a map from edge index to vertex index
- *
- * This caches indices for previously processed slice.
- *
- * Edge index is the linearized position of the edge using size + 1
- * in all dimensions as coordinates plus the direction as 4th coord.
- * WARNING: direction 0 for x, 1 for y and 2 for z
- */
- std::map<unsigned int, unsigned int> * edge_indices;
-};
-
-
-/* Implementation */
-
-template <typename FloatIn, typename FloatOut>
-MarchingCubes<FloatIn, FloatOut>::MarchingCubes(const FloatIn level)
-{
- this->edge_indices = 0;
- this->reset();
- this->height = 0;
- this->width = 0;
- this->isolevel = level;
- this->invert_normals = true;
- this->sampling[0] = 1;
- this->sampling[1] = 1;
- this->sampling[2] = 1;
-}
-
-template <typename FloatIn, typename FloatOut>
-MarchingCubes<FloatIn, FloatOut>::~MarchingCubes()
-{
-}
-
-template <typename FloatIn, typename FloatOut>
-void
-MarchingCubes<FloatIn, FloatOut>::reset()
-{
- this->depth = 0;
- this->vertices.clear();
- this->normals.clear();
- this->indices.clear();
- if (this->edge_indices != 0) {
- delete this->edge_indices;
- this->edge_indices = 0;
- }
-}
-
-template <typename FloatIn, typename FloatOut>
-void
-MarchingCubes<FloatIn, FloatOut>::finish_process()
-{
- if (this->edge_indices != 0) {
- delete this->edge_indices;
- this->edge_indices = 0;
- }
-}
-
-
-template <typename FloatIn, typename FloatOut>
-void
-MarchingCubes<FloatIn, FloatOut>::process(const FloatIn * data,
- const unsigned int depth,
- const unsigned int height,
- const unsigned int width)
-{
- assert(data != NULL);
- unsigned int size = height * width * this->sampling[DEPTH_IDX];
-
- /* number of slices minus - 1 to process */
- const unsigned int nb_slices = (depth - 1) / this->sampling[DEPTH_IDX];
-
- this->reset();
- this->set_slice_size(height, width);
-
- for (unsigned int index=0; index < nb_slices; index++) {
- const FloatIn * slice0 = data + (index * size);
- const FloatIn * slice1 = slice0 + size;
-
- this->process_slice(slice0, slice1);
- }
- this->finish_process();
-
- this->depth = depth; /* Forced as it might be < depth otherwise */
-}
-
-
-template <typename FloatIn, typename FloatOut>
-void
-MarchingCubes<FloatIn, FloatOut>::set_slice_size(const unsigned int height,
- const unsigned int width)
-{
- this->reset();
- this->height = height;
- this->width = width;
-}
-
-
-template <typename FloatIn, typename FloatOut>
-void
-MarchingCubes<FloatIn, FloatOut>::process_slice(const FloatIn * slice0,
- const FloatIn * slice1)
-{
- assert(slice0 != NULL);
- assert(slice1 != NULL);
- unsigned int row, col;
-
- if (this->edge_indices == 0) {
- /* No previously processed slice, bootstrap */
- this->first_slice(slice0, slice1);
- }
-
- /* Keep reference to cache from previous slice */
- std::map<unsigned int, unsigned int> * previous_edge_indices =
- this->edge_indices;
-
- /* Init cache for this slice */
- this->edge_indices = new std::map<unsigned int, unsigned int>();
-
- /* Loop over slice to add vertices */
- for (row=0; row < this->height; row += this->sampling[HEIGHT_IDX]) {
- unsigned int line_index = row * this->width;
-
- for (col=0; col < this->width; col += this->sampling[WIDTH_IDX]) {
- unsigned int item_index = line_index + col;
-
- FloatIn value0 = slice1[item_index];
-
- /* Test forward edges and add vertices in the current slice plane */
- if (col < (width - this->sampling[WIDTH_IDX])) {
- FloatIn value = slice1[item_index + this->sampling[WIDTH_IDX]];
-
- this->process_edge(value0, value, this->depth, row, col, 0,
- slice0, slice1, 0);
- }
-
- if (row < (height - this->sampling[HEIGHT_IDX])) {
- /* Value from next line*/
- FloatIn value = slice1[item_index + this->width * this->sampling[HEIGHT_IDX]];
-
- this->process_edge(value0, value, this->depth, row, col, 1,
- slice0, slice1, 0);
- }
-
- /* Test backward edges and add vertices in z direction */
- {
- FloatIn value = slice0[item_index];
-
- /* Expect forward edge, so pass: previous, current */
- this->process_edge(value, value0,
- this->depth - this->sampling[DEPTH_IDX],
- row, col, 2,
- 0, slice0, slice1);
- }
-
- }
- }
-
- /* Loop over cubes to add triangle indices */
- for (row=0; row < this->height - this->sampling[HEIGHT_IDX]; row += this->sampling[HEIGHT_IDX]) {
- for (col=0; col < this->width - this->sampling[WIDTH_IDX]; col += this->sampling[WIDTH_IDX]) {
- unsigned char code = this->get_cell_code(slice0, slice1,
- row, col);
-
- if (code == 0) {
- continue;
- }
-
- const int * edgeIndexPtr = &MCTriangleTable[code][0];
- for (; *edgeIndexPtr >= 0; edgeIndexPtr++) {
- const unsigned int * offsets = \
- MCEdgeIndexToCoordOffsets[*edgeIndexPtr];
-
- unsigned int edge_index = this->edge_index(
- this->depth - this->sampling[DEPTH_IDX] + offsets[DEPTH_IDX] * this->sampling[DEPTH_IDX],
- row + offsets[HEIGHT_IDX] * this->sampling[HEIGHT_IDX],
- col + offsets[WIDTH_IDX] * this->sampling[WIDTH_IDX],
- offsets[3]);
-
- /* Add vertex index to the list of indices */
- std::map<unsigned int, unsigned int>::iterator it, end;
- if (offsets[DEPTH_IDX] == 0 && offsets[3] != 2) {
- it = previous_edge_indices->find(edge_index);
- end = previous_edge_indices->end();
- } else {
- it = this->edge_indices->find(edge_index);
- end = this->edge_indices->end();
- }
- if (it == end) {
- throw std::runtime_error(
- "Internal error: cannot build triangle indices.");
- }
- else {
- this->indices.push_back(it->second);
- }
- }
-
- }
- }
-
- /* Clean-up previous slice cache */
- delete previous_edge_indices;
-
- this->depth += this->sampling[DEPTH_IDX];
-}
-
-
-template <typename FloatIn, typename FloatOut>
-void
-MarchingCubes<FloatIn, FloatOut>::first_slice(const FloatIn * slice,
- const FloatIn * next)
-{
- assert(slice != NULL);
- assert(next != NULL);
- /* Init cache for this slice */
- this->edge_indices = new std::map<unsigned int, unsigned int>();
-
- unsigned int row, col;
-
- /* Loop over slice, and add isosurface vertices in the slice plane */
- for (row=0; row < this->height; row += this->sampling[HEIGHT_IDX]) {
- unsigned int line_index = row * this->width;
-
- for (col=0; col < this->width; col += this->sampling[WIDTH_IDX]) {
- unsigned int item_index = line_index + col;
-
- /* For each point test forward edges */
- FloatIn value0 = slice[item_index];
-
- if (col < (width - this->sampling[WIDTH_IDX])) {
- FloatIn value = slice[item_index + this->sampling[WIDTH_IDX]];
-
- this->process_edge(value0, value, this->depth, row, col, 0,
- 0, slice, next);
- }
-
- if (row < (height - this->sampling[HEIGHT_IDX])) {
- /* Value from next line */
- FloatIn value = slice[item_index + this->width * this->sampling[HEIGHT_IDX]];
-
- this->process_edge(value0, value, this->depth, row, col, 1,
- 0, slice, next);
- }
- }
- }
-
- this->depth += this->sampling[DEPTH_IDX];
-}
-
-
-template <typename FloatIn, typename FloatOut>
-inline unsigned int
-MarchingCubes<FloatIn, FloatOut>::edge_index(const unsigned int depth,
- const unsigned int row,
- const unsigned int col,
- const unsigned int direction)
-{
- return ((depth * (this->height + 1) + row) *
- (this->width + 1) + col) * 3 + direction;
-}
-
-
-template <typename FloatIn, typename FloatOut>
-inline void
-MarchingCubes<FloatIn, FloatOut>::process_edge(const FloatIn value0,
- const FloatIn value,
- const unsigned int depth,
- const unsigned int row,
- const unsigned int col,
- const unsigned int direction,
- const FloatIn * previous,
- const FloatIn * current,
- const FloatIn * next)
-{
- assert(current != NULL);
-
- if ((value0 <= this->isolevel) ^ (value <= this->isolevel)) {
-
- /* Crossing iso-surface, store it */
- FloatIn offset = (this->isolevel - value0) / (value - value0);
-
- /* Store edge to vertex index correspondance */
- unsigned int edge_index = this->edge_index(depth, row, col, direction);
- (*this->edge_indices)[edge_index] = this->vertices.size() / 3;
-
- /* Store vertex as (z, y, x) */
- if (direction == 0) {
- this->vertices.push_back((FloatOut) depth);
- this->vertices.push_back((FloatOut) row);
- this->vertices.push_back(
- (FloatOut) col + offset * this->sampling[WIDTH_IDX]);
- }
- else if (direction == 1) {
- this->vertices.push_back((FloatOut) depth);
- this->vertices.push_back(
- (FloatOut) row + offset * this->sampling[HEIGHT_IDX]);
- this->vertices.push_back((FloatOut) col);
- }
- else if (direction == 2) {
- this->vertices.push_back(
- (FloatOut) depth + offset * this->sampling[DEPTH_IDX]);
- this->vertices.push_back((FloatOut) row);
- this->vertices.push_back((FloatOut) col);
- } else {
- throw std::runtime_error(
- "Internal error: dimension > 3, never event.");
- }
-
- /* Store normal as (nz, ny, nx) */
- FloatOut nz, ny, nx;
- const FloatIn * slice0 = (previous != 0) ? previous : current;
- const FloatIn * slice1 = (previous != 0) ? current : next;
-
- unsigned int row_offset = this->width * this->sampling[HEIGHT_IDX];
-
- if (direction == 0) {
- { /* nz */
- unsigned int item, item_next_col;
-
- item = row * this->width + col;
- if (col >= this->width - this->sampling[WIDTH_IDX]) {
- /* For last column, use previous column */
- item -= this->sampling[WIDTH_IDX];
- }
- item_next_col = item + this->sampling[WIDTH_IDX];
-
- nz = ((1. - offset) * (slice1[item] - slice0[item]) +
- offset * (slice1[item_next_col] - slice0[item_next_col]));
- }
-
- { /* ny */
- unsigned int item, item_next_col;
-
- item = row * this->width + col;
- if (row >= this->height - this->sampling[HEIGHT_IDX]) {
- /* For last row, use previous row */
- item -= row_offset;
- }
- if (col >= this->width - this->sampling[WIDTH_IDX]) {
- /* For last column, use previous column */
- item -= this->sampling[WIDTH_IDX];
- }
- item_next_col = item + this->sampling[WIDTH_IDX];
-
- ny = ((1. - offset) * (current[item + row_offset] -
- current[item]) +
- offset * (current[item_next_col + row_offset] -
- current[item_next_col]));
- }
-
- nx = value - value0;
-
- } else if (direction == 1) {
- { /* nz */
- unsigned int item, item_next_row;
-
- item = row * this->width + col;
- if (row >= this->height - this->sampling[HEIGHT_IDX]) {
- /* For last row, use previous row */
- item -= row_offset;
- }
- item_next_row = item + row_offset;
-
- nz = ((1. - offset) * (slice1[item] - slice0[item]) +
- offset * (slice1[item_next_row] - slice0[item_next_row]));
- }
-
- ny = value - value0;
-
- { /* nx */
- unsigned int item, item_next_row;
-
- item = row * this->width + col;
- if (row >= this->height - this->sampling[HEIGHT_IDX]) {
- /* For last row, use previous row */
- item -= row_offset;
- }
- if (col >= this->width - this->sampling[WIDTH_IDX]) {
- /* For last column, use previous column */
- item -= this->sampling[WIDTH_IDX];
- }
-
- item_next_row = item + row_offset;
-
- nx = ((1. - offset) * (current[item + this->sampling[WIDTH_IDX]] - current[item]) +
- offset * (current[item_next_row + this->sampling[WIDTH_IDX]] - current[item_next_row]));
- }
-
- } else { /* direction == 2 */
- assert(direction == 2);
- /* Previous should always be 0, only here in case this changes */
- const FloatIn * other_slice = (previous != 0) ? previous : next;
-
- nz = value - value0;
-
- { /* ny */
- unsigned int item, item_next_row;
-
- item = row * this->width + col;
- if (row >= this->height - this->sampling[HEIGHT_IDX]) {
- /* For last row, use previous row */
- item -= row_offset;
- }
- item_next_row = item + row_offset;
-
- ny = ((1. - offset) * (current[item_next_row] - current[item]) +
- offset * (other_slice[item_next_row] - other_slice[item]));
- }
-
- { /* nx */
- unsigned int item;
-
- item = row * this->width + col;
- if (col >= this->width - this->sampling[WIDTH_IDX]) {
- /* For last column, use previous column */
- item -= this->sampling[WIDTH_IDX];
- }
- const unsigned int item_next_col = item + this->sampling[WIDTH_IDX];
-
- nx = ((1. - offset) * (current[item_next_col] - current[item]) +
- offset * (other_slice[item_next_col] - other_slice[item]));
- }
- }
-
- /* apply sampling scaling */
- nz /= (FloatOut) this->sampling[0];
- ny /= (FloatOut) this->sampling[1];
- nx /= (FloatOut) this->sampling[2];
-
- /* normalisation */
- FloatOut norm = sqrt(nz * nz + ny * ny + nx * nx);
- if (this->invert_normals) { /* Normal inversion */
- norm *= -1.;
- }
-
- if (norm != 0) {
- nz /= norm;
- ny /= norm;
- nx /= norm;
- }
- this->normals.push_back(nz);
- this->normals.push_back(ny);
- this->normals.push_back(nx);
- }
-}
-
-
-template <typename FloatIn, typename FloatOut>
-inline unsigned char
-MarchingCubes<FloatIn, FloatOut>::get_cell_code(const FloatIn * slice1,
- const FloatIn * slice2,
- const unsigned int row,
- const unsigned int col)
-{
- assert(slice1 != NULL);
- assert(slice2 != NULL);
- unsigned int item = row * this->width + col;
- unsigned int item_next_row = item + this->width * this->sampling[HEIGHT_IDX];
- unsigned char code = 0;
-
- /* Cube convention for cell code:
- * WARNING: This differ from layout in memory
- *
- * 4 +------+ 5
- * /| /|
- * / | / |
- * 7 +------+ 6|
- * | | | |
- * |0 +---|--+ 1
- * | / | /
- * |/ |/
- * 3 +------+ 2
- *
- */
- /* First slice */
- if (slice1[item] <= this->isolevel) {
- code |= 1 << 0;
- }
- if (slice1[item + this->sampling[WIDTH_IDX]] <= this->isolevel) {
- code |= 1 << 1;
- }
- if (slice1[item_next_row + this->sampling[WIDTH_IDX]] <= this->isolevel) {
- code |= 1 << 2;
- }
- if (slice1[item_next_row] <= this->isolevel) {
- code |= 1 << 3;
- }
-
- /* Second slice */
- if (slice2[item] <= this->isolevel) {
- code |= 1 << 4;
- }
- if (slice2[item + this->sampling[WIDTH_IDX]] <= this->isolevel) {
- code |= 1 << 5;
- }
- if (slice2[item_next_row + this->sampling[WIDTH_IDX]] <= this->isolevel) {
- code |= 1 << 6;
- }
- if (slice2[item_next_row] <= this->isolevel) {
- code |= 1 << 7;
- }
-
- return code;
-}
-
-#endif /*__mc_HPP__*/
diff --git a/silx/math/marchingcubes/mc_lut.cpp b/silx/math/marchingcubes/mc_lut.cpp
deleted file mode 100644
index 7998f1b..0000000
--- a/silx/math/marchingcubes/mc_lut.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-# /*##########################################################################
-#
-# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-
-#include "mc.hpp"
-
-
-/** Gives edge index of triangles vertices for each of the 256 possible cubes.
- *
- * Table taken from http://paulbourke.net/geometry/polygonise/
- * Author: Cory Bloyd
- * Originially this code is public domain,
- * relicensed here as MIT to provide a license.
- *
- * The cube index is a bit mask of cube corners <= isoValue.
- * See vertexOffset for the place of each corner in the bit mask.
- */
-const int MCTriangleTable[256][16] = {
- {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
- {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
- {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
- {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
- {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
- {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
- {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
- {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
- {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
- {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
- {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
- {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
- {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
- {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
- {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
- {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
- {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
- {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
- {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
- {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
- {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
- {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
- {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
- {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
- {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
- {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
- {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
- {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
- {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
- {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
- {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
- {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
- {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
- {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
- {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
- {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
- {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
- {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
- {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
- {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
- {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
- {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
- {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
- {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
- {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
- {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
- {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
- {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
- {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
- {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
- {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
- {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
- {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
- {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
- {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
- {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
- {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
- {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
- {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
- {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
- {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
- {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
- {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
- {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
- {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
- {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
- {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
- {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
- {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
- {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
- {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
- {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
- {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
- {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
- {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
- {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
- {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
- {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
- {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
- {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
- {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
- {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
- {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
- {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
- {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
- {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
- {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
- {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
- {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
- {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
- {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
- {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
- {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
- {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
- {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
- {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
- {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
- {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
- {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
- {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
- {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
- {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
- {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
- {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
- {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
- {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
- {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
- {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
- {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
- {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
- {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
- {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
- {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
- {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
- {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
- {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
- {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
- {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
- {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
- {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
- {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
- {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
- {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
- {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
- {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
- {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
- {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
- {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
- {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
- {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
- {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
- {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
- {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
- {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
- {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
- {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
- {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
- {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
- {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
- {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
- {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
- {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
- {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
- {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
- {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
- {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
- {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
- {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
- {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
- {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
- {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
- {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
- {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
- {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
- {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
- {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
- {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
- {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
- {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
- {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
- {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
- {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
- {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
- {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
- {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
- {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
- {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
- {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
- {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
- {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
- {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
- {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
- {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
- {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
- {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
- {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
- {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
- {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
- {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
- {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
-};
-
-
-/** List edge origin and direction for each edge index in [0-12).
- *
- * For each edge, gives the first vertices as 3 coordinates from the origin
- * of the cube and the direction of the edge as the 4th value.
- */
-const unsigned int MCEdgeIndexToCoordOffsets[12][4] = {
- {0, 0, 0, 0},
- {0, 0, 1, 1},
- {0, 1, 0, 0},
- {0, 0, 0, 1},
- {1, 0, 0, 0},
- {1, 0, 1, 1},
- {1, 1, 0, 0},
- {1, 0, 0, 1},
- {0, 0, 0, 2},
- {0, 0, 1, 2},
- {0, 1, 1, 2},
- {0, 1, 0, 2}
-};
diff --git a/silx/math/math_compatibility.pxd b/silx/math/math_compatibility.pxd
deleted file mode 100644
index ddaa550..0000000
--- a/silx/math/math_compatibility.pxd
+++ /dev/null
@@ -1,35 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-# Provides Visual Studio 2008 missing math functions/macros
-
-cdef extern from "math_compatibility.h":
- double asinh(double x) nogil
- bint isnan(double x) nogil
- bint isfinite(double x) nogil
- long int lrint(double x) nogil
-
- double INFINITY
- double NAN
diff --git a/silx/math/mc.pxd b/silx/math/mc.pxd
deleted file mode 100644
index b1c81e7..0000000
--- a/silx/math/mc.pxd
+++ /dev/null
@@ -1,51 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-
-from libcpp.vector cimport vector as std_vector
-from libcpp cimport bool
-
-cdef extern from "mc.hpp":
- cdef cppclass MarchingCubes[FloatIn, FloatOut]:
- MarchingCubes(FloatIn level) except +
- void process(FloatIn * data,
- unsigned int depth,
- unsigned int height,
- unsigned int width) except +
- void set_slice_size(unsigned int height,
- unsigned int width)
- void process_slice(FloatIn * slice0,
- FloatIn * slice1) except +
- void finish_process()
- void reset()
-
- unsigned int depth
- unsigned int height
- unsigned int width
- unsigned int sampling[3]
- FloatIn isolevel
- bool invert_normals
- std_vector[FloatOut] vertices
- std_vector[FloatOut] normals
- std_vector[unsigned int] indices
diff --git a/silx/math/medianfilter/__init__.py b/silx/math/medianfilter/__init__.py
deleted file mode 100644
index 2b05f06..0000000
--- a/silx/math/medianfilter/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["H. Payno"]
-__license__ = "MIT"
-__date__ = "02/05/2017"
-
-
-from .medianfilter import (medfilt, medfilt1d, medfilt2d)
diff --git a/silx/math/medianfilter/include/median_filter.hpp b/silx/math/medianfilter/include/median_filter.hpp
deleted file mode 100644
index 7e42980..0000000
--- a/silx/math/medianfilter/include/median_filter.hpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*##########################################################################
-#
-# Copyright (c) 2017-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-// __authors__ = ["H. Payno"]
-// __license__ = "MIT"
-// __date__ = "10/02/2017"
-
-#ifndef MEDIAN_FILTER
-#define MEDIAN_FILTER
-
-#include <vector>
-#include <assert.h>
-#include <algorithm>
-#include <signal.h>
-#include <iostream>
-#include <cmath>
-#include <cfloat>
-
-/* Needed for pytohn2.7 on Windows... */
-#ifndef INFINITY
-#define INFINITY (DBL_MAX+DBL_MAX)
-#endif
-
-#ifndef NAN
-#define NAN (INFINITY-INFINITY)
-#endif
-
-// Modes for the median filter
-enum MODE{
- NEAREST=0,
- REFLECT=1,
- MIRROR=2,
- SHRINK=3,
- CONSTANT=4,
-};
-
-// Simple function browsing a deque and registering the min and max values
-// and if those values are unique or not
-template<typename T>
-void getMinMax(std::vector<T>& v, T& min, T&max,
- typename std::vector<T>::const_iterator end){
- // init min and max values
- typename std::vector<T>::const_iterator it = v.begin();
- if (v.size() == 0){
- raise(SIGINT);
- }else{
- min = max = *it;
- }
- it++;
-
- // Browse all the deque
- while(it!=end){
- // check if repeated (should always be before min/max setting)
- T value = *it;
- if(value > max) max = value;
- if(value < min) min = value;
-
- it++;
- }
-}
-
-
-// apply the median filter only on limited part of the vector
-// In case of even number of elements (either due to NaNs in the window
-// or for image borders in shrink mode):
-// the highest of the 2 central values is returned
-template<typename T>
-inline T median(std::vector<T>& v, int window_size) {
- int pivot = window_size / 2;
- std::nth_element(v.begin(), v.begin() + pivot, v.begin()+window_size);
- return v[pivot];
-}
-
-
-// return the index into 0, (length_max - 1) in reflect mode
-inline int reflect(int index, int length_max){
- int res = index;
- // if the index is negative get the positive symmetrical value
- if(res < 0){
- res += 1;
- res = -res;
- }
- // then apply the reflect algorithm. Frequency is 2 max length
- res = res % (2*length_max);
- if(res >= length_max){
- res = 2*length_max - res -1;
- res = res % length_max;
- }
- return res;
-}
-
-// return the index into 0, (length_max - 1) in mirror mode
-inline int mirror(int index, int length_max){
- int res = index;
- // if the index is negative get the positive symmetrical value
- if(res < 0){
- res = -res;
- }
- int rightLimit = length_max -1;
- // apply the redundancy each two right limit
- res = res % (2*rightLimit);
- if(res >= length_max){
- int distToRedundancy = (2*rightLimit) - res;
- res = distToRedundancy;
- }
- return res;
-}
-
-/* Provide a way to access NaN that also works for integers*/
-
-template<typename T>
-inline T NotANumber(void) {
- assert(false); //This should never be called
- return 0;
-}
-
-template<>
-inline float NotANumber<float>(void) { return NAN; }
-
-template<>
-inline double NotANumber<double>(void) { return NAN; }
-
-
-// Browse the column of pixel_x
-template<typename T>
-void median_filter(
- const T* input,
- T* output,
- int* kernel_dim, // two values : 0:width, 1:height
- int* image_dim, // two values : 0:width, 1:height
- int y_pixel, // the x pixel to process
- int x_pixel_range_min,
- int x_pixel_range_max,
- bool conditional,
- int pMode,
- T cval) {
-
- assert(kernel_dim[0] > 0);
- assert(kernel_dim[1] > 0);
- assert(y_pixel >= 0);
- assert(image_dim[0] > 0);
- assert(image_dim[1] > 0);
- assert(y_pixel >= 0);
- assert(y_pixel < image_dim[0]);
- assert(x_pixel_range_max < image_dim[1]);
- assert(x_pixel_range_min <= x_pixel_range_max);
- // kernel odd assertion
- assert((kernel_dim[0] - 1)%2 == 0);
- assert((kernel_dim[1] - 1)%2 == 0);
-
- // # this should be move up to avoid calculation each time
- int halfKernel_x = (kernel_dim[1] - 1) / 2;
- int halfKernel_y = (kernel_dim[0] - 1) / 2;
-
- MODE mode = static_cast<MODE>(pMode);
-
- // init buffer
- std::vector<T> window_values(kernel_dim[0]*kernel_dim[1]);
-
- bool not_horizontal_border = (y_pixel >= halfKernel_y && y_pixel < image_dim[0] - halfKernel_y);
-
- for(int x_pixel=x_pixel_range_min; x_pixel <= x_pixel_range_max; x_pixel ++ ){
- typename std::vector<T>::iterator it = window_values.begin();
- // fill the vector
-
- if (not_horizontal_border &&
- x_pixel >= halfKernel_x && x_pixel < image_dim[1] - halfKernel_x) {
- //This is not a border, just fill it
- for(int win_y=y_pixel-halfKernel_y; win_y<= y_pixel+halfKernel_y; win_y++) {
- for(int win_x = x_pixel-halfKernel_x; win_x <= x_pixel+halfKernel_x; win_x++){
- T value = input[win_y*image_dim[1] + win_x];
- if (value == value) { // Ignore NaNs
- *it = value;
- ++it;
- }
- }
- }
-
- } else { // This is a border, handle the special case
- for(int win_y=y_pixel-halfKernel_y; win_y<= y_pixel+halfKernel_y; win_y++)
- {
- for(int win_x = x_pixel-halfKernel_x; win_x <= x_pixel+halfKernel_x; win_x++)
- {
- T value = 0;
- int index_x = win_x;
- int index_y = win_y;
-
- switch(mode){
- case NEAREST:
- index_x = std::min(std::max(win_x, 0), image_dim[1] - 1);
- index_y = std::min(std::max(win_y, 0), image_dim[0] - 1);
- value = input[index_y*image_dim[1] + index_x];
- break;
-
- case REFLECT:
- index_x = reflect(win_x, image_dim[1]);
- index_y = reflect(win_y, image_dim[0]);
- value = input[index_y*image_dim[1] + index_x];
- break;
-
- case MIRROR:
- index_x = mirror(win_x, image_dim[1]);
- // deal with 1d case
- if(win_y == 0 && image_dim[0] == 1){
- index_y = 0;
- }else{
- index_y = mirror(win_y, image_dim[0]);
- }
- value = input[index_y*image_dim[1] + index_x];
- break;
-
- case SHRINK:
- if ((index_x < 0) || (index_x > image_dim[1] -1) ||
- (index_y < 0) || (index_y > image_dim[0] -1)) {
- continue;
- }
- value = input[index_y*image_dim[1] + index_x];
- break;
- case CONSTANT:
- if ((index_x < 0) || (index_x > image_dim[1] -1) ||
- (index_y < 0) || (index_y > image_dim[0] -1)) {
- value = cval;
- } else {
- value = input[index_y*image_dim[1] + index_x];
- }
- break;
- }
-
- if (value == value) { // Ignore NaNs
- *it = value;
- ++it;
- }
- }
- }
- }
-
- //window_size can be smaller than kernel size in shrink mode or if there is NaNs
- int window_size = std::distance(window_values.begin(), it);
-
- if (window_size == 0) {
- // Window is empty, this is the case when all values are NaNs
- output[image_dim[1]*y_pixel + x_pixel] = NotANumber<T>();
- } else {
- // apply the median value if needed for this pixel
- const T currentPixelValue = input[image_dim[1]*y_pixel + x_pixel];
- if (conditional == true){
- typename std::vector<T>::iterator window_end = window_values.begin() + window_size;
- T min = 0;
- T max = 0;
- getMinMax(window_values, min, max, window_end);
- // NaNs are propagated through unchanged
- if ((currentPixelValue == max) || (currentPixelValue == min)){
- output[image_dim[1]*y_pixel + x_pixel] = median<T>(window_values, window_size);
- }else{
- output[image_dim[1]*y_pixel + x_pixel] = currentPixelValue;
- }
- }else{
- output[image_dim[1]*y_pixel + x_pixel] = median<T>(window_values, window_size);
- }
- }
- }
-}
-
-#endif // MEDIAN_FILTER
diff --git a/silx/math/medianfilter/median_filter.pxd b/silx/math/medianfilter/median_filter.pxd
deleted file mode 100644
index 2fc0283..0000000
--- a/silx/math/medianfilter/median_filter.pxd
+++ /dev/null
@@ -1,42 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2015-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-
-from libcpp cimport bool
-
-# pyx
-cdef extern from "median_filter.hpp":
- cdef extern void median_filter[T](const T* image,
- T* output,
- int* kernel_dim,
- int* image_dim,
- int x_pixel_range_min,
- int x_pixel_range_max,
- int y_pixel_range_min,
- int y_pixel_range_max,
- bool conditional,
- T cval) nogil;
-
- cdef extern int reflect(int index, int length_max);
- cdef extern int mirror(int index, int length_max);
diff --git a/silx/math/medianfilter/medianfilter.pyx b/silx/math/medianfilter/medianfilter.pyx
deleted file mode 100644
index fe05a78..0000000
--- a/silx/math/medianfilter/medianfilter.pyx
+++ /dev/null
@@ -1,496 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2015-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module provides median filter function for 1D and 2D arrays.
-"""
-
-__authors__ = ["H. Payno", "J. Kieffer"]
-__license__ = "MIT"
-__date__ = "02/05/2017"
-
-
-from cython.parallel import prange
-cimport cython
-cimport silx.math.medianfilter.median_filter as median_filter
-import numpy
-cimport numpy as cnumpy
-from libcpp cimport bool
-
-import numbers
-
-ctypedef unsigned long uint64
-ctypedef unsigned int uint32
-ctypedef unsigned short uint16
-
-
-MODES = {'nearest': 0, 'reflect': 1, 'mirror': 2, 'shrink': 3, 'constant': 4}
-
-
-def medfilt1d(data,
- kernel_size=3,
- bool conditional=False,
- mode='nearest',
- cval=0):
- """Function computing the median filter of the given input.
-
- Behavior at boundaries: the algorithm is reducing the size of the
- window/kernel for pixels at boundaries (there is no mirroring).
-
- Not-a-Number (NaN) float values are ignored.
- If the window only contains NaNs, it evaluates to NaN.
-
- In event of an even number of valid values in the window (either
- because of NaN values or on image border in shrink mode),
- the highest of the 2 central sorted values is taken.
-
- :param numpy.ndarray data: the array for which we want to apply
- the median filter. Should be 1d.
- :param kernel_size: the dimension of the kernel.
- :type kernel_size: int
- :param bool conditional: True if we want to apply a conditional median
- filtering.
- :param str mode: the algorithm used to determine how values at borders
- are determined: 'nearest', 'reflect', 'mirror', 'shrink', 'constant'
- :param cval: Value used outside borders in 'constant' mode
-
- :returns: the array with the median value for each pixel.
- """
- return medfilt(data, kernel_size, conditional, mode, cval)
-
-
-def medfilt2d(image,
- kernel_size=3,
- bool conditional=False,
- mode='nearest',
- cval=0):
- """Function computing the median filter of the given input.
- Behavior at boundaries: the algorithm is reducing the size of the
- window/kernel for pixels at boundaries (there is no mirroring).
-
- Not-a-Number (NaN) float values are ignored.
- If the window only contains NaNs, it evaluates to NaN.
-
- In event of an even number of valid values in the window (either
- because of NaN values or on image border in shrink mode),
- the highest of the 2 central sorted values is taken.
-
- :param numpy.ndarray data: the array for which we want to apply
- the median filter. Should be 2d.
- :param kernel_size: the dimension of the kernel.
- :type kernel_size: For 1D should be an int for 2D should be a tuple or
- a list of (kernel_height, kernel_width)
- :param bool conditional: True if we want to apply a conditional median
- filtering.
- :param str mode: the algorithm used to determine how values at borders
- are determined: 'nearest', 'reflect', 'mirror', 'shrink', 'constant'
- :param cval: Value used outside borders in 'constant' mode
-
- :returns: the array with the median value for each pixel.
- """
- return medfilt(image, kernel_size, conditional, mode, cval)
-
-
-def medfilt(data,
- kernel_size=3,
- bool conditional=False,
- mode='nearest',
- cval=0):
- """Function computing the median filter of the given input.
- Behavior at boundaries: the algorithm is reducing the size of the
- window/kernel for pixels at boundaries (there is no mirroring).
-
- Not-a-Number (NaN) float values are ignored.
- If the window only contains NaNs, it evaluates to NaN.
-
- In event of an even number of valid values in the window (either
- because of NaN values or on image border in shrink mode),
- the highest of the 2 central sorted values is taken.
-
- :param numpy.ndarray data: the array for which we want to apply
- the median filter. Should be 1d or 2d.
- :param kernel_size: the dimension of the kernel.
- :type kernel_size: For 1D should be an int for 2D should be a tuple or
- a list of (kernel_height, kernel_width)
- :param bool conditional: True if we want to apply a conditional median
- filtering.
- :param str mode: the algorithm used to determine how values at borders
- are determined: 'nearest', 'reflect', 'mirror', 'shrink', 'constant'
- :param cval: Value used outside borders in 'constant' mode
-
- :returns: the array with the median value for each pixel.
- """
- if mode not in MODES:
- err = 'Requested mode %s is unknown.' % mode
- raise ValueError(err)
-
- if data.ndim > 2:
- raise ValueError(
- "Invalid data shape. Dimension of the array should be 1 or 2")
-
- # Handle case of scalar kernel size
- if isinstance(kernel_size, numbers.Integral):
- kernel_size = [kernel_size] * data.ndim
-
- assert len(kernel_size) == data.ndim
-
- # Convert 1D arrays to 2D
- reshaped = False
- if len(data.shape) == 1:
- data = data.reshape(1, data.shape[0])
- kernel_size = [1, kernel_size[0]]
- reshaped = True
-
- # simple median filter apply into a 2D buffer
- output_buffer = numpy.zeros_like(data)
- check(data, output_buffer)
-
- ker_dim = numpy.array(kernel_size, dtype=numpy.int32)
-
- if data.dtype == numpy.float64:
- medfilterfc = _median_filter_float64
- elif data.dtype == numpy.float32:
- medfilterfc = _median_filter_float32
- elif data.dtype == numpy.int64:
- medfilterfc = _median_filter_int64
- elif data.dtype == numpy.uint64:
- medfilterfc = _median_filter_uint64
- elif data.dtype == numpy.int32:
- medfilterfc = _median_filter_int32
- elif data.dtype == numpy.uint32:
- medfilterfc = _median_filter_uint32
- elif data.dtype == numpy.int16:
- medfilterfc = _median_filter_int16
- elif data.dtype == numpy.uint16:
- medfilterfc = _median_filter_uint16
- else:
- raise ValueError("%s type is not managed by the median filter" % data.dtype)
-
- medfilterfc(input_buffer=data,
- output_buffer=output_buffer,
- kernel_size=ker_dim,
- conditional=conditional,
- mode=MODES[mode],
- cval=cval)
-
- if reshaped:
- output_buffer.shape = -1 # Convert to 1D array
-
- return output_buffer
-
-
-def check(input_buffer, output_buffer):
- """Simple check on the two buffers to make sure we can apply the median filter
- """
- if (input_buffer.flags['C_CONTIGUOUS'] is False):
- raise ValueError('<input_buffer> must be a C_CONTIGUOUS numpy array.')
-
- if (output_buffer.flags['C_CONTIGUOUS'] is False):
- raise ValueError('<output_buffer> must be a C_CONTIGUOUS numpy array.')
-
- if not (len(input_buffer.shape) <= 2):
- raise ValueError('<input_buffer> dimension must mo higher than 2.')
-
- if not (len(output_buffer.shape) <= 2):
- raise ValueError('<output_buffer> dimension must mo higher than 2.')
-
- if not(input_buffer.dtype == output_buffer.dtype):
- raise ValueError('input buffer and output_buffer must be of the same type')
-
- if not (input_buffer.shape == output_buffer.shape):
- raise ValueError('input buffer and output_buffer must be of the same dimension and same dimension')
-
-
-######### implementations of the include/median_filter.hpp function ############
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def reflect(int index, int length_max):
- """find the correct index into [0, length_max-1] for index in reflect mode
-
- :param int index: the index to move into [0, length_max-1] in reflect mode
- :param int length_max: the higher bound limit
- """
- return median_filter.reflect(index, length_max)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def mirror(int index, int length_max):
- """find the correct index into [0, length_max-1] for index in mirror mode
-
- :param int index: the index to move into [0, length_max-1] in mirror mode
- :param int length_max: the higher bound limit
- """
- return median_filter.mirror(index, length_max)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_float32(float[:, ::1] input_buffer not None,
- float[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- float cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[float](<float*> & input_buffer[0,0],
- <float*> & output_buffer[0,0],
- <int*>& kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_float64(double[:, ::1] input_buffer not None,
- double[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- double cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[double](<double*> & input_buffer[0, 0],
- <double*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_int64(cnumpy.int64_t[:, ::1] input_buffer not None,
- cnumpy.int64_t[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- cnumpy.int64_t cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[long](<long*> & input_buffer[0,0],
- <long*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_uint64(cnumpy.uint64_t[:, ::1] input_buffer not None,
- cnumpy.uint64_t[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- cnumpy.uint64_t cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[uint64](<uint64*> & input_buffer[0,0],
- <uint64*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_int32(cnumpy.int32_t[:, ::1] input_buffer not None,
- cnumpy.int32_t[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- cnumpy.int32_t cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[int](<int*> & input_buffer[0,0],
- <int*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_uint32(cnumpy.uint32_t[:, ::1] input_buffer not None,
- cnumpy.uint32_t[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- cnumpy.uint32_t cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[uint32](<uint32*> & input_buffer[0,0],
- <uint32*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_int16(cnumpy.int16_t[:, ::1] input_buffer not None,
- cnumpy.int16_t[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- cnumpy.int16_t cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[short](<short*> & input_buffer[0,0],
- <short*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
-
-
-@cython.cdivision(True)
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.initializedcheck(False)
-def _median_filter_uint16(
- cnumpy.uint16_t[:, ::1] input_buffer not None,
- cnumpy.uint16_t[:, ::1] output_buffer not None,
- cnumpy.int32_t[::1] kernel_size not None,
- bool conditional,
- int mode,
- cnumpy.uint16_t cval):
-
- cdef:
- int y = 0
- int image_dim = input_buffer.shape[1] - 1
- int[2] buffer_shape,
- buffer_shape[0] = input_buffer.shape[0]
- buffer_shape[1] = input_buffer.shape[1]
-
- for y in prange(input_buffer.shape[0], nogil=True):
- median_filter.median_filter[uint16](<uint16*> & input_buffer[0, 0],
- <uint16*> & output_buffer[0, 0],
- <int*>&kernel_size[0],
- <int*>buffer_shape,
- y,
- 0,
- image_dim,
- conditional,
- mode,
- cval)
diff --git a/silx/math/medianfilter/setup.py b/silx/math/medianfilter/setup.py
deleted file mode 100644
index d228357..0000000
--- a/silx/math/medianfilter/setup.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "02/05/2017"
-
-
-import numpy
-
-from numpy.distutils.misc_util import Configuration
-
-
-def configuration(parent_package='', top_path=None):
- config = Configuration('medianfilter', parent_package, top_path)
- config.add_subpackage('test')
-
- # =====================================
- # median filter
- # =====================================
- medfilt_src = ['medianfilter.pyx']
- medfilt_inc = ['include', numpy.get_include()]
- extra_link_args = ['-fopenmp']
- extra_compile_args = ['-fopenmp']
- config.add_extension('medianfilter',
- sources=medfilt_src,
- include_dirs=[medfilt_inc],
- language='c++',
- extra_link_args=extra_link_args,
- extra_compile_args=extra_compile_args)
-
- return config
-
-
-if __name__ == "__main__":
- from numpy.distutils.core import setup
-
- setup(configuration=configuration) \ No newline at end of file
diff --git a/silx/math/medianfilter/test/__init__.py b/silx/math/medianfilter/test/__init__.py
deleted file mode 100644
index 92a6524..0000000
--- a/silx/math/medianfilter/test/__init__.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-__authors__ = ["H. Payno"]
-__license__ = "MIT"
-__date__ = "22/06/2016"
-
-import unittest
-
-from . import test_medianfilter
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(test_medianfilter.suite())
- return test_suite
diff --git a/silx/math/medianfilter/test/benchmark.py b/silx/math/medianfilter/test/benchmark.py
deleted file mode 100644
index cbb16b3..0000000
--- a/silx/math/medianfilter/test/benchmark.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2017-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Tests of the median filter"""
-
-__authors__ = ["H. Payno"]
-__license__ = "MIT"
-__date__ = "02/05/2017"
-
-from silx.gui import qt
-from silx.math.medianfilter import medfilt2d as medfilt2d_silx
-import numpy
-import numpy.random
-from timeit import Timer
-from silx.gui.plot import Plot1D
-import logging
-
-try:
- import scipy
-except:
- scipy = None
-else:
- import scipy.ndimage
-
-try:
- import PyMca5.PyMca as pymca
-except:
- pymca = None
-else:
- from PyMca5.PyMca.median import medfilt2d as medfilt2d_pymca
-
-logger = logging.getLogger(__name__)
-logger.setLevel(logging.INFO)
-
-
-class BenchmarkMedianFilter(object):
- """Simple benchmark of the median fiter silx vs scipy"""
-
- NB_ITER = 3
-
- def __init__(self, imageWidth, kernels):
- self.img = numpy.random.rand(imageWidth, imageWidth)
- self.kernels = kernels
-
- self.run()
-
- def run(self):
- self.execTime = {}
- for kernel in self.kernels:
- self.execTime[kernel] = self.bench(kernel)
-
- def bench(self, width):
- def execSilx():
- medfilt2d_silx(self.img, width)
-
- def execScipy():
- scipy.ndimage.median_filter(input=self.img,
- size=width,
- mode='nearest')
-
- def execPymca():
- medfilt2d_pymca(self.img, width)
-
- execTime = {}
-
- t = Timer(execSilx)
- execTime["silx"] = t.timeit(BenchmarkMedianFilter.NB_ITER)
- logger.info(
- 'exec time silx (kernel size = %s) is %s' % (width, execTime["silx"]))
-
- if scipy is not None:
- t = Timer(execScipy)
- execTime["scipy"] = t.timeit(BenchmarkMedianFilter.NB_ITER)
- logger.info(
- 'exec time scipy (kernel size = %s) is %s' % (width, execTime["scipy"]))
- if pymca is not None:
- t = Timer(execPymca)
- execTime["pymca"] = t.timeit(BenchmarkMedianFilter.NB_ITER)
- logger.info(
- 'exec time pymca (kernel size = %s) is %s' % (width, execTime["pymca"]))
-
- return execTime
-
- def getExecTimeFor(self, id):
- res = []
- for k in self.kernels:
- res.append(self.execTime[k][id])
- return res
-
-
-app = qt.QApplication([])
-kernels = [3, 5, 7, 11, 15]
-benchmark = BenchmarkMedianFilter(imageWidth=1000, kernels=kernels)
-plot = Plot1D()
-plot.addCurve(x=kernels, y=benchmark.getExecTimeFor("silx"), legend='silx')
-if scipy is not None:
- plot.addCurve(x=kernels, y=benchmark.getExecTimeFor("scipy"), legend='scipy')
-if pymca is not None:
- plot.addCurve(x=kernels, y=benchmark.getExecTimeFor("pymca"), legend='pymca')
-plot.show()
-app.exec_()
-del app
diff --git a/silx/math/medianfilter/test/test_medianfilter.py b/silx/math/medianfilter/test/test_medianfilter.py
deleted file mode 100644
index 3a45b3d..0000000
--- a/silx/math/medianfilter/test/test_medianfilter.py
+++ /dev/null
@@ -1,740 +0,0 @@
-# coding: utf-8
-# ##########################################################################
-# Copyright (C) 2017-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################
-"""Tests of the median filter"""
-
-__authors__ = ["H. Payno"]
-__license__ = "MIT"
-__date__ = "17/01/2018"
-
-import unittest
-import numpy
-from silx.math.medianfilter import medfilt2d, medfilt1d
-from silx.math.medianfilter.medianfilter import reflect, mirror
-from silx.math.medianfilter.medianfilter import MODES as silx_mf_modes
-from silx.utils.testutils import ParametricTestCase
-try:
- import scipy
- import scipy.misc
-except:
- scipy = None
-else:
- import scipy.ndimage
-
-import logging
-_logger = logging.getLogger(__name__)
-
-RANDOM_FLOAT_MAT = numpy.array([
- [0.05564293, 0.62717157, 0.75002406, 0.40555336, 0.70278975],
- [0.76532598, 0.02839148, 0.05272484, 0.65166994, 0.42161216],
- [0.23067427, 0.74219128, 0.56049024, 0.44406320, 0.28773158],
- [0.81025249, 0.20303021, 0.68382382, 0.46372299, 0.81281709],
- [0.94691602, 0.07813661, 0.81651256, 0.84220106, 0.33623165]])
-
-RANDOM_INT_MAT = numpy.array([
- [0, 5, 2, 6, 1],
- [2, 3, 1, 7, 1],
- [9, 8, 6, 7, 8],
- [5, 6, 8, 2, 4]])
-
-
-class TestMedianFilterNearest(ParametricTestCase):
- """Unit tests for the median filter in nearest mode"""
-
- def testFilter3_100(self):
- """Test median filter on a 10x10 matrix with a 3x3 kernel."""
- dataIn = numpy.arange(100, dtype=numpy.int32)
- dataIn = dataIn.reshape((10, 10))
-
- dataOut = medfilt2d(image=dataIn,
- kernel_size=(3, 3),
- conditional=False,
- mode='nearest')
- self.assertTrue(dataOut[0, 0] == 1)
- self.assertTrue(dataOut[9, 0] == 90)
- self.assertTrue(dataOut[9, 9] == 98)
-
- self.assertTrue(dataOut[0, 9] == 9)
- self.assertTrue(dataOut[0, 4] == 5)
- self.assertTrue(dataOut[9, 4] == 93)
- self.assertTrue(dataOut[4, 4] == 44)
-
- def testFilter3_9(self):
- "Test median filter on a 3x3 matrix with a 3x3 kernel."
- dataIn = numpy.array([0, -1, 1,
- 12, 6, -2,
- 100, 4, 12],
- dtype=numpy.int16)
- dataIn = dataIn.reshape((3, 3))
- dataOut = medfilt2d(image=dataIn,
- kernel_size=(3, 3),
- conditional=False,
- mode='nearest')
- self.assertTrue(dataOut.shape == dataIn.shape)
- self.assertTrue(dataOut[1, 1] == 4)
- self.assertTrue(dataOut[0, 0] == 0)
- self.assertTrue(dataOut[0, 1] == 0)
- self.assertTrue(dataOut[1, 0] == 6)
-
- def testFilterWidthOne(self):
- """Make sure a filter of one by one give the same result as the input
- """
- dataIn = numpy.arange(100, dtype=numpy.int32)
- dataIn = dataIn.reshape((10, 10))
-
- dataOut = medfilt2d(image=dataIn,
- kernel_size=(1, 1),
- conditional=False,
- mode='nearest')
-
- self.assertTrue(numpy.array_equal(dataIn, dataOut))
-
- def testFilter3_1d(self):
- """Test binding and result of the 1d filter"""
- self.assertTrue(numpy.array_equal(
- medfilt1d(RANDOM_INT_MAT[0], kernel_size=3, conditional=False,
- mode='nearest'),
- [0, 2, 5, 2, 1])
- )
-
- def testFilter3Conditionnal(self):
- """Test that the conditional filter apply correctly in a 10x10 matrix
- with a 3x3 kernel
- """
- dataIn = numpy.arange(100, dtype=numpy.int32)
- dataIn = dataIn.reshape((10, 10))
-
- dataOut = medfilt2d(image=dataIn,
- kernel_size=(3, 3),
- conditional=True,
- mode='nearest')
- self.assertTrue(dataOut[0, 0] == 1)
- self.assertTrue(dataOut[0, 1] == 1)
- self.assertTrue(numpy.array_equal(dataOut[1:8, 1:8], dataIn[1:8, 1:8]))
- self.assertTrue(dataOut[9, 9] == 98)
-
- def testFilter3_1D(self):
- """Simple test of a 3x3 median filter on a 1D array"""
- dataIn = numpy.arange(100, dtype=numpy.int32)
-
- dataOut = medfilt2d(image=dataIn,
- kernel_size=(5),
- conditional=False,
- mode='nearest')
-
- self.assertTrue(dataOut[0] == 0)
- self.assertTrue(dataOut[9] == 9)
- self.assertTrue(dataOut[99] == 99)
-
- def testNaNs(self):
- """Test median filter on image with NaNs in nearest mode"""
- # Data with a NaN in first corner
- nan_corner = numpy.arange(100.).reshape(10, 10)
- nan_corner[0, 0] = numpy.nan
- output = medfilt2d(
- nan_corner, kernel_size=3, conditional=False, mode='nearest')
- self.assertEqual(output[0, 0], 10)
- self.assertEqual(output[0, 1], 2)
- self.assertEqual(output[1, 0], 11)
- self.assertEqual(output[1, 1], 12)
-
- # Data with some NaNs
- some_nans = numpy.arange(100.).reshape(10, 10)
- some_nans[0, 1] = numpy.nan
- some_nans[1, 1] = numpy.nan
- some_nans[1, 0] = numpy.nan
- output = medfilt2d(
- some_nans, kernel_size=3, conditional=False, mode='nearest')
- self.assertEqual(output[0, 0], 0)
- self.assertEqual(output[0, 1], 2)
- self.assertEqual(output[1, 0], 20)
- self.assertEqual(output[1, 1], 20)
-
-
-class TestMedianFilterReflect(ParametricTestCase):
- """Unit test for the median filter in reflect mode"""
-
- def testArange9(self):
- """Test from a 3x3 window to RANDOM_FLOAT_MAT"""
- img = numpy.arange(9, dtype=numpy.int32)
- img = img.reshape(3, 3)
- kernel = (3, 3)
- res = medfilt2d(image=img,
- kernel_size=kernel,
- conditional=False,
- mode='reflect')
- self.assertTrue(
- numpy.array_equal(res.ravel(), [1, 2, 2, 3, 4, 5, 6, 6, 7]))
-
- def testRandom10(self):
- """Test a (5, 3) window to a RANDOM_FLOAT_MAT"""
- kernel = (5, 3)
-
- thRes = numpy.array([
- [0.23067427, 0.56049024, 0.56049024, 0.4440632, 0.42161216],
- [0.23067427, 0.62717157, 0.56049024, 0.56049024, 0.46372299],
- [0.62717157, 0.62717157, 0.56049024, 0.56049024, 0.4440632],
- [0.76532598, 0.68382382, 0.56049024, 0.56049024, 0.42161216],
- [0.81025249, 0.68382382, 0.56049024, 0.68382382, 0.46372299]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=False,
- mode='reflect')
-
- self.assertTrue(numpy.array_equal(thRes, res))
-
- def testApplyReflect1D(self):
- """Test the reflect function used for the median filter in reflect mode
- """
- # test for inside values
- self.assertTrue(reflect(2, 3) == 2)
- # test for boundaries values
- self.assertTrue(reflect(3, 3) == 2)
- self.assertTrue(reflect(4, 3) == 1)
- self.assertTrue(reflect(5, 3) == 0)
- self.assertTrue(reflect(6, 3) == 0)
- self.assertTrue(reflect(7, 3) == 1)
- self.assertTrue(reflect(-1, 3) == 0)
- self.assertTrue(reflect(-2, 3) == 1)
- self.assertTrue(reflect(-3, 3) == 2)
- self.assertTrue(reflect(-4, 3) == 2)
- self.assertTrue(reflect(-5, 3) == 1)
- self.assertTrue(reflect(-6, 3) == 0)
- self.assertTrue(reflect(-7, 3) == 0)
-
- def testRandom10Conditionnal(self):
- """Test the median filter in reflect mode and with the conditionnal
- option"""
- kernel = (3, 1)
-
- thRes = numpy.array([
- [0.05564293, 0.62717157, 0.75002406, 0.40555336, 0.70278975],
- [0.23067427, 0.62717157, 0.56049024, 0.44406320, 0.42161216],
- [0.76532598, 0.20303021, 0.56049024, 0.46372299, 0.42161216],
- [0.81025249, 0.20303021, 0.68382382, 0.46372299, 0.33623165],
- [0.94691602, 0.07813661, 0.81651256, 0.84220106, 0.33623165]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=True,
- mode='reflect')
- self.assertTrue(numpy.array_equal(thRes, res))
-
- def testNaNs(self):
- """Test median filter on image with NaNs in reflect mode"""
- # Data with a NaN in first corner
- nan_corner = numpy.arange(100.).reshape(10, 10)
- nan_corner[0, 0] = numpy.nan
- output = medfilt2d(
- nan_corner, kernel_size=3, conditional=False, mode='reflect')
- self.assertEqual(output[0, 0], 10)
- self.assertEqual(output[0, 1], 2)
- self.assertEqual(output[1, 0], 11)
- self.assertEqual(output[1, 1], 12)
-
- # Data with some NaNs
- some_nans = numpy.arange(100.).reshape(10, 10)
- some_nans[0, 1] = numpy.nan
- some_nans[1, 1] = numpy.nan
- some_nans[1, 0] = numpy.nan
- output = medfilt2d(
- some_nans, kernel_size=3, conditional=False, mode='reflect')
- self.assertEqual(output[0, 0], 0)
- self.assertEqual(output[0, 1], 2)
- self.assertEqual(output[1, 0], 20)
- self.assertEqual(output[1, 1], 20)
-
- def testFilter3_1d(self):
- """Test binding and result of the 1d filter"""
- self.assertTrue(numpy.array_equal(
- medfilt1d(RANDOM_INT_MAT[0], kernel_size=5, conditional=False,
- mode='reflect'),
- [2, 2, 2, 2, 2])
- )
-
-
-class TestMedianFilterMirror(ParametricTestCase):
- """Unit test for the median filter in mirror mode
- """
-
- def testApplyMirror1D(self):
- """Test the reflect function used for the median filter in mirror mode
- """
- # test for inside values
- self.assertTrue(mirror(2, 3) == 2)
- # test for boundaries values
- self.assertTrue(mirror(4, 4) == 2)
- self.assertTrue(mirror(5, 4) == 1)
- self.assertTrue(mirror(6, 4) == 0)
- self.assertTrue(mirror(7, 4) == 1)
- self.assertTrue(mirror(8, 4) == 2)
- self.assertTrue(mirror(-1, 4) == 1)
- self.assertTrue(mirror(-2, 4) == 2)
- self.assertTrue(mirror(-3, 4) == 3)
- self.assertTrue(mirror(-4, 4) == 2)
- self.assertTrue(mirror(-5, 4) == 1)
- self.assertTrue(mirror(-6, 4) == 0)
-
- def testRandom10(self):
- """Test a (5, 3) window to a random array"""
- kernel = (3, 5)
-
- thRes = numpy.array([
- [0.05272484, 0.40555336, 0.42161216, 0.42161216, 0.42161216],
- [0.56049024, 0.56049024, 0.4440632, 0.4440632, 0.4440632],
- [0.56049024, 0.46372299, 0.46372299, 0.46372299, 0.46372299],
- [0.68382382, 0.56049024, 0.56049024, 0.46372299, 0.56049024],
- [0.68382382, 0.46372299, 0.68382382, 0.46372299, 0.68382382]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=False,
- mode='mirror')
-
- self.assertTrue(numpy.array_equal(thRes, res))
-
- def testRandom10Conditionnal(self):
- """Test the median filter in reflect mode and with the conditionnal
- option"""
- kernel = (1, 3)
-
- thRes = numpy.array([
- [0.62717157, 0.62717157, 0.62717157, 0.70278975, 0.40555336],
- [0.02839148, 0.05272484, 0.05272484, 0.42161216, 0.65166994],
- [0.74219128, 0.56049024, 0.56049024, 0.44406320, 0.44406320],
- [0.20303021, 0.68382382, 0.46372299, 0.68382382, 0.46372299],
- [0.07813661, 0.81651256, 0.81651256, 0.81651256, 0.84220106]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=True,
- mode='mirror')
-
- self.assertTrue(numpy.array_equal(thRes, res))
-
- def testNaNs(self):
- """Test median filter on image with NaNs in mirror mode"""
- # Data with a NaN in first corner
- nan_corner = numpy.arange(100.).reshape(10, 10)
- nan_corner[0, 0] = numpy.nan
- output = medfilt2d(
- nan_corner, kernel_size=3, conditional=False, mode='mirror')
- self.assertEqual(output[0, 0], 11)
- self.assertEqual(output[0, 1], 11)
- self.assertEqual(output[1, 0], 11)
- self.assertEqual(output[1, 1], 12)
-
- # Data with some NaNs
- some_nans = numpy.arange(100.).reshape(10, 10)
- some_nans[0, 1] = numpy.nan
- some_nans[1, 1] = numpy.nan
- some_nans[1, 0] = numpy.nan
- output = medfilt2d(
- some_nans, kernel_size=3, conditional=False, mode='mirror')
- self.assertEqual(output[0, 0], 0)
- self.assertEqual(output[0, 1], 12)
- self.assertEqual(output[1, 0], 21)
- self.assertEqual(output[1, 1], 20)
-
- def testFilter3_1d(self):
- """Test binding and result of the 1d filter"""
- self.assertTrue(numpy.array_equal(
- medfilt1d(RANDOM_INT_MAT[0], kernel_size=5, conditional=False,
- mode='mirror'),
- [2, 5, 2, 5, 2])
- )
-
-class TestMedianFilterShrink(ParametricTestCase):
- """Unit test for the median filter in mirror mode
- """
-
- def testRandom_3x3(self):
- """Test the median filter in shrink mode and with the conditionnal
- option"""
- kernel = (3, 3)
-
- thRes = numpy.array([
- [0.62717157, 0.62717157, 0.62717157, 0.65166994, 0.65166994],
- [0.62717157, 0.56049024, 0.56049024, 0.44406320, 0.44406320],
- [0.74219128, 0.56049024, 0.46372299, 0.46372299, 0.46372299],
- [0.74219128, 0.68382382, 0.56049024, 0.56049024, 0.46372299],
- [0.81025249, 0.81025249, 0.68382382, 0.81281709, 0.81281709]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=False,
- mode='shrink')
-
- self.assertTrue(numpy.array_equal(thRes, res))
-
- def testBounds(self):
- """Test the median filter in shrink mode with 3 different kernels
- which should return the same result due to the large values of kernels
- used.
- """
- kernel1 = (1, 9)
- kernel2 = (1, 11)
- kernel3 = (1, 21)
-
- thRes = numpy.array([[2, 2, 2, 2, 2],
- [2, 2, 2, 2, 2],
- [8, 8, 8, 8, 8],
- [5, 5, 5, 5, 5]])
-
- resK1 = medfilt2d(image=RANDOM_INT_MAT,
- kernel_size=kernel1,
- conditional=False,
- mode='shrink')
-
- resK2 = medfilt2d(image=RANDOM_INT_MAT,
- kernel_size=kernel2,
- conditional=False,
- mode='shrink')
-
- resK3 = medfilt2d(image=RANDOM_INT_MAT,
- kernel_size=kernel3,
- conditional=False,
- mode='shrink')
-
- self.assertTrue(numpy.array_equal(resK1, thRes))
- self.assertTrue(numpy.array_equal(resK2, resK1))
- self.assertTrue(numpy.array_equal(resK3, resK1))
-
- def testRandom_3x3Conditionnal(self):
- """Test the median filter in reflect mode and with the conditionnal
- option"""
- kernel = (3, 3)
-
- thRes = numpy.array([
- [0.05564293, 0.62717157, 0.62717157, 0.40555336, 0.65166994],
- [0.62717157, 0.56049024, 0.05272484, 0.65166994, 0.42161216],
- [0.23067427, 0.74219128, 0.56049024, 0.44406320, 0.46372299],
- [0.81025249, 0.20303021, 0.68382382, 0.46372299, 0.81281709],
- [0.81025249, 0.81025249, 0.81651256, 0.81281709, 0.81281709]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=True,
- mode='shrink')
-
- self.assertTrue(numpy.array_equal(res, thRes))
-
- def testRandomInt(self):
- """Test 3x3 kernel on RANDOM_INT_MAT
- """
- kernel = (3, 3)
-
- thRes = numpy.array([[3, 2, 5, 2, 6],
- [5, 3, 6, 6, 7],
- [6, 6, 6, 6, 7],
- [8, 8, 7, 7, 7]])
-
- resK1 = medfilt2d(image=RANDOM_INT_MAT,
- kernel_size=kernel,
- conditional=False,
- mode='shrink')
-
- self.assertTrue(numpy.array_equal(resK1, thRes))
-
- def testNaNs(self):
- """Test median filter on image with NaNs in shrink mode"""
- # Data with a NaN in first corner
- nan_corner = numpy.arange(100.).reshape(10, 10)
- nan_corner[0, 0] = numpy.nan
- output = medfilt2d(
- nan_corner, kernel_size=3, conditional=False, mode='shrink')
- self.assertEqual(output[0, 0], 10)
- self.assertEqual(output[0, 1], 10)
- self.assertEqual(output[1, 0], 11)
- self.assertEqual(output[1, 1], 12)
-
- # Data with some NaNs
- some_nans = numpy.arange(100.).reshape(10, 10)
- some_nans[0, 1] = numpy.nan
- some_nans[1, 1] = numpy.nan
- some_nans[1, 0] = numpy.nan
- output = medfilt2d(
- some_nans, kernel_size=3, conditional=False, mode='shrink')
- self.assertEqual(output[0, 0], 0)
- self.assertEqual(output[0, 1], 2)
- self.assertEqual(output[1, 0], 20)
- self.assertEqual(output[1, 1], 20)
-
- def testFilter3_1d(self):
- """Test binding and result of the 1d filter"""
- self.assertTrue(numpy.array_equal(
- medfilt1d(RANDOM_INT_MAT[0], kernel_size=3, conditional=False,
- mode='shrink'),
- [5, 2, 5, 2, 6])
- )
-
-class TestMedianFilterConstant(ParametricTestCase):
- """Unit test for the median filter in constant mode
- """
-
- def testRandom10(self):
- """Test a (5, 3) window to a random array"""
- kernel = (3, 5)
-
- thRes = numpy.array([
- [0., 0.02839148, 0.05564293, 0.02839148, 0.],
- [0.05272484, 0.40555336, 0.4440632, 0.42161216, 0.28773158],
- [0.05272484, 0.44406320, 0.46372299, 0.42161216, 0.28773158],
- [0.20303021, 0.46372299, 0.56049024, 0.44406320, 0.33623165],
- [0., 0.07813661, 0.33623165, 0.07813661, 0.]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=False,
- mode='constant')
-
- self.assertTrue(numpy.array_equal(thRes, res))
-
- RANDOM_FLOAT_MAT = numpy.array([
- [0.05564293, 0.62717157, 0.75002406, 0.40555336, 0.70278975],
- [0.76532598, 0.02839148, 0.05272484, 0.65166994, 0.42161216],
- [0.23067427, 0.74219128, 0.56049024, 0.44406320, 0.28773158],
- [0.81025249, 0.20303021, 0.68382382, 0.46372299, 0.81281709],
- [0.94691602, 0.07813661, 0.81651256, 0.84220106, 0.33623165]])
-
- def testRandom10Conditionnal(self):
- """Test the median filter in reflect mode and with the conditionnal
- option"""
- kernel = (1, 3)
-
- print(RANDOM_FLOAT_MAT)
-
- thRes = numpy.array([
- [0.05564293, 0.62717157, 0.62717157, 0.70278975, 0.40555336],
- [0.02839148, 0.05272484, 0.05272484, 0.42161216, 0.42161216],
- [0.23067427, 0.56049024, 0.56049024, 0.44406320, 0.28773158],
- [0.20303021, 0.68382382, 0.46372299, 0.68382382, 0.46372299],
- [0.07813661, 0.81651256, 0.81651256, 0.81651256, 0.33623165]])
-
- res = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=True,
- mode='constant')
-
- self.assertTrue(numpy.array_equal(thRes, res))
-
- def testNaNs(self):
- """Test median filter on image with NaNs in constant mode"""
- # Data with a NaN in first corner
- nan_corner = numpy.arange(100.).reshape(10, 10)
- nan_corner[0, 0] = numpy.nan
- output = medfilt2d(nan_corner,
- kernel_size=3,
- conditional=False,
- mode='constant',
- cval=0)
- self.assertEqual(output[0, 0], 0)
- self.assertEqual(output[0, 1], 2)
- self.assertEqual(output[1, 0], 10)
- self.assertEqual(output[1, 1], 12)
-
- # Data with some NaNs
- some_nans = numpy.arange(100.).reshape(10, 10)
- some_nans[0, 1] = numpy.nan
- some_nans[1, 1] = numpy.nan
- some_nans[1, 0] = numpy.nan
- output = medfilt2d(some_nans,
- kernel_size=3,
- conditional=False,
- mode='constant',
- cval=0)
- self.assertEqual(output[0, 0], 0)
- self.assertEqual(output[0, 1], 0)
- self.assertEqual(output[1, 0], 0)
- self.assertEqual(output[1, 1], 20)
-
- def testFilter3_1d(self):
- """Test binding and result of the 1d filter"""
- self.assertTrue(numpy.array_equal(
- medfilt1d(RANDOM_INT_MAT[0], kernel_size=5, conditional=False,
- mode='constant'),
- [0, 2, 2, 2, 1])
- )
-
-class TestGeneralExecution(ParametricTestCase):
- """Some general test on median filter application"""
-
- def testTypes(self):
- """Test that all needed types have their implementation of the median
- filter
- """
- for mode in silx_mf_modes:
- for testType in [numpy.float32, numpy.float64, numpy.int16,
- numpy.uint16, numpy.int32, numpy.int64,
- numpy.uint64]:
- with self.subTest(mode=mode, type=testType):
- data = (numpy.random.rand(10, 10) * 65000).astype(testType)
- out = medfilt2d(image=data,
- kernel_size=(3, 3),
- conditional=False,
- mode=mode)
- self.assertTrue(out.dtype.type is testType)
-
- def testInputDataIsNotModify(self):
- """Make sure input data is not modify by the median filter"""
- dataIn = numpy.arange(100, dtype=numpy.int32)
- dataIn = dataIn.reshape((10, 10))
- dataInCopy = dataIn.copy()
-
- for mode in silx_mf_modes:
- with self.subTest(mode=mode):
- medfilt2d(image=dataIn,
- kernel_size=(3, 3),
- conditional=False,
- mode=mode)
- self.assertTrue(numpy.array_equal(dataIn, dataInCopy))
-
- def testAllNaNs(self):
- """Test median filter on image all NaNs"""
- all_nans = numpy.empty((10, 10), dtype=numpy.float32)
- all_nans[:] = numpy.nan
-
- for mode in silx_mf_modes:
- for conditional in (True, False):
- with self.subTest(mode=mode, conditional=conditional):
- output = medfilt2d(
- all_nans,
- kernel_size=3,
- conditional=conditional,
- mode=mode,
- cval=numpy.nan)
- self.assertTrue(numpy.all(numpy.isnan(output)))
-
- def testConditionalWithNaNs(self):
- """Test that NaNs are propagated through conditional median filter"""
- for mode in silx_mf_modes:
- with self.subTest(mode=mode):
- image = numpy.ones((10, 10), dtype=numpy.float32)
- nan_mask = numpy.zeros_like(image, dtype=bool)
- nan_mask[0, 0] = True
- nan_mask[4, :] = True
- nan_mask[6, 4] = True
- image[nan_mask] = numpy.nan
- output = medfilt2d(
- image,
- kernel_size=3,
- conditional=True,
- mode=mode)
- out_isnan = numpy.isnan(output)
- self.assertTrue(numpy.all(out_isnan[nan_mask]))
- self.assertFalse(
- numpy.any(out_isnan[numpy.logical_not(nan_mask)]))
-
-
-def _getScipyAndSilxCommonModes():
- """return the mode which are comparable between silx and scipy"""
- modes = silx_mf_modes.copy()
- del modes['shrink']
- return modes
-
-
-@unittest.skipUnless(scipy is not None, "scipy not available")
-class TestVsScipy(ParametricTestCase):
- """Compare scipy.ndimage.median_filter vs silx.math.medianfilter
- on comparable
- """
- def testWithArange(self):
- """Test vs scipy with different kernels on arange matrix"""
- data = numpy.arange(10000, dtype=numpy.int32)
- data = data.reshape(100, 100)
-
- kernels = [(3, 7), (7, 5), (1, 1), (3, 3)]
- modesToTest = _getScipyAndSilxCommonModes()
- for kernel in kernels:
- for mode in modesToTest:
- with self.subTest(kernel=kernel, mode=mode):
- resScipy = scipy.ndimage.median_filter(input=data,
- size=kernel,
- mode=mode)
- resSilx = medfilt2d(image=data,
- kernel_size=kernel,
- conditional=False,
- mode=mode)
-
- self.assertTrue(numpy.array_equal(resScipy, resSilx))
-
- def testRandomMatrice(self):
- """Test vs scipy with different kernels on RANDOM_FLOAT_MAT"""
- kernels = [(3, 7), (7, 5), (1, 1), (3, 3)]
- modesToTest = _getScipyAndSilxCommonModes()
- for kernel in kernels:
- for mode in modesToTest:
- with self.subTest(kernel=kernel, mode=mode):
- resScipy = scipy.ndimage.median_filter(input=RANDOM_FLOAT_MAT,
- size=kernel,
- mode=mode)
-
- resSilx = medfilt2d(image=RANDOM_FLOAT_MAT,
- kernel_size=kernel,
- conditional=False,
- mode=mode)
-
- self.assertTrue(numpy.array_equal(resScipy, resSilx))
-
- def testAscentOrLena(self):
- """Test vs scipy with """
- if hasattr(scipy.misc, 'ascent'):
- img = scipy.misc.ascent()
- else:
- img = scipy.misc.lena()
-
- kernels = [(3, 1), (3, 5), (5, 9), (9, 3)]
- modesToTest = _getScipyAndSilxCommonModes()
-
- for kernel in kernels:
- for mode in modesToTest:
- with self.subTest(kernel=kernel, mode=mode):
- resScipy = scipy.ndimage.median_filter(input=img,
- size=kernel,
- mode=mode)
-
- resSilx = medfilt2d(image=img,
- kernel_size=kernel,
- conditional=False,
- mode=mode)
-
- self.assertTrue(numpy.array_equal(resScipy, resSilx))
-
-
-def suite():
- test_suite = unittest.TestSuite()
- for test in [TestGeneralExecution,
- TestVsScipy,
- TestMedianFilterNearest,
- TestMedianFilterReflect,
- TestMedianFilterMirror,
- TestMedianFilterShrink,
- TestMedianFilterConstant]:
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(test))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/math/setup.py b/silx/math/setup.py
deleted file mode 100644
index 8cc15e6..0000000
--- a/silx/math/setup.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "27/03/2017"
-
-import os.path
-
-import numpy
-
-from numpy.distutils.misc_util import Configuration
-
-
-def configuration(parent_package='', top_path=None):
- config = Configuration('math', parent_package, top_path)
- config.add_subpackage('test')
- config.add_subpackage('fit')
- config.add_subpackage('medianfilter')
- config.add_subpackage('fft')
-
- # =====================================
- # histogramnd
- # =====================================
- histo_src = [os.path.join('histogramnd', 'src', 'histogramnd_c.c'),
- 'chistogramnd.pyx']
- histo_inc = [os.path.join('histogramnd', 'include'),
- numpy.get_include()]
-
- config.add_extension('chistogramnd',
- sources=histo_src,
- include_dirs=histo_inc,
- language='c')
-
- # =====================================
- # histogramnd_lut
- # =====================================
- config.add_extension('chistogramnd_lut',
- sources=['chistogramnd_lut.pyx'],
- include_dirs=histo_inc,
- language='c')
- # =====================================
- # marching cubes
- # =====================================
- mc_src = [os.path.join('marchingcubes', 'mc_lut.cpp'),
- 'marchingcubes.pyx']
- config.add_extension('marchingcubes',
- sources=mc_src,
- include_dirs=['marchingcubes', numpy.get_include()],
- language='c++')
-
- # min/max
- config.add_extension('combo',
- sources=['combo.pyx'],
- include_dirs=['include'],
- language='c')
-
- config.add_extension('colormap',
- sources=["colormap.pyx"],
- language='c',
- include_dirs=['include', numpy.get_include()],
- extra_link_args=['-fopenmp'],
- extra_compile_args=['-fopenmp'])
-
- config.add_extension('interpolate',
- sources=["interpolate.pyx"],
- language='c',
- include_dirs=['include', numpy.get_include()],
- extra_link_args=['-fopenmp'],
- extra_compile_args=['-fopenmp'])
-
- return config
-
-
-if __name__ == "__main__":
- from numpy.distutils.core import setup
-
- setup(configuration=configuration)
diff --git a/silx/math/test/__init__.py b/silx/math/test/__init__.py
deleted file mode 100644
index e9f29f3..0000000
--- a/silx/math/test/__init__.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "04/07/2016"
-
-import unittest
-
-from .test_histogramnd_error import suite as test_histo_error
-from .test_histogramnd_nominal import suite as test_histo_nominal
-from .test_histogramnd_vs_np import suite as test_histo_vs_np
-from .test_HistogramndLut_nominal import suite as test_histolut_nominal
-from ..fit.test import suite as test_fit_suite
-from .test_marchingcubes import suite as test_marchingcubes_suite
-from ..medianfilter.test import suite as test_medianfilter_suite
-from .test_combo import suite as test_combo_suite
-from .test_calibration import suite as test_calibration_suite
-from .test_colormap import suite as test_colormap_suite
-from .test_interpolate import suite as test_interpolate_suite
-from ..fft.test import suite as test_fft_suite
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(test_histo_nominal())
- test_suite.addTest(test_histo_error())
- test_suite.addTest(test_histo_vs_np())
- test_suite.addTest(test_fit_suite())
- test_suite.addTest(test_histolut_nominal())
- test_suite.addTest(test_marchingcubes_suite())
- test_suite.addTest(test_medianfilter_suite())
- test_suite.addTest(test_combo_suite())
- test_suite.addTest(test_calibration_suite())
- test_suite.addTest(test_colormap_suite())
- test_suite.addTest(test_interpolate_suite())
- test_suite.addTest(test_fft_suite())
- return test_suite
diff --git a/silx/math/test/benchmark_combo.py b/silx/math/test/benchmark_combo.py
deleted file mode 100644
index e179f76..0000000
--- a/silx/math/test/benchmark_combo.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2017 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Benchmarks of the combo module"""
-
-from __future__ import division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "17/01/2018"
-
-
-import logging
-import os.path
-import time
-import unittest
-
-import numpy
-
-from silx.test.utils import temp_dir
-from silx.utils.testutils import ParametricTestCase
-
-from silx.math import combo
-
-_logger = logging.getLogger(__name__)
-_logger.setLevel(logging.DEBUG)
-
-
-class BenchmarkMinMax(ParametricTestCase):
- """Benchmark of min max combo"""
-
- DTYPES = ('float32', 'float64',
- 'int8', 'int16', 'int32', 'int64',
- 'uint8', 'uint16', 'uint32', 'uint64')
-
- ARANGE = 'ascent', 'descent', 'random'
-
- EXPONENT = 3, 4, 5, 6, 7
-
- def test_benchmark_min_max(self):
- """Benchmark min_max without min positive.
-
- Compares with:
-
- - numpy.nanmin, numpy.nanmax and
- - numpy.argmin, numpy.argmax
-
- It runs bench for different types, different data size and 3
- data sets: increasing , decreasing and random data.
- """
- durations = {'min/max': [], 'argmin/max': [], 'combo': []}
-
- _logger.info('Benchmark against argmin/argmax and nanmin/nanmax')
-
- for dtype in self.DTYPES:
- for arange in self.ARANGE:
- for exponent in self.EXPONENT:
- size = 10**exponent
- with self.subTest(dtype=dtype, size=size, arange=arange):
- if arange == 'ascent':
- data = numpy.arange(0, size, 1, dtype=dtype)
- elif arange == 'descent':
- data = numpy.arange(size, 0, -1, dtype=dtype)
- else:
- if dtype in ('float32', 'float64'):
- data = numpy.random.random(size)
- else:
- data = numpy.random.randint(10**6, size=size)
- data = numpy.array(data, dtype=dtype)
-
- start = time.time()
- ref_min = numpy.nanmin(data)
- ref_max = numpy.nanmax(data)
- durations['min/max'].append(time.time() - start)
-
- start = time.time()
- ref_argmin = numpy.argmin(data)
- ref_argmax = numpy.argmax(data)
- durations['argmin/max'].append(time.time() - start)
-
- start = time.time()
- result = combo.min_max(data, min_positive=False)
- durations['combo'].append(time.time() - start)
-
- _logger.info(
- '%s-%s-10**%d\tx%.2f argmin/max x%.2f min/max',
- dtype, arange, exponent,
- durations['argmin/max'][-1] / durations['combo'][-1],
- durations['min/max'][-1] / durations['combo'][-1])
-
- self.assertEqual(result.minimum, ref_min)
- self.assertEqual(result.maximum, ref_max)
- self.assertEqual(result.argmin, ref_argmin)
- self.assertEqual(result.argmax, ref_argmax)
-
- self.show_results('min/max', durations, 'combo')
-
- def test_benchmark_min_pos(self):
- """Benchmark min_max wit min positive.
-
- Compares with:
-
- - numpy.nanmin(data[data > 0]); numpy.nanmin(pos); numpy.nanmax(pos)
-
- It runs bench for different types, different data size and 3
- data sets: increasing , decreasing and random data.
- """
- durations = {'min/max': [], 'combo': []}
-
- _logger.info('Benchmark against min, max, positive min')
-
- for dtype in self.DTYPES:
- for arange in self.ARANGE:
- for exponent in self.EXPONENT:
- size = 10**exponent
- with self.subTest(dtype=dtype, size=size, arange=arange):
- if arange == 'ascent':
- data = numpy.arange(0, size, 1, dtype=dtype)
- elif arange == 'descent':
- data = numpy.arange(size, 0, -1, dtype=dtype)
- else:
- if dtype in ('float32', 'float64'):
- data = numpy.random.random(size)
- else:
- data = numpy.random.randint(10**6, size=size)
- data = numpy.array(data, dtype=dtype)
-
- start = time.time()
- ref_min_positive = numpy.nanmin(data[data > 0])
- ref_min = numpy.nanmin(data)
- ref_max = numpy.nanmax(data)
- durations['min/max'].append(time.time() - start)
-
- start = time.time()
- result = combo.min_max(data, min_positive=True)
- durations['combo'].append(time.time() - start)
-
- _logger.info(
- '%s-%s-10**%d\tx%.2f min/minpos/max',
- dtype, arange, exponent,
- durations['min/max'][-1] / durations['combo'][-1])
-
- self.assertEqual(result.min_positive, ref_min_positive)
- self.assertEqual(result.minimum, ref_min)
- self.assertEqual(result.maximum, ref_max)
-
- self.show_results('min/max/min positive', durations, 'combo')
-
- def show_results(self, title, durations, ref_key):
- try:
- from matplotlib import pyplot
- except ImportError:
- _logger.warning('matplotlib not available')
- return
-
- pyplot.title(title)
- pyplot.xlabel('-'.join(self.DTYPES))
- pyplot.ylabel('duration (sec)')
- for label, values in durations.items():
- pyplot.semilogy(values, label=label)
- pyplot.legend()
- pyplot.show()
-
- pyplot.title(title)
- pyplot.xlabel('-'.join(self.DTYPES))
- pyplot.ylabel('Duration ratio')
- ref = numpy.array(durations[ref_key])
- for label, values in durations.items():
- values = numpy.array(values)
- pyplot.plot(values/ref, label=label + ' / ' + ref_key)
- pyplot.legend()
- pyplot.show()
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTests(
- unittest.defaultTestLoader.loadTestsFromTestCase(BenchmarkMinMax))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/histo_benchmarks.py b/silx/math/test/histo_benchmarks.py
deleted file mode 100644
index 7d3216d..0000000
--- a/silx/math/test/histo_benchmarks.py
+++ /dev/null
@@ -1,269 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-histogramnd benchmarks, vs numpy.histogramdd (bin counts and weights).
-"""
-
-import numpy as np
-
-import time
-
-from silx.math import histogramnd
-
-
-def print_times(t0s, t1s, t2s, t3s):
- c_times = t1s - t0s
- np_times = t2s - t1s
- np_w_times = t3s - t2s
-
- time_txt = 'min : {0: <7.3f}; max : {1: <7.3f}; avg : {2: <7.3f}'
-
- print('\tTimes :')
- print('\tC : ' + time_txt.format(c_times.min(),
- c_times.max(),
- c_times.mean()))
- print('\tNP : ' + time_txt.format(np_times.min(),
- np_times.max(),
- np_times.mean()))
- print('\tNP(W) : ' + time_txt.format(np_w_times.min(),
- np_w_times.max(),
- np_w_times.mean()))
-
-
-def commpare_results(txt,
- times,
- result_c,
- result_np,
- result_np_w,
- sample,
- weights,
- raise_ex=False):
-
- if result_np:
- hits_cmp = np.array_equal(result_c[0], result_np[0])
- else:
- hits_cmp = None
-
- if result_np_w and result_c[1] is not None:
- weights_cmp = np.array_equal(result_c[1], result_np_w[0])
- else:
- weights_cmp = None
-
- if((hits_cmp is not None and not hits_cmp) or
- (weights_cmp is not None and not weights_cmp)):
- err_txt = (txt + ' : results arent the same : '
- 'hits : {0}, '
- 'weights : {1}.'
- ''.format('OK' if hits_cmp else 'NOK',
- 'OK' if weights_cmp else 'NOK'))
- print('\t' + err_txt)
- if raise_ex:
- raise ValueError(err_txt)
- return False
-
- result_txt = ' : results OK. c : {0: <7.3f};'.format(times[0])
- if result_np or result_np_w:
- result_txt += (' np : {0: <7.3f}; '
- 'np (weights) {1: <7.3f}.'
- ''.format(times[1], times[2]))
- print('\t' + txt + result_txt)
- return True
-
-
-def benchmark(n_loops,
- sample_shape,
- sample_rng,
- weights_rng,
- histo_range,
- n_bins,
- weight_min,
- weight_max,
- last_bin_closed,
- dtype=np.double,
- do_weights=True,
- do_numpy=True):
-
- int_min = 0
- int_max = 100000
-
- sample = np.random.randint(int_min,
- high=int_max,
- size=sample_shape).astype(np.double)
- sample = (sample_rng[0] +
- (sample - int_min) *
- (sample_rng[1] - sample_rng[0]) /
- (int_max - int_min))
- sample = sample.astype(dtype)
-
- if do_weights:
- weights = np.random.randint(int_min,
- high=int_max,
- size=(ssetup.pyample_shape[0],))
- weights = weights.astype(np.double)
- weights = (weights_rng[0] +
- (weights - int_min) *
- (weights_rng[1] - weights_rng[0]) /
- (int_max - int_min))
- else:
- weights = None
-
- t0s = []
- t1s = []
- t2s = []
- t3s = []
-
- for i in range(n_loops):
- t0s.append(time.time())
- result_c = histogramnd(sample,
- histo_range,
- n_bins,
- weights=weights,
- weight_min=weight_min,
- weight_max=weight_max,
- last_bin_closed=last_bin_closed)
- t1s.append(time.time())
- if do_numpy:
- result_np = np.histogramdd(sample,
- bins=n_bins,
- range=histo_range)
- t2s.append(time.time())
- result_np_w = np.histogramdd(sample,
- bins=n_bins,
- range=histo_range,
- weights=weights)
- t3s.append(time.time())
- else:
- result_np = None
- result_np_w = None
- t2s.append(0)
- t3s.append(0)
-
- commpare_results('Run {0}'.format(i),
- [t1s[-1] - t0s[-1], t2s[-1] - t1s[-1], t3s[-1] - t2s[-1]],
- result_c,
- result_np,
- result_np_w,
- sample,
- weights)
-
- print_times(np.array(t0s), np.array(t1s), np.array(t2s), np.array(t3s))
-
-
-def run_benchmark(dtype=np.double,
- do_weights=True,
- do_numpy=True):
- n_loops = 5
-
- weights_rng = [0., 100.]
- sample_rng = [0., 100.]
-
- weight_min = None
- weight_max = None
- last_bin_closed = True
-
- # ====================================================
- # ====================================================
- # 1D
- # ====================================================
- # ====================================================
-
- print('==========================')
- print(' 1D [{0}]'.format(dtype))
- print('==========================')
- sample_shape = (10**7,)
- histo_range = [[0., 100.]]
- n_bins = 30
-
- benchmark(n_loops,
- sample_shape,
- sample_rng,
- weights_rng,
- histo_range,
- n_bins,
- weight_min,
- weight_max,
- last_bin_closed,
- dtype=dtype,
- do_weights=True,
- do_numpy=do_numpy)
-
- # ====================================================
- # ====================================================
- # 2D
- # ====================================================
- # ====================================================
-
- print('==========================')
- print(' 2D [{0}]'.format(dtype))
- print('==========================')
- sample_shape = (10**7, 2)
- histo_range = [[0., 100.], [0., 100.]]
- n_bins = 30
-
- benchmark(n_loops,
- sample_shape,
- sample_rng,
- weights_rng,
- histo_range,
- n_bins,
- weight_min,
- weight_max,
- last_bin_closed,
- dtype=dtype,
- do_weights=True,
- do_numpy=do_numpy)
-
- # ====================================================
- # ====================================================
- # 3D
- # ====================================================
- # ====================================================
-
- print('==========================')
- print(' 3D [{0}]'.format(dtype))
- print('==========================')
- sample_shape = (10**7, 3)
- histo_range = np.array([[0., 100.], [0., 100.], [0., 100.]])
- n_bins = 30
-
- benchmark(n_loops,
- sample_shape,
- sample_rng,
- weights_rng,
- histo_range,
- n_bins,
- weight_min,
- weight_max,
- last_bin_closed,
- dtype=dtype,
- do_weights=True,
- do_numpy=do_numpy)
-
-if __name__ == '__main__':
- types = (np.double, np.int32, np.float32,)
-
- for t in types:
- run_benchmark(t,
- do_weights=True,
- do_numpy=True)
diff --git a/silx/math/test/test_HistogramndLut_nominal.py b/silx/math/test/test_HistogramndLut_nominal.py
deleted file mode 100644
index 08ca682..0000000
--- a/silx/math/test/test_HistogramndLut_nominal.py
+++ /dev/null
@@ -1,587 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Nominal tests of the HistogramndLut function.
-"""
-
-import unittest
-
-import numpy as np
-
-from silx.math import HistogramndLut
-
-
-def _get_bin_edges(histo_range, n_bins, n_dims):
- edges = []
- for i_dim in range(n_dims):
- edges.append(histo_range[i_dim, 0] +
- np.arange(n_bins[i_dim] + 1) *
- (histo_range[i_dim, 1] - histo_range[i_dim, 0]) /
- n_bins[i_dim])
- return tuple(edges)
-
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-class _TestHistogramndLut_nominal(unittest.TestCase):
- """
- Unit tests of the HistogramndLut class.
- """
-
- ndims = None
-
- def setUp(self):
- ndims = self.ndims
- self.tested_dim = ndims-1
-
- if ndims is None:
- raise ValueError('ndims class member not set.')
-
- sample = np.array([5.5, -3.3,
- 0., -0.5,
- 3.3, 8.8,
- -7.7, 6.0,
- -4.0])
-
- weights = np.array([500.5, -300.3,
- 0.01, -0.5,
- 300.3, 800.8,
- -700.7, 600.6,
- -400.4])
-
- n_elems = len(sample)
-
- if ndims == 1:
- shape = (n_elems,)
- else:
- shape = (n_elems, ndims)
-
- self.sample = np.zeros(shape=shape, dtype=sample.dtype)
- if ndims == 1:
- self.sample = sample
- else:
- self.sample[..., ndims-1] = sample
-
- self.weights = weights
-
- # the tests are performed along one dimension,
- # all the other bins indices along the other dimensions
- # are expected to be 2
- # (e.g : when testing a 2D sample : [0, x] will go into
- # bin [2, y] because of the bin ranges [-2, 2] and n_bins = 4
- # for the first dimension)
- self.other_axes_index = 2
- self.histo_range = np.repeat([[-2., 2.]], ndims, axis=0)
- self.histo_range[ndims-1] = [-4., 6.]
-
- self.n_bins = np.array([4]*ndims)
- self.n_bins[ndims-1] = 5
-
- if ndims == 1:
- def fill_histo(h, v, dim, op=None):
- if op:
- h[:] = op(h[:], v)
- else:
- h[:] = v
- self.fill_histo = fill_histo
- else:
- def fill_histo(h, v, dim, op=None):
- idx = [self.other_axes_index]*len(h.shape)
- idx[dim] = slice(0, None)
- idx = tuple(idx)
- if op:
- h[idx] = op(h[idx], v)
- else:
- h[idx] = v
- self.fill_histo = fill_histo
-
- def test_nominal_bin_edges(self):
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- bin_edges = instance.bins_edges
-
- expected_edges = _get_bin_edges(self.histo_range,
- self.n_bins,
- self.ndims)
-
- for i_edges, edges in enumerate(expected_edges):
- self.assertTrue(np.array_equal(bin_edges[i_edges],
- expected_edges[i_edges]),
- msg='Testing bin_edges for dim {0}'
- ''.format(i_edges+1))
-
- def test_nominal_histo_range(self):
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- histo_range = instance.histo_range
-
- self.assertTrue(np.array_equal(histo_range, self.histo_range))
-
- def test_nominal_last_bin_closed(self):
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- last_bin_closed = instance.last_bin_closed
-
- self.assertEqual(last_bin_closed, False)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins,
- last_bin_closed=True)
-
- last_bin_closed = instance.last_bin_closed
-
- self.assertEqual(last_bin_closed, True)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins,
- last_bin_closed=False)
-
- last_bin_closed = instance.last_bin_closed
-
- self.assertEqual(last_bin_closed, False)
-
- def test_nominal_n_bins_array(self):
-
- test_n_bins = np.arange(self.ndims) + 10
- instance = HistogramndLut(self.sample,
- self.histo_range,
- test_n_bins)
-
- n_bins = instance.n_bins
-
- self.assertTrue(np.array_equal(test_n_bins, n_bins))
-
- def test_nominal_n_bins_scalar(self):
-
- test_n_bins = 10
- expected_n_bins = np.array([test_n_bins] * self.ndims)
- instance = HistogramndLut(self.sample,
- self.histo_range,
- test_n_bins)
-
- n_bins = instance.n_bins
-
- self.assertTrue(np.array_equal(expected_n_bins, n_bins))
-
- def test_nominal_histo_ref(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- instance.accumulate(self.weights)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
- histo_ref = instance.histo(copy=False)
- w_histo_ref = instance.weighted_histo(copy=False)
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
- self.assertTrue(np.array_equal(histo_ref, expected_h))
- self.assertTrue(np.array_equal(w_histo_ref, expected_c))
-
- histo_ref[0, ...] = histo_ref[0, ...] + 10
- w_histo_ref[0, ...] = w_histo_ref[0, ...] + 20
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
- self.assertFalse(np.array_equal(histo_ref, expected_h))
- self.assertFalse(np.array_equal(w_histo_ref, expected_c))
-
- histo_2 = instance.histo()
- w_histo_2 = instance.weighted_histo()
-
- self.assertFalse(np.array_equal(histo_2, expected_h))
- self.assertFalse(np.array_equal(w_histo_2, expected_c))
- self.assertTrue(np.array_equal(histo_2, histo_ref))
- self.assertTrue(np.array_equal(w_histo_2, w_histo_ref))
-
- def test_nominal_accumulate_once(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- instance.accumulate(self.weights)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
- self.assertTrue(np.array_equal(instance.histo(), expected_h))
- self.assertTrue(np.array_equal(instance.weighted_histo(),
- expected_c))
-
- def test_nominal_accumulate_twice(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- # calling accumulate twice
- expected_h *= 2
- expected_c *= 2
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- instance.accumulate(self.weights)
-
- instance.accumulate(self.weights)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
- self.assertTrue(np.array_equal(instance.histo(), expected_h))
- self.assertTrue(np.array_equal(instance.weighted_histo(),
- expected_c))
-
- def test_nominal_apply_lut_once(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- histo, w_histo = instance.apply_lut(self.weights)
-
- self.assertEqual(w_histo.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
- self.assertEqual(instance.histo(), None)
- self.assertEqual(instance.weighted_histo(), None)
-
- def test_nominal_apply_lut_twice(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- # calling apply_lut twice
- expected_h *= 2
- expected_c *= 2
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- histo, w_histo = instance.apply_lut(self.weights)
- histo_2, w_histo_2 = instance.apply_lut(self.weights,
- histo=histo,
- weighted_histo=w_histo)
-
- self.assertEqual(id(histo), id(histo_2))
- self.assertEqual(id(w_histo), id(w_histo_2))
- self.assertEqual(w_histo.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
- self.assertEqual(instance.histo(), None)
- self.assertEqual(instance.weighted_histo(), None)
-
- def test_nominal_accumulate_last_bin_closed(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 2])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 1101.1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins,
- last_bin_closed=True)
-
- instance.accumulate(self.weights)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
-
- def test_nominal_accumulate_weight_min_max(self):
- """
- """
- weight_min = -299.9
- weight_max = 499.9
-
- expected_h_tpl = np.array([0, 1, 1, 1, 0])
- expected_c_tpl = np.array([0., -0.5, 0.01, 300.3, 0.])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- instance.accumulate(self.weights,
- weight_min=weight_min,
- weight_max=weight_max)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
-
- def test_nominal_accumulate_forced_int32(self):
- """
- double weights, int32 weighted_histogram
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700, 0, 0, 300, 500])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins,
- dtype=np.int32)
-
- instance.accumulate(self.weights)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.int32)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
-
- def test_nominal_accumulate_forced_float32(self):
- """
- int32 weights, float32 weighted_histogram
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700., 0., 0., 300., 500.])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.float32)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins,
- dtype=np.float32)
-
- instance.accumulate(self.weights.astype(np.int32))
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.float32)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
-
- def test_nominal_accumulate_int32(self):
- """
- int32 weights
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700, 0, 0, 300, 500])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.int32)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- instance.accumulate(self.weights.astype(np.int32))
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- self.assertEqual(w_histo.dtype, np.int32)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
-
- def test_nominal_accumulate_int32_double(self):
- """
- int32 weights
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700, 0, 0, 300, 500])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.int32)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- instance = HistogramndLut(self.sample,
- self.histo_range,
- self.n_bins)
-
- instance.accumulate(self.weights.astype(np.int32))
- instance.accumulate(self.weights)
-
- histo = instance.histo()
- w_histo = instance.weighted_histo()
-
- expected_h *= 2
- expected_c *= 2
-
- self.assertEqual(w_histo.dtype, np.int32)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(w_histo, expected_c))
-
- def testNoneNativeTypes(self):
- type = self.sample.dtype.newbyteorder("B")
- sampleB = self.sample.astype(type)
-
- type = self.sample.dtype.newbyteorder("L")
- sampleL = self.sample.astype(type)
-
- histo_inst = HistogramndLut(sampleB,
- self.histo_range,
- self.n_bins)
-
- histo_inst = HistogramndLut(sampleL,
- self.histo_range,
- self.n_bins)
-
-
-class TestHistogramndLut_nominal_1d(_TestHistogramndLut_nominal):
- ndims = 1
-
-
-class TestHistogramndLut_nominal_2d(_TestHistogramndLut_nominal):
- ndims = 2
-
-
-class TestHistogramndLut_nominal_3d(_TestHistogramndLut_nominal):
- ndims = 3
-
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-test_cases = (TestHistogramndLut_nominal_1d,
- TestHistogramndLut_nominal_2d,
- TestHistogramndLut_nominal_3d,)
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/test_calibration.py b/silx/math/test/test_calibration.py
deleted file mode 100644
index 5a0c20e..0000000
--- a/silx/math/test/test_calibration.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Tests of the calibration module"""
-
-from __future__ import division
-
-__authors__ = ["P. Knobel"]
-__license__ = "MIT"
-__date__ = "14/05/2018"
-
-
-import unittest
-
-import numpy
-
-from silx.math.calibration import NoCalibration, LinearCalibration, \
- ArrayCalibration, FunctionCalibration
-
-
-X = numpy.array([3.14, 2.73, 1337])
-
-
-class TestNoCalibration(unittest.TestCase):
- def setUp(self):
- self.calib = NoCalibration()
-
- def testIsAffine(self):
- self.assertTrue(self.calib.is_affine())
-
- def testSlope(self):
- self.assertEqual(self.calib.get_slope(), 1.)
-
- def testYIntercept(self):
- self.assertEqual(self.calib(0.),
- 0.)
-
- def testCall(self):
- self.assertTrue(numpy.array_equal(self.calib(X), X))
-
-
-class TestLinearCalibration(unittest.TestCase):
- def setUp(self):
- self.y_intercept = 1.5
- self.slope = 2.5
- self.calib = LinearCalibration(y_intercept=self.y_intercept,
- slope=self.slope)
-
- def testIsAffine(self):
- self.assertTrue(self.calib.is_affine())
-
- def testSlope(self):
- self.assertEqual(self.calib.get_slope(), self.slope)
-
- def testYIntercept(self):
- self.assertEqual(self.calib(0.),
- self.y_intercept)
-
- def testCall(self):
- self.assertTrue(numpy.array_equal(self.calib(X),
- self.y_intercept + self.slope * X))
-
-
-class TestArrayCalibration(unittest.TestCase):
- def setUp(self):
- self.arr = numpy.array([45.2, 25.3, 666., -8.])
- self.calib = ArrayCalibration(self.arr)
- self.affine_calib = ArrayCalibration([0.1, 0.2, 0.3])
-
- def testIsAffine(self):
- self.assertFalse(self.calib.is_affine())
- self.assertTrue(self.affine_calib.is_affine())
-
- def testSlope(self):
- with self.assertRaises(AttributeError):
- self.calib.get_slope()
- self.assertEqual(self.affine_calib.get_slope(),
- 0.1)
-
- def testYIntercept(self):
- self.assertEqual(self.calib(0),
- self.arr[0])
-
- def testCall(self):
- with self.assertRaises(ValueError):
- # X is an array with a different shape
- self.calib(X)
-
- with self.assertRaises(ValueError):
- # floats are not valid indices
- self.calib(3.14)
-
- self.assertTrue(
- numpy.array_equal(self.calib([1, 2, 3, 4]),
- self.arr))
-
- for idx, value in enumerate(self.arr):
- self.assertEqual(self.calib(idx), value)
-
-
-class TestFunctionCalibration(unittest.TestCase):
- def setUp(self):
- self.non_affine_fun = numpy.sin
- self.non_affine_calib = FunctionCalibration(self.non_affine_fun)
-
- self.affine_fun = lambda x: 52. * x + 0.01
- self.affine_calib = FunctionCalibration(self.affine_fun,
- is_affine=True)
-
- def testIsAffine(self):
- self.assertFalse(self.non_affine_calib.is_affine())
- self.assertTrue(self.affine_calib.is_affine())
-
- def testSlope(self):
- with self.assertRaises(AttributeError):
- self.non_affine_calib.get_slope()
- self.assertAlmostEqual(self.affine_calib.get_slope(),
- 52.)
-
- def testCall(self):
- for x in X:
- self.assertAlmostEqual(self.non_affine_calib(x),
- self.non_affine_fun(x))
- self.assertAlmostEqual(self.affine_calib(x),
- self.affine_fun(x))
-
-
-def suite():
- test_suite = unittest.TestSuite()
- loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
- test_suite.addTest(loadTests(TestNoCalibration))
- test_suite.addTest(loadTests(TestArrayCalibration))
- test_suite.addTest(loadTests(TestLinearCalibration))
- test_suite.addTest(loadTests(TestFunctionCalibration))
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/test_colormap.py b/silx/math/test/test_colormap.py
deleted file mode 100644
index 4e80710..0000000
--- a/silx/math/test/test_colormap.py
+++ /dev/null
@@ -1,266 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2018-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Test for colormap mapping implementation"""
-
-from __future__ import division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "16/05/2018"
-
-
-import logging
-import sys
-import unittest
-
-import numpy
-
-from silx.utils.testutils import ParametricTestCase
-from silx.math import colormap
-
-
-_logger = logging.getLogger(__name__)
-
-
-class TestNormalization(ParametricTestCase):
- """Test silx.math.colormap.Normalization sub classes"""
-
- def _testCodec(self, normalization, rtol=1e-5):
- """Test apply/revert for normalizations"""
- test_data = (numpy.arange(1, 10, dtype=numpy.int32),
- numpy.linspace(1., 100., 1000, dtype=numpy.float32),
- numpy.linspace(-1., 1., 100, dtype=numpy.float32),
- 1.,
- 1)
-
- for index in range(len(test_data)):
- with self.subTest(normalization=normalization, data_index=index):
- data = test_data[index]
- normalized = normalization.apply(data, 1., 100.)
- result = normalization.revert(normalized, 1., 100.)
-
- self.assertTrue(numpy.array_equal(
- numpy.isnan(normalized), numpy.isnan(result)))
-
- if isinstance(data, numpy.ndarray):
- notNaN = numpy.logical_not(numpy.isnan(result))
- data = data[notNaN]
- result = result[notNaN]
- self.assertTrue(numpy.allclose(data, result, rtol=rtol))
-
- def testLinearNormalization(self):
- """Test for LinearNormalization"""
- normalization = colormap.LinearNormalization()
- self._testCodec(normalization)
-
- def testLogarithmicNormalization(self):
- """Test for LogarithmicNormalization"""
- normalization = colormap.LogarithmicNormalization()
- # relative tolerance is higher because of the log approximation
- self._testCodec(normalization, rtol=1e-3)
-
- # Specific extra tests
- self.assertTrue(numpy.isnan(normalization.apply(-1., 1., 100.)))
- self.assertTrue(numpy.isnan(normalization.apply(numpy.nan, 1., 100.)))
- self.assertEqual(normalization.apply(numpy.inf, 1., 100.), numpy.inf)
- self.assertEqual(normalization.apply(0, 1., 100.), - numpy.inf)
-
- def testArcsinhNormalization(self):
- """Test for ArcsinhNormalization"""
- self._testCodec(colormap.ArcsinhNormalization())
-
- def testSqrtNormalization(self):
- """Test for SqrtNormalization"""
- normalization = colormap.SqrtNormalization()
- self._testCodec(normalization)
-
- # Specific extra tests
- self.assertTrue(numpy.isnan(normalization.apply(-1., 0., 100.)))
- self.assertTrue(numpy.isnan(normalization.apply(numpy.nan, 0., 100.)))
- self.assertEqual(normalization.apply(numpy.inf, 0., 100.), numpy.inf)
- self.assertEqual(normalization.apply(0, 0., 100.), 0.)
-
-
-class TestColormap(ParametricTestCase):
- """Test silx.math.colormap.cmap"""
-
- NORMALIZATIONS = (
- 'linear',
- 'log',
- 'arcsinh',
- 'sqrt',
- colormap.LinearNormalization(),
- colormap.LogarithmicNormalization(),
- colormap.PowerNormalization(2.),
- colormap.PowerNormalization(0.5))
-
- @staticmethod
- def ref_colormap(data, colors, vmin, vmax, normalization, nan_color):
- """Reference implementation of colormap
-
- :param numpy.ndarray data: Data to convert
- :param numpy.ndarray colors: Color look-up-table
- :param float vmin: Lower bound of the colormap range
- :param float vmax: Upper bound of the colormap range
- :param str normalization: Normalization to use
- :param Union[numpy.ndarray, None] nan_color: Color to use for NaN
- """
- norm_functions = {'linear': lambda v: v,
- 'log': numpy.log10,
- 'arcsinh': numpy.arcsinh,
- 'sqrt': numpy.sqrt}
-
- if isinstance(normalization, str):
- norm_function = norm_functions[normalization]
- else:
- def norm_function(value):
- return normalization.apply(value, vmin, vmax)
-
- with numpy.errstate(divide='ignore', invalid='ignore'):
- # Ignore divide by zero and invalid value encountered in log10, sqrt
- norm_data, vmin, vmax = map(norm_function, (data, vmin, vmax))
-
- if normalization == 'arcsinh' and sys.platform == 'win32':
- # There is a difference of behavior of numpy.arcsinh
- # between Windows and other OS for results of infinite values
- # This makes Windows behaves as Linux and MacOS
- norm_data[data == numpy.inf] = numpy.inf
- norm_data[data == -numpy.inf] = -numpy.inf
-
- nb_colors = len(colors)
- scale = nb_colors / (vmax - vmin)
-
- # Substraction must be done in float to avoid overflow with uint
- indices = numpy.clip(scale * (norm_data - float(vmin)),
- 0, nb_colors - 1)
- indices[numpy.isnan(indices)] = nb_colors # Use an extra index for NaN
- indices = indices.astype('uint')
-
- # Add NaN color to array
- if nan_color is None:
- nan_color = (0,) * colors.shape[-1]
- colors = numpy.append(colors, numpy.atleast_2d(nan_color), axis=0)
-
- return colors[indices]
-
- def _test(self, data, colors, vmin, vmax, normalization, nan_color):
- """Run test of colormap against alternative implementation
-
- :param numpy.ndarray data: Data to convert
- :param numpy.ndarray colors: Color look-up-table
- :param float vmin: Lower bound of the colormap range
- :param float vmax: Upper bound of the colormap range
- :param str normalization: Normalization to use
- :param Union[numpy.ndarray, None] nan_color: Color to use for NaN
- """
- image = colormap.cmap(
- data, colors, vmin, vmax, normalization, nan_color)
-
- ref_image = self.ref_colormap(
- data, colors, vmin, vmax, normalization, nan_color)
-
- self.assertTrue(numpy.allclose(ref_image, image))
- self.assertEqual(image.dtype, colors.dtype)
- self.assertEqual(image.shape, data.shape + (colors.shape[-1],))
-
- def test(self):
- """Test all dtypes with finite data
-
- Test all supported types and endianness
- """
- colors = numpy.zeros((256, 4), dtype=numpy.uint8)
- colors[:, 0] = numpy.arange(len(colors))
- colors[:, 3] = 255
-
- # Generates (u)int and floats types
- dtypes = [e + k + i for e in '<>' for k in 'uif' for i in '1248'
- if k != 'f' or i != '1']
- dtypes.append(numpy.dtype(numpy.longdouble).name) # Add long double
-
- for normalization in self.NORMALIZATIONS:
- for dtype in dtypes:
- with self.subTest(dtype=dtype, normalization=normalization):
- _logger.info('normalization: %s, dtype: %s',
- normalization, dtype)
- data = numpy.arange(-5, 15, dtype=dtype).reshape(4, 5)
-
- self._test(data, colors, 1, 10, normalization, None)
-
- def test_not_finite(self):
- """Test float data with not finite values"""
- colors = numpy.zeros((256, 4), dtype=numpy.uint8)
- colors[:, 0] = numpy.arange(len(colors))
- colors[:, 3] = 255
-
- test_data = { # message: data
- 'no finite values': (float('inf'), float('-inf'), float('nan')),
- 'only NaN': (float('nan'), float('nan'), float('nan')),
- 'mix finite/not finite': (float('inf'), float('-inf'), 1., float('nan')),
- }
-
- for normalization in self.NORMALIZATIONS:
- for msg, data in test_data.items():
- with self.subTest(msg, normalization=normalization):
- _logger.info('normalization: %s, %s', normalization, msg)
- data = numpy.array(data, dtype=numpy.float64)
- self._test(data, colors, 1, 10, normalization, (0, 0, 0, 0))
-
- def test_errors(self):
- """Test raising exception for bad vmin, vmax, normalization parameters
- """
- colors = numpy.zeros((256, 4), dtype=numpy.uint8)
- colors[:, 0] = numpy.arange(len(colors))
- colors[:, 3] = 255
-
- data = numpy.arange(10, dtype=numpy.float64)
-
- test_params = [ # (vmin, vmax, normalization)
- (-1., 2., 'log'),
- (0., 1., 'log'),
- (1., 0., 'log'),
- (-1., 1., 'sqrt'),
- (1., -1., 'sqrt'),
- ]
-
- for vmin, vmax, normalization in test_params:
- with self.subTest(
- vmin=vmin, vmax=vmax, normalization=normalization):
- _logger.info('normalization: %s, range: [%f, %f]',
- normalization, vmin, vmax)
- with self.assertRaises(ValueError):
- self._test(data, colors, vmin, vmax, normalization, None)
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestColormap))
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestNormalization))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/math/test/test_combo.py b/silx/math/test/test_combo.py
deleted file mode 100644
index 1335763..0000000
--- a/silx/math/test/test_combo.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Tests of the combo module"""
-
-from __future__ import division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "17/01/2018"
-
-
-import unittest
-
-import numpy
-
-from silx.utils.testutils import ParametricTestCase
-
-from silx.math.combo import min_max
-
-
-class TestMinMax(ParametricTestCase):
- """Tests of min max combo"""
-
- FLOATING_DTYPES = 'float32', 'float64'
- if hasattr(numpy, "float128"):
- FLOATING_DTYPES += ('float128',)
- SIGNED_INT_DTYPES = 'int8', 'int16', 'int32', 'int64'
- UNSIGNED_INT_DTYPES = 'uint8', 'uint16', 'uint32', 'uint64'
- DTYPES = FLOATING_DTYPES + SIGNED_INT_DTYPES + UNSIGNED_INT_DTYPES
-
- def _numpy_min_max(self, data, min_positive=False, finite=False):
- """Reference numpy implementation of min_max
-
- :param numpy.ndarray data: Data set to use for test
- :param bool min_positive: True to test with positive min
- :param bool finite: True to only test finite values
- """
- data = numpy.array(data, copy=False)
- if data.size == 0:
- raise ValueError('Zero-sized array')
-
- minimum = None
- argmin = None
- maximum = None
- argmax = None
- min_pos = None
- argmin_pos = None
-
- if finite:
- filtered_data = data[numpy.isfinite(data)]
- else:
- filtered_data = data
-
- if filtered_data.size > 0:
- if numpy.all(numpy.isnan(filtered_data)):
- minimum = numpy.nan
- argmin = 0
- maximum = numpy.nan
- argmax = 0
- else:
- minimum = numpy.nanmin(filtered_data)
- # nanargmin equivalent
- argmin = numpy.where(data == minimum)[0][0]
- maximum = numpy.nanmax(filtered_data)
- # nanargmax equivalent
- argmax = numpy.where(data == maximum)[0][0]
-
- if min_positive:
- with numpy.errstate(invalid='ignore'):
- # Ignore invalid value encountered in greater
- pos_data = filtered_data[filtered_data > 0]
- if pos_data.size > 0:
- min_pos = numpy.min(pos_data)
- argmin_pos = numpy.where(data == min_pos)[0][0]
-
- return minimum, min_pos, maximum, argmin, argmin_pos, argmax
-
- def _test_min_max(self, data, min_positive, finite=False):
- """Compare min_max with numpy for the given dataset
-
- :param numpy.ndarray data: Data set to use for test
- :param bool min_positive: True to test with positive min
- :param bool finite: True to only test finite values
- """
- minimum, min_pos, maximum, argmin, argmin_pos, argmax = \
- self._numpy_min_max(data, min_positive, finite)
-
- result = min_max(data, min_positive, finite)
-
- self.assertSimilar(minimum, result.minimum)
- self.assertSimilar(min_pos, result.min_positive)
- self.assertSimilar(maximum, result.maximum)
- self.assertSimilar(argmin, result.argmin)
- self.assertSimilar(argmin_pos, result.argmin_positive)
- self.assertSimilar(argmax, result.argmax)
-
- def assertSimilar(self, a, b):
- """Assert that a and b are both None or NaN or that a == b."""
- self.assertTrue((a is None and b is None) or
- (numpy.isnan(a) and numpy.isnan(b)) or
- a == b)
-
- def test_different_datasets(self):
- """Test min_max with different numpy.arange datasets."""
- size = 1000
-
- for dtype in self.DTYPES:
-
- tests = {
- '0 to N': (0, 1),
- 'N-1 to 0': (size - 1, -1)}
- if dtype not in self.UNSIGNED_INT_DTYPES:
- tests['N/2 to -N/2'] = size // 2, -1
- tests['0 to -N'] = 0, -1
-
- for name, (start, step) in tests.items():
- for min_positive in (True, False):
- with self.subTest(dtype=dtype,
- min_positive=min_positive,
- data=name):
- data = numpy.arange(
- start, start + step * size, step, dtype=dtype)
-
- self._test_min_max(data, min_positive)
-
- def test_nodata(self):
- """Test min_max with None and empty array"""
- for dtype in self.DTYPES:
- with self.subTest(dtype=dtype):
- with self.assertRaises(TypeError):
- min_max(None)
-
- data = numpy.array((), dtype=dtype)
- with self.assertRaises(ValueError):
- min_max(data)
-
- NAN_TEST_DATA = [
- (float('nan'), float('nan')), # All NaNs
- (float('nan'), 1.0), # NaN first and positive
- (float('nan'), -1.0), # NaN first and negative
- (1.0, 2.0, float('nan')), # NaN last and positive
- (-1.0, -2.0, float('nan')), # NaN last and negative
- (1.0, float('nan'), -1.0), # Some NaN
- ]
-
- def test_nandata(self):
- """Test min_max with NaN in data"""
- for dtype in self.FLOATING_DTYPES:
- for data in self.NAN_TEST_DATA:
- with self.subTest(dtype=dtype, data=data):
- data = numpy.array(data, dtype=dtype)
- self._test_min_max(data, min_positive=True)
-
- INF_TEST_DATA = [
- [float('inf')] * 3, # All +inf
- [float('-inf')] * 3, # All -inf
- (float('inf'), float('-inf')), # + and - inf
- (float('inf'), float('-inf'), float('nan')), # +/-inf, nan last
- (float('nan'), float('-inf'), float('inf')), # +/-inf, nan first
- (float('inf'), float('nan'), float('-inf')), # +/-inf, nan center
- ]
-
- def test_infdata(self):
- """Test min_max with inf."""
- for dtype in self.FLOATING_DTYPES:
- for data in self.INF_TEST_DATA:
- with self.subTest(dtype=dtype, data=data):
- data = numpy.array(data, dtype=dtype)
- self._test_min_max(data, min_positive=True)
-
- def test_finite(self):
- """Test min_max with finite=True"""
- tests = [
- (-1., 2., 0.), # Basic test
- (float('nan'), float('inf'), float('-inf')), # NaN + Inf
- (float('nan'), float('inf'), -2, float('-inf')), # NaN + Inf + 1 value
- (float('inf'), -3, -2), # values + inf
- ]
- tests += self.INF_TEST_DATA
- tests += self.NAN_TEST_DATA
-
- for dtype in self.FLOATING_DTYPES:
- for data in tests:
- with self.subTest(dtype=dtype, data=data):
- data = numpy.array(data, dtype=dtype)
- self._test_min_max(data, min_positive=True, finite=True)
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTests(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestMinMax))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/test_histogramnd_error.py b/silx/math/test/test_histogramnd_error.py
deleted file mode 100644
index a8c66a0..0000000
--- a/silx/math/test/test_histogramnd_error.py
+++ /dev/null
@@ -1,535 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-
-__authors__ = ["D. Naudet"]
-__license__ = "MIT"
-__date__ = "01/02/2016"
-
-"""
-Tests of the histogramnd function, error cases.
-"""
-import sys
-import platform
-import unittest
-
-import numpy as np
-
-from silx.math.chistogramnd import chistogramnd as histogramnd
-from silx.math import Histogramnd
-
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-class _Test_chistogramnd_errors(unittest.TestCase):
- """
- Unit tests of the chistogramnd error cases.
- """
- def setUp(self):
- raise NotImplementedError('')
-
- def test_weights_shape(self):
- """
- """
-
- for err_w_shape in self.err_weights_shapes:
- test_msg = ('Testing invalid weights shape : {0}'
- ''.format(err_w_shape))
-
- err_weights = np.random.randint(0,
- high=10,
- size=err_w_shape)
- err_weights = err_weights.astype(np.double)
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=err_weights)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str,
- '<weights> must be an array whose length '
- 'is equal to the number of samples.')
-
- def test_histo_range_shape(self):
- """
- """
- n_dims = 1 if len(self.s_shape) == 1 else self.s_shape[1]
- expected_txt_tpl = ('<histo_range> error : expected {n_dims} sets '
- 'of lower and upper bin edges, '
- 'got the following instead : {histo_range}. '
- '(provided <sample> contains '
- '{n_dims}D values)')
-
- for err_histo_range in self.err_histo_range_shapes:
- test_msg = ('Testing invalid histo_range shape : {0}'
- ''.format(err_histo_range))
-
- expected_txt = expected_txt_tpl.format(histo_range=err_histo_range,
- n_dims=n_dims)
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- err_histo_range,
- self.n_bins,
- weights=self.weights)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_nbins_shape(self):
- """
- """
-
- expected_txt = ('n_bins must be either a scalar (same number '
- 'of bins for all dimensions) or '
- 'an array (number of bins for each '
- 'dimension).')
-
- for err_n_bins in self.err_n_bins_shapes:
- test_msg = ('Testing invalid n_bins shape : {0}'
- ''.format(err_n_bins))
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- err_n_bins,
- weights=self.weights)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_nbins_values(self):
- """
- """
- expected_txt = ('<n_bins> : only positive values allowed.')
-
- for err_n_bins in self.err_n_bins_values:
- test_msg = ('Testing invalid n_bins value : {0}'
- ''.format(err_n_bins))
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- err_n_bins,
- weights=self.weights)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_histo_shape(self):
- """
- """
- for err_h_shape in self.err_histo_shapes:
-
- # windows & python 2.7 : numpy shapes are long values
- if platform.system() == 'Windows':
- version = (sys.version_info.major, sys.version_info.minor)
- if version <= (2, 7):
- err_h_shape = tuple([long(val) for val in err_h_shape])
-
- test_msg = ('Testing invalid histo shape : {0}'
- ''.format(err_h_shape))
-
- expected_txt = ('Provided <histo> array doesn\'t have '
- 'a shape compatible with <n_bins> '
- ': should be {0} instead of {1}.'
- ''.format(self.h_shape, err_h_shape))
-
- histo = np.zeros(shape=err_h_shape, dtype=np.uint32)
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- histo=histo)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_histo_dtype(self):
- """
- """
- for err_h_dtype in self.err_histo_dtypes:
- test_msg = ('Testing invalid histo dtype : {0}'
- ''.format(err_h_dtype))
-
- histo = np.zeros(shape=self.h_shape, dtype=err_h_dtype)
-
- expected_txt = ('Provided <histo> array doesn\'t have '
- 'the expected type '
- ': should be {0} instead of {1}.'
- ''.format(np.uint32, histo.dtype))
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- histo=histo)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_weighted_histo_shape(self):
- """
- """
- # using the same values as histo
- for err_h_shape in self.err_histo_shapes:
-
- # windows & python 2.7 : numpy shapes are long values
- if platform.system() == 'Windows':
- version = (sys.version_info.major, sys.version_info.minor)
- if version <= (2, 7):
- err_h_shape = tuple([long(val) for val in err_h_shape])
-
- test_msg = ('Testing invalid weighted_histo shape : {0}'
- ''.format(err_h_shape))
-
- expected_txt = ('Provided <weighted_histo> array doesn\'t have '
- 'a shape compatible with <n_bins> '
- ': should be {0} instead of {1}.'
- ''.format(self.h_shape, err_h_shape))
-
- cumul = np.zeros(shape=err_h_shape, dtype=np.double)
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- weighted_histo=cumul)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_cumul_dtype(self):
- """
- """
- # using the same values as histo
- for err_h_dtype in self.err_histo_dtypes:
- test_msg = ('Testing invalid weighted_histo dtype : {0}'
- ''.format(err_h_dtype))
-
- cumul = np.zeros(shape=self.h_shape, dtype=err_h_dtype)
-
- expected_txt = ('Provided <weighted_histo> array doesn\'t have '
- 'the expected type '
- ': should be {0} or {1} instead of {2}.'
- ''.format(np.float64, np.float32, cumul.dtype))
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- weighted_histo=cumul)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_wh_histo_dtype(self):
- """
- """
- # using the same values as histo
- for err_h_dtype in self.err_histo_dtypes:
- test_msg = ('Testing invalid wh_dtype dtype : {0}'
- ''.format(err_h_dtype))
-
- expected_txt = ('<wh_dtype> type not supported : {0}.'
- ''.format(err_h_dtype))
-
- ex_str = None
- try:
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- wh_dtype=err_h_dtype)[0:2]
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_unmanaged_dtypes(self):
- """
- """
- for err_unmanaged_dtype in self.err_unmanaged_dtypes:
- test_msg = ('Testing unmanaged dtypes : {0}'
- ''.format(err_unmanaged_dtype))
-
- sample = self.sample.astype(err_unmanaged_dtype[0])
- weights = self.weights.astype(err_unmanaged_dtype[1])
-
- expected_txt = ('Case not supported - sample:{0} '
- 'and weights:{1}.'
- ''.format(sample.dtype,
- weights.dtype))
-
- ex_str = None
- try:
- histogramnd(sample,
- self.histo_range,
- self.n_bins,
- weights=weights)
- except TypeError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str, msg=test_msg)
- self.assertEqual(ex_str, expected_txt, msg=test_msg)
-
- def test_uncontiguous_histo(self):
- """
- """
- # non contiguous array
- shape = np.array(self.n_bins, ndmin=1)
- shape[0] *= 2
- histo_tmp = np.zeros(shape)
- histo = histo_tmp[::2, ...]
-
- expected_txt = ('<histo> must be a C_CONTIGUOUS numpy array.')
-
- ex_str = None
- try:
- histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- histo=histo)
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str)
- self.assertEqual(ex_str, expected_txt)
-
- def test_uncontiguous_weighted_histo(self):
- """
- """
- # non contiguous array
- shape = np.array(self.n_bins, ndmin=1)
- shape[0] *= 2
- cumul_tmp = np.zeros(shape)
- cumul = cumul_tmp[::2, ...]
-
- expected_txt = ('<weighted_histo> must be a C_CONTIGUOUS numpy array.')
-
- ex_str = None
- try:
- histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- weighted_histo=cumul)
- except ValueError as ex:
- ex_str = str(ex)
-
- self.assertIsNotNone(ex_str)
- self.assertEqual(ex_str, expected_txt)
-
-
-class Test_chistogramnd_1D_errors(_Test_chistogramnd_errors):
- """
- Unit tests of the 1D histogramnd error cases.
- """
-
- def setUp(self):
- # nominal values
- self.n_elements = 1000
- self.s_shape = (self.n_elements,)
- self.w_shape = (self.n_elements,)
-
- self.histo_range = [0., 100.]
- self.n_bins = 10
-
- self.h_shape = (self.n_bins,)
-
- self.sample = np.random.randint(0,
- high=10,
- size=self.s_shape)
- self.sample = self.sample.astype(np.double)
-
- self.weights = np.random.randint(0,
- high=10,
- size=self.w_shape)
- self.weights = self.weights.astype(np.double)
-
- self.err_weights_shapes = ((self.n_elements+1,),
- (self.n_elements-1,),
- (self.n_elements-1, 3))
- self.err_histo_range_shapes = ([0.],
- [0., 1., 2.],
- [[0.], [1.]])
- self.err_n_bins_shapes = ([10, 2],
- [[10], [2]])
- self.err_n_bins_values = (0,
- [-10],
- None)
- self.err_histo_shapes = ((self.n_bins+1,),
- (self.n_bins-1,),
- (self.n_bins, self.n_bins))
- # these are used for testing the histo parameter as well
- # as the weighted_histo parameter.
- self.err_histo_dtypes = (np.uint16,
- np.float16)
-
- self.err_unmanaged_dtypes = ((np.double, np.uint16),
- (np.uint16, np.double),
- (np.uint16, np.uint16))
-
-class Test_chistogramnd_ND_range(unittest.TestCase):
- """
-
- """
-
- def test_invalid_histo_range(self):
- data = np.random.random((60, 60))
- nbins = 10
-
- with self.assertRaises(ValueError):
- histo_range = data.min(), np.inf
-
- Histogramnd(sample=data.ravel(),
- histo_range=histo_range,
- n_bins=nbins)
-
- histo_range = data.min(), np.nan
-
- Histogramnd(sample=data.ravel(),
- histo_range=histo_range,
- n_bins=nbins)
-
-
-class Test_chistogramnd_ND_errors(_Test_chistogramnd_errors):
- """
- Unit tests of the 3D histogramnd error cases.
- """
-
- def setUp(self):
- # nominal values
- self.n_elements = 1000
- self.s_shape = (self.n_elements, 3)
- self.w_shape = (self.n_elements,)
-
- self.histo_range = [[0., 100.], [0., 100.], [0., 100.]]
- self.n_bins = (10, 20, 30)
-
- self.h_shape = self.n_bins
-
- self.sample = np.random.randint(0,
- high=10,
- size=self.s_shape)
- self.sample = self.sample.astype(np.double)
-
- self.weights = np.random.randint(0,
- high=10,
- size=self.w_shape)
- self.weights = self.weights.astype(np.double)
-
- self.err_weights_shapes = ((self.n_elements+1,),
- (self.n_elements-1,),
- (self.n_elements-1, 3))
- self.err_histo_range_shapes = ([0.],
- [0., 1.],
- [[0., 10.], [0., 10.]],
- [0., 10., 0, 10., 0, 10.])
- self.err_n_bins_shapes = ([10, 2],
- [[10], [20], [30]])
- self.err_n_bins_values = (0,
- [-10],
- [10, 20, -4],
- None,
- [10, None, 30])
- self.err_histo_shapes = ((self.n_bins[0]+1,
- self.n_bins[1],
- self.n_bins[2]),
- (self.n_bins[0],
- self.n_bins[1],
- self.n_bins[2]-1),
- (self.n_bins[0],
- self.n_bins[1]),
- (self.n_bins[1],
- self.n_bins[0],
- self.n_bins[2]),
- (self.n_bins[0],
- self.n_bins[1],
- self.n_bins[2],
- 10)
- )
- # these are used for testing the histo parameter as well
- # as the weighted_histo parameter.
- self.err_histo_dtypes = (np.uint16,
- np.float16)
-
- self.err_unmanaged_dtypes = ((np.double, np.uint16),
- (np.uint16, np.double),
- (np.uint16, np.uint16))
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-test_cases = (Test_chistogramnd_1D_errors,
- Test_chistogramnd_ND_errors,
- Test_chistogramnd_ND_range)
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/test_histogramnd_nominal.py b/silx/math/test/test_histogramnd_nominal.py
deleted file mode 100644
index 92febdd..0000000
--- a/silx/math/test/test_histogramnd_nominal.py
+++ /dev/null
@@ -1,949 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016-2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Nominal tests of the histogramnd function.
-"""
-
-import unittest
-
-import numpy as np
-
-from silx.math.chistogramnd import chistogramnd as histogramnd
-from silx.math import Histogramnd
-
-
-def _get_bin_edges(histo_range, n_bins, n_dims):
- edges = []
- for i_dim in range(n_dims):
- edges.append(histo_range[i_dim, 0] +
- np.arange(n_bins[i_dim] + 1) *
- (histo_range[i_dim, 1] - histo_range[i_dim, 0]) /
- n_bins[i_dim])
- return tuple(edges)
-
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-class _Test_chistogramnd_nominal(unittest.TestCase):
- """
- Unit tests of the histogramnd function.
- """
-
- ndims = None
-
- def setUp(self):
- ndims = self.ndims
- self.tested_dim = ndims-1
-
- if ndims is None:
- raise ValueError('ndims class member not set.')
-
- sample = np.array([5.5, -3.3,
- 0., -0.5,
- 3.3, 8.8,
- -7.7, 6.0,
- -4.0])
-
- weights = np.array([500.5, -300.3,
- 0.01, -0.5,
- 300.3, 800.8,
- -700.7, 600.6,
- -400.4])
-
- n_elems = len(sample)
-
- if ndims == 1:
- shape = (n_elems,)
- else:
- shape = (n_elems, ndims)
-
- self.sample = np.zeros(shape=shape, dtype=sample.dtype)
- if ndims == 1:
- self.sample = sample
- else:
- self.sample[..., ndims-1] = sample
-
- self.weights = weights
-
- # the tests are performed along one dimension,
- # all the other bins indices along the other dimensions
- # are expected to be 2
- # (e.g : when testing a 2D sample : [0, x] will go into
- # bin [2, y] because of the bin ranges [-2, 2] and n_bins = 4
- # for the first dimension)
- self.other_axes_index = 2
- self.histo_range = np.repeat([[-2., 2.]], ndims, axis=0)
- self.histo_range[ndims-1] = [-4., 6.]
-
- self.n_bins = np.array([4]*ndims)
- self.n_bins[ndims-1] = 5
-
- if ndims == 1:
- def fill_histo(h, v, dim, op=None):
- if op:
- h[:] = op(h[:], v)
- else:
- h[:] = v
- self.fill_histo = fill_histo
- else:
- def fill_histo(h, v, dim, op=None):
- idx = [self.other_axes_index]*len(h.shape)
- idx[dim] = slice(0, None)
- idx = tuple(idx)
- if op:
- h[idx] = op(h[idx], v)
- else:
- h[idx] = v
- self.fill_histo = fill_histo
-
- def test_nominal(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul, bin_edges = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- expected_edges = _get_bin_edges(self.histo_range,
- self.n_bins,
- self.ndims)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- for i_edges, edges in enumerate(expected_edges):
- self.assertTrue(np.array_equal(bin_edges[i_edges],
- expected_edges[i_edges]),
- msg='Testing bin_edges for dim {0}'
- ''.format(i_edges+1))
-
- def test_nominal_wh_dtype(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.float32)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul, bin_edges = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- wh_dtype=np.float32)
-
- self.assertEqual(cumul.dtype, np.float32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.allclose(cumul, expected_c))
-
- def test_nominal_uncontiguous_sample(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- shape = list(self.sample.shape)
- shape[0] *= 2
- sample = np.zeros(shape, dtype=self.sample.dtype)
- uncontig_sample = sample[::2, ...]
- uncontig_sample[:] = self.sample
-
- self.assertFalse(uncontig_sample.flags['C_CONTIGUOUS'],
- msg='Making sure the array is not contiguous.')
-
- histo, cumul, bin_edges = histogramnd(uncontig_sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_nominal_uncontiguous_weights(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- shape = list(self.weights.shape)
- shape[0] *= 2
- weights = np.zeros(shape, dtype=self.weights.dtype)
- uncontig_weights = weights[::2, ...]
- uncontig_weights[:] = self.weights
-
- self.assertFalse(uncontig_weights.flags['C_CONTIGUOUS'],
- msg='Making sure the array is not contiguous.')
-
- histo, cumul, bin_edges = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=uncontig_weights)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_nominal_wo_weights(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=None)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(cumul is None)
-
- def test_nominal_wo_weights_w_cumul(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
-
- # creating an array of ones just to make sure that
- # it is not cleared by histogramnd
- cumul_in = np.ones(self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=None,
- weighted_histo=cumul_in)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(cumul is None)
- self.assertTrue(np.array_equal(cumul_in,
- np.ones(shape=self.n_bins,
- dtype=np.double)))
-
- def test_nominal_wo_weights_w_histo(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
-
- # creating an array of ones just to make sure that
- # it is not cleared by histogramnd
- histo_in = np.ones(self.n_bins, dtype=np.uint32)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=None,
- histo=histo_in)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h + 1))
- self.assertTrue(cumul is None)
- self.assertEqual(id(histo), id(histo_in))
-
- def test_nominal_last_bin_closed(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 2])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 1101.1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_int32_weights_double_weights_range(self):
- """
- """
- weight_min = -299.9 # ===> will be cast to -299
- weight_max = 499.9 # ===> will be cast to 499
-
- expected_h_tpl = np.array([0, 1, 1, 1, 0])
- expected_c_tpl = np.array([0., 0., 0., 300., 0.])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights.astype(np.int32),
- weight_min=weight_min,
- weight_max=weight_max)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_reuse_histo(self):
- """
- """
-
- expected_h_tpl = np.array([2, 3, 2, 2, 2])
- expected_c_tpl = np.array([0.0, -7007, -5.0, 0.1, 3003.0])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)[0:2]
-
- sample_2 = self.sample[:]
- if len(sample_2.shape) == 1:
- idx = (slice(0, None),)
- else:
- idx = slice(0, None), self.tested_dim
-
- sample_2[idx] += 2
-
- histo_2, cumul = histogramnd(sample_2, # <==== !!
- self.histo_range,
- self.n_bins,
- weights=10 * self.weights, # <==== !!
- histo=histo)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
- self.assertEqual(id(histo), id(histo_2))
-
- def test_reuse_cumul(self):
- """
- """
-
- expected_h_tpl = np.array([0, 2, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -7007.5, -4.99, 300.4, 3503.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)[0:2]
-
- sample_2 = self.sample[:]
- if len(sample_2.shape) == 1:
- idx = (slice(0, None),)
- else:
- idx = slice(0, None), self.tested_dim
-
- sample_2[idx] += 2
-
- histo, cumul_2 = histogramnd(sample_2, # <==== !!
- self.histo_range,
- self.n_bins,
- weights=10 * self.weights, # <==== !!
- weighted_histo=cumul)[0:2]
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.allclose(cumul, expected_c, rtol=10e-15))
- self.assertEqual(id(cumul), id(cumul_2))
-
- def test_reuse_cumul_float(self):
- """
- """
-
- expected_h_tpl = np.array([0, 2, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -7007.5, -4.99, 300.4, 3503.5],
- dtype=np.float32)
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)[0:2]
-
- # converting the cumul array to float
- cumul = cumul.astype(np.float32)
-
- sample_2 = self.sample[:]
- if len(sample_2.shape) == 1:
- idx = (slice(0, None),)
- else:
- idx = slice(0, None), self.tested_dim
-
- sample_2[idx] += 2
-
- histo, cumul_2 = histogramnd(sample_2, # <==== !!
- self.histo_range,
- self.n_bins,
- weights=10 * self.weights, # <==== !!
- weighted_histo=cumul)[0:2]
-
- self.assertEqual(cumul.dtype, np.float32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertEqual(id(cumul), id(cumul_2))
- self.assertTrue(np.allclose(cumul, expected_c, rtol=10e-15))
-
-class _Test_Histogramnd_nominal(unittest.TestCase):
- """
- Unit tests of the Histogramnd class.
- """
-
- ndims = None
-
- def setUp(self):
- ndims = self.ndims
- self.tested_dim = ndims-1
-
- if ndims is None:
- raise ValueError('ndims class member not set.')
-
- sample = np.array([5.5, -3.3,
- 0., -0.5,
- 3.3, 8.8,
- -7.7, 6.0,
- -4.0])
-
- weights = np.array([500.5, -300.3,
- 0.01, -0.5,
- 300.3, 800.8,
- -700.7, 600.6,
- -400.4])
-
- n_elems = len(sample)
-
- if ndims == 1:
- shape = (n_elems,)
- else:
- shape = (n_elems, ndims)
-
- self.sample = np.zeros(shape=shape, dtype=sample.dtype)
- if ndims == 1:
- self.sample = sample
- else:
- self.sample[..., ndims-1] = sample
-
- self.weights = weights
-
- # the tests are performed along one dimension,
- # all the other bins indices along the other dimensions
- # are expected to be 2
- # (e.g : when testing a 2D sample : [0, x] will go into
- # bin [2, y] because of the bin ranges [-2, 2] and n_bins = 4
- # for the first dimension)
- self.other_axes_index = 2
- self.histo_range = np.repeat([[-2., 2.]], ndims, axis=0)
- self.histo_range[ndims-1] = [-4., 6.]
-
- self.n_bins = np.array([4]*ndims)
- self.n_bins[ndims-1] = 5
-
- if ndims == 1:
- def fill_histo(h, v, dim, op=None):
- if op:
- h[:] = op(h[:], v)
- else:
- h[:] = v
- self.fill_histo = fill_histo
- else:
- def fill_histo(h, v, dim, op=None):
- idx = [self.other_axes_index]*len(h.shape)
- idx[dim] = slice(0, None)
- if op:
- h[idx] = op(h[idx], v)
- else:
- h[idx] = v
- self.fill_histo = fill_histo
-
- def test_nominal(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- histo, cumul, bin_edges = histo
-
- expected_edges = _get_bin_edges(self.histo_range,
- self.n_bins,
- self.ndims)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- for i_edges, edges in enumerate(expected_edges):
- self.assertTrue(np.array_equal(bin_edges[i_edges],
- expected_edges[i_edges]),
- msg='Testing bin_edges for dim {0}'
- ''.format(i_edges+1))
-
- def test_nominal_wh_dtype(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.float32)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul, bin_edges = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- wh_dtype=np.float32)
-
- self.assertEqual(cumul.dtype, np.float32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.allclose(cumul, expected_c))
-
- def test_nominal_uncontiguous_sample(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- shape = list(self.sample.shape)
- shape[0] *= 2
- sample = np.zeros(shape, dtype=self.sample.dtype)
- uncontig_sample = sample[::2, ...]
- uncontig_sample[:] = self.sample
-
- self.assertFalse(uncontig_sample.flags['C_CONTIGUOUS'],
- msg='Making sure the array is not contiguous.')
-
- histo, cumul, bin_edges = Histogramnd(uncontig_sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_nominal_uncontiguous_weights(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- shape = list(self.weights.shape)
- shape[0] *= 2
- weights = np.zeros(shape, dtype=self.weights.dtype)
- uncontig_weights = weights[::2, ...]
- uncontig_weights[:] = self.weights
-
- self.assertFalse(uncontig_weights.flags['C_CONTIGUOUS'],
- msg='Making sure the array is not contiguous.')
-
- histo, cumul, bin_edges = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=uncontig_weights)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_nominal_wo_weights(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
-
- histo, cumul = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=None)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(cumul is None)
-
- def test_nominal_last_bin_closed(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 2])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 1101.1])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_int32_weights_double_weights_range(self):
- """
- """
- weight_min = -299.9 # ===> will be cast to -299
- weight_max = 499.9 # ===> will be cast to 499
-
- expected_h_tpl = np.array([0, 1, 1, 1, 0])
- expected_c_tpl = np.array([0., 0., 0., 300., 0.])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo, cumul = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights.astype(np.int32),
- weight_min=weight_min,
- weight_max=weight_max)[0:2]
-
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def test_nominal_no_sample(self):
- """
- """
-
- histo_inst = Histogramnd(None,
- self.histo_range,
- self.n_bins)
-
- histo, weighted_histo, edges = histo_inst
-
- self.assertIsNone(histo)
- self.assertIsNone(weighted_histo)
- self.assertIsNone(edges)
- self.assertIsNone(histo_inst.histo)
- self.assertIsNone(histo_inst.weighted_histo)
- self.assertIsNone(histo_inst.edges)
-
- def test_empty_init_accumulate(self):
- """
- """
- expected_h_tpl = np.array([2, 1, 1, 1, 1])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo_inst = Histogramnd(None,
- self.histo_range,
- self.n_bins)
-
- histo_inst.accumulate(self.sample,
- weights=self.weights)
-
- histo = histo_inst.histo
- cumul = histo_inst.weighted_histo
- bin_edges = histo_inst.edges
-
- expected_edges = _get_bin_edges(self.histo_range,
- self.n_bins,
- self.ndims)
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertEqual(histo.dtype, np.uint32)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- for i_edges, edges in enumerate(expected_edges):
- self.assertTrue(np.array_equal(bin_edges[i_edges],
- expected_edges[i_edges]),
- msg='Testing bin_edges for dim {0}'
- ''.format(i_edges+1))
-
- def test_accumulate(self):
- """
- """
-
- expected_h_tpl = np.array([2, 3, 2, 2, 2])
- expected_c_tpl = np.array([-700.7, -7007.5, -4.99, 300.4, 3503.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo_inst = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- sample_2 = self.sample[:]
- if len(sample_2.shape) == 1:
- idx = (slice(0, None),)
- else:
- idx = slice(0, None), self.tested_dim
-
- sample_2[idx] += 2
-
- histo_inst.accumulate(sample_2, # <==== !!
- weights=10 * self.weights) # <==== !!
-
- histo = histo_inst.histo
- cumul = histo_inst.weighted_histo
- bin_edges = histo_inst.edges
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.allclose(cumul, expected_c, rtol=10e-15))
-
- def test_accumulate_no_weights(self):
- """
- """
-
- expected_h_tpl = np.array([2, 3, 2, 2, 2])
- expected_c_tpl = np.array([-700.7, -0.5, 0.01, 300.3, 500.5])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo_inst = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- sample_2 = self.sample[:]
- if len(sample_2.shape) == 1:
- idx = (slice(0, None),)
- else:
- idx = slice(0, None), self.tested_dim
-
- sample_2[idx] += 2
-
- histo_inst.accumulate(sample_2) # <==== !!
-
- histo = histo_inst.histo
- cumul = histo_inst.weighted_histo
- bin_edges = histo_inst.edges
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.allclose(cumul, expected_c, rtol=10e-15))
-
- def test_accumulate_no_weights_at_init(self):
- """
- """
-
- expected_h_tpl = np.array([2, 3, 2, 2, 2])
- expected_c_tpl = np.array([0.0, -700.7, -0.5, 0.01, 300.3])
-
- expected_h = np.zeros(shape=self.n_bins, dtype=np.double)
- expected_c = np.zeros(shape=self.n_bins, dtype=np.double)
-
- self.fill_histo(expected_h, expected_h_tpl, self.ndims-1)
- self.fill_histo(expected_c, expected_c_tpl, self.ndims-1)
-
- histo_inst = Histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=None) # <==== !!
-
- cumul = histo_inst.weighted_histo
- self.assertIsNone(cumul)
-
- sample_2 = self.sample[:]
- if len(sample_2.shape) == 1:
- idx = (slice(0, None),)
- else:
- idx = slice(0, None), self.tested_dim
-
- sample_2[idx] += 2
-
- histo_inst.accumulate(sample_2,
- weights=self.weights) # <==== !!
-
- histo = histo_inst.histo
- cumul = histo_inst.weighted_histo
- bin_edges = histo_inst.edges
-
- self.assertEqual(cumul.dtype, np.float64)
- self.assertTrue(np.array_equal(histo, expected_h))
- self.assertTrue(np.array_equal(cumul, expected_c))
-
- def testNoneNativeTypes(self):
- type = self.sample.dtype.newbyteorder("B")
- sampleB = self.sample.astype(type)
-
- type = self.sample.dtype.newbyteorder("L")
- sampleL = self.sample.astype(type)
-
- histo_inst = Histogramnd(sampleB,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
- histo_inst = Histogramnd(sampleL,
- self.histo_range,
- self.n_bins,
- weights=self.weights)
-
-
-class Test_chistogram_nominal_1d(_Test_chistogramnd_nominal):
- ndims = 1
-
-
-class Test_chistogram_nominal_2d(_Test_chistogramnd_nominal):
- ndims = 2
-
-
-class Test_chistogram_nominal_3d(_Test_chistogramnd_nominal):
- ndims = 3
-
-
-class Test_Histogramnd_nominal_1d(_Test_Histogramnd_nominal):
- ndims = 1
-
-
-class Test_Histogramnd_nominal_2d(_Test_Histogramnd_nominal):
- ndims = 2
-
-
-class Test_Histogramnd_nominal_3d(_Test_Histogramnd_nominal):
- ndims = 3
-
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-test_cases = (Test_chistogram_nominal_1d,
- Test_chistogram_nominal_2d,
- Test_chistogram_nominal_3d,
- Test_Histogramnd_nominal_1d,
- # Test_Histogramnd_nominal_2d,
- # Test_Histogramnd_nominal_3d
- )
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/test_histogramnd_vs_np.py b/silx/math/test/test_histogramnd_vs_np.py
deleted file mode 100644
index 36d71f9..0000000
--- a/silx/math/test/test_histogramnd_vs_np.py
+++ /dev/null
@@ -1,848 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""
-Tests for the histogramnd function.
-Results are compared to numpy's histogramdd.
-"""
-
-import unittest
-import operator
-
-import numpy as np
-
-from silx.math.chistogramnd import chistogramnd as histogramnd
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-_RTOL_DICT = {np.float64: 10**-13,
- np.float32: 10**-5}
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-def _add_values_to_array_if_missing(array, values, n_values):
- max_in_col = np.any(array[:, ...] == values, axis=0)
-
- if len(array.shape) == 1:
- if not max_in_col:
- rnd_idx = np.random.randint(0,
- high=len(array)-1,
- size=(n_values,))
- array[rnd_idx] = values
- else:
- for i in range(len(max_in_col)):
- if not max_in_col[i]:
- rnd_idx = np.random.randint(0,
- high=len(array)-1,
- size=(n_values,))
- array[rnd_idx, i] = values[i]
-
-
-def _get_values_index(array, values, op=operator.lt):
- idx = op(array[:, ...], values)
- if array.ndim > 1:
- idx = np.all(idx, axis=1)
- return np.where(idx)[0]
-
-
-def _get_in_range_indices(array,
- minvalues,
- maxvalues,
- minop=operator.ge,
- maxop=operator.lt):
- idx = np.logical_and(minop(array, minvalues),
- maxop(array, maxvalues))
- if array.ndim > 1:
- idx = np.all(idx, axis=1)
- return np.where(idx)[0]
-
-
-class _TestHistogramnd(unittest.TestCase):
-
- """
- Unit tests of the histogramnd function.
- """
- sample_rng = None
- weights_rng = None
- n_dims = None
-
- filter_min = None
- filter_max = None
-
- histo_range = None
- n_bins = None
-
- dtype_sample = None
- dtype_weights = None
-
- def generate_data(self):
-
- self.longMessage = True
-
- int_min = 0
- int_max = 100000
- n_elements = 10**5
-
- if self.n_dims == 1:
- shape = (n_elements,)
- else:
- shape = (n_elements, self.n_dims,)
-
- self.rng_state = np.random.get_state()
-
- self.state_msg = ('Current RNG state :\n'
- '{0}'.format(self.rng_state))
-
- sample = np.random.randint(int_min,
- high=int_max,
- size=shape)
-
- sample = sample.astype(self.dtype_sample)
- sample = (self.sample_rng[0] +
- (sample-int_min) *
- (self.sample_rng[1]-self.sample_rng[0]) /
- (int_max-int_min)).astype(self.dtype_sample)
-
- weights = np.random.randint(int_min,
- high=int_max,
- size=(n_elements,))
- weights = weights.astype(self.dtype_weights)
- weights = (self.weights_rng[0] +
- (weights-int_min) *
- (self.weights_rng[1]-self.weights_rng[0]) /
- (int_max-int_min)).astype(self.dtype_weights)
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # the bins range are cast to the same type as the sample
- # in order to get the same results as numpy
- # (which doesnt cast the range)
- self.histo_range = np.array(self.histo_range).astype(self.dtype_sample)
-
- # adding some values that are equal to the max
- # in order to test the opened/closed last bin
- bins_max = [b[1] for b in self.histo_range]
- _add_values_to_array_if_missing(sample,
- bins_max,
- 100)
-
- # adding some values that are equal to the min weight value
- # in order to test the filters
- _add_values_to_array_if_missing(weights,
- self.weights_rng[0],
- 100)
-
- # adding some values that are equal to the max weight value
- # in order to test the filters
- _add_values_to_array_if_missing(weights,
- self.weights_rng[1],
- 100)
-
- return sample, weights
-
- def setUp(self):
- self.sample, self.weights = self.generate_data()
- self.rtol = _RTOL_DICT.get(self.dtype_weights, None)
-
- def array_compare(self, ar_a, ar_b):
- if self.rtol is None:
- return np.array_equal(ar_a, ar_b)
- return np.allclose(ar_a, ar_b, self.rtol)
-
- def test_bin_ranges(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True)
-
- result_np = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range)
-
- for i_edges, edges in enumerate(result_c[2]):
- # allclose for now until I can try with the latest version (TBD)
- # of numpy
- self.assertTrue(np.allclose(edges,
- result_np[1][i_edges]),
- msg='{0}. Testing bin_edges for dim {1}.'
- ''.format(self.state_msg, i_edges+1))
-
- def test_last_bin_closed(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True)
-
- result_np = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights)
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c[0],
- result_np[0])
- # comparing weights
- weights_cmp = np.array_equal(result_c[1],
- result_np_w[0])
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertTrue(weights_cmp, msg=self.state_msg)
-
- bins_min = [rng[0] for rng in self.histo_range]
- bins_max = [rng[1] for rng in self.histo_range]
- inrange_idx = _get_in_range_indices(self.sample,
- bins_min,
- bins_max,
- minop=operator.ge,
- maxop=operator.le)
-
- self.assertEqual(result_c[0].sum(), inrange_idx.shape[0],
- msg=self.state_msg)
-
- # we have to sum the weights using the same precision as the
- # histogramnd function
- weights_sum = self.weights[inrange_idx].astype(result_c[1].dtype).sum()
- self.assertTrue(self.array_compare(result_c[1].sum(), weights_sum),
- msg=self.state_msg)
-
- def test_last_bin_open(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=False)
-
- bins_max = [rng[1] for rng in self.histo_range]
- filtered_idx = _get_values_index(self.sample, bins_max)
-
- result_np = np.histogramdd(self.sample[filtered_idx],
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w = np.histogramdd(self.sample[filtered_idx],
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights[filtered_idx])
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c[0], result_np[0])
- # comparing weights
- weights_cmp = np.array_equal(result_c[1],
- result_np_w[0])
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertTrue(weights_cmp, msg=self.state_msg)
-
- bins_min = [rng[0] for rng in self.histo_range]
- bins_max = [rng[1] for rng in self.histo_range]
- inrange_idx = _get_in_range_indices(self.sample,
- bins_min,
- bins_max,
- minop=operator.ge,
- maxop=operator.lt)
-
- self.assertEqual(result_c[0].sum(), len(inrange_idx),
- msg=self.state_msg)
- # we have to sum the weights using the same precision as the
- # histogramnd function
- weights_sum = self.weights[inrange_idx].astype(result_c[1].dtype).sum()
- self.assertTrue(self.array_compare(result_c[1].sum(), weights_sum),
- msg=self.state_msg)
-
- def test_filter_min(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True,
- weight_min=self.filter_min)
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!
- filter_min = self.dtype_weights(self.filter_min)
-
- weight_idx = _get_values_index(self.weights,
- filter_min, # <------ !!!
- operator.ge)
-
- result_np = np.histogramdd(self.sample[weight_idx],
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w = np.histogramdd(self.sample[weight_idx],
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights[weight_idx])
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c[0],
- result_np[0])
- # comparing weights
- weights_cmp = np.array_equal(result_c[1], result_np_w[0])
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertTrue(weights_cmp, msg=self.state_msg)
-
- bins_min = [rng[0] for rng in self.histo_range]
- bins_max = [rng[1] for rng in self.histo_range]
- inrange_idx = _get_in_range_indices(self.sample[weight_idx],
- bins_min,
- bins_max,
- minop=operator.ge,
- maxop=operator.le)
-
- inrange_idx = weight_idx[inrange_idx]
-
- self.assertEqual(result_c[0].sum(), len(inrange_idx),
- msg=self.state_msg)
-
- # we have to sum the weights using the same precision as the
- # histogramnd function
- weights_sum = self.weights[inrange_idx].astype(result_c[1].dtype).sum()
- self.assertTrue(self.array_compare(result_c[1].sum(), weights_sum),
- msg=self.state_msg)
-
- def test_filter_max(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True,
- weight_max=self.filter_max)
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!
- filter_max = self.dtype_weights(self.filter_max)
-
- weight_idx = _get_values_index(self.weights,
- filter_max, # <------ !!!
- operator.le)
-
- result_np = np.histogramdd(self.sample[weight_idx],
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w = np.histogramdd(self.sample[weight_idx],
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights[weight_idx])
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c[0],
- result_np[0])
- # comparing weights
- weights_cmp = np.array_equal(result_c[1], result_np_w[0])
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertTrue(weights_cmp, msg=self.state_msg)
-
- bins_min = [rng[0] for rng in self.histo_range]
- bins_max = [rng[1] for rng in self.histo_range]
- inrange_idx = _get_in_range_indices(self.sample[weight_idx],
- bins_min,
- bins_max,
- minop=operator.ge,
- maxop=operator.le)
-
- inrange_idx = weight_idx[inrange_idx]
-
- self.assertEqual(result_c[0].sum(), len(inrange_idx),
- msg=self.state_msg)
-
- # we have to sum the weights using the same precision as the
- # histogramnd function
- weights_sum = self.weights[inrange_idx].astype(result_c[1].dtype).sum()
- self.assertTrue(self.array_compare(result_c[1].sum(), weights_sum),
- msg=self.state_msg)
-
- def test_filter_minmax(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True,
- weight_min=self.filter_min,
- weight_max=self.filter_max)
-
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!
- filter_min = self.dtype_weights(self.filter_min)
- filter_max = self.dtype_weights(self.filter_max)
-
- weight_idx = _get_in_range_indices(self.weights,
- filter_min, # <------ !!!
- filter_max, # <------ !!!
- minop=operator.ge,
- maxop=operator.le)
-
- result_np = np.histogramdd(self.sample[weight_idx],
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w = np.histogramdd(self.sample[weight_idx],
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights[weight_idx])
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c[0],
- result_np[0])
- # comparing weights
- weights_cmp = np.array_equal(result_c[1], result_np_w[0])
-
- self.assertTrue(hits_cmp)
- self.assertTrue(weights_cmp)
-
- bins_min = [rng[0] for rng in self.histo_range]
- bins_max = [rng[1] for rng in self.histo_range]
- inrange_idx = _get_in_range_indices(self.sample[weight_idx],
- bins_min,
- bins_max,
- minop=operator.ge,
- maxop=operator.le)
-
- inrange_idx = weight_idx[inrange_idx]
-
- self.assertEqual(result_c[0].sum(), len(inrange_idx),
- msg=self.state_msg)
-
- # we have to sum the weights using the same precision as the
- # histogramnd function
- weights_sum = self.weights[inrange_idx].astype(result_c[1].dtype).sum()
- self.assertTrue(self.array_compare(result_c[1].sum(), weights_sum),
- msg=self.state_msg)
-
- def test_reuse_histo(self):
- """
-
- """
- result_c_1 = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True)
-
- result_np_1 = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range)
-
- np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights)
-
- sample_2, weights_2 = self.generate_data()
-
- result_c_2 = histogramnd(sample_2,
- self.histo_range,
- self.n_bins,
- weights=weights_2,
- last_bin_closed=True,
- histo=result_c_1[0])
-
- result_np_2 = np.histogramdd(sample_2,
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w_2 = np.histogramdd(sample_2,
- bins=self.n_bins,
- range=self.histo_range,
- weights=weights_2)
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c_2[0],
- result_np_1[0] +
- result_np_2[0])
- # comparing weights
- weights_cmp = np.array_equal(result_c_2[1],
- result_np_w_2[0])
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertTrue(weights_cmp, msg=self.state_msg)
-
- def test_reuse_cumul(self):
- """
-
- """
- result_c = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True)
-
- np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights)
-
- sample_2, weights_2 = self.generate_data()
-
- result_c_2 = histogramnd(sample_2,
- self.histo_range,
- self.n_bins,
- weights=weights_2,
- last_bin_closed=True,
- weighted_histo=result_c[1])
-
- result_np_2 = np.histogramdd(sample_2,
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w_2 = np.histogramdd(sample_2,
- bins=self.n_bins,
- range=self.histo_range,
- weights=weights_2)
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c_2[0],
- result_np_2[0])
- # comparing weights
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertTrue(self.array_compare(result_c_2[1],
- result_np_w[0] + result_np_w_2[0]),
- msg=self.state_msg)
-
- def test_reuse_cumul_float(self):
- """
-
- """
- n_bins = np.array(self.n_bins, ndmin=1)
- if len(self.sample.shape) == 2:
- if len(n_bins) == self.sample.shape[1]:
- shp = tuple([x for x in n_bins])
- else:
- shp = (self.n_bins,) * self.sample.shape[1]
- cumul = np.zeros(shp, dtype=np.float32)
- else:
- shp = (self.n_bins,)
- cumul = np.zeros(shp, dtype=np.float32)
-
- result_c_1 = histogramnd(self.sample,
- self.histo_range,
- self.n_bins,
- weights=self.weights,
- last_bin_closed=True,
- weighted_histo=cumul)
-
- result_np_1 = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range)
-
- result_np_w_1 = np.histogramdd(self.sample,
- bins=self.n_bins,
- range=self.histo_range,
- weights=self.weights)
-
- # comparing "hits"
- hits_cmp = np.array_equal(result_c_1[0],
- result_np_1[0])
-
- self.assertTrue(hits_cmp, msg=self.state_msg)
- self.assertEqual(result_c_1[1].dtype, np.float32, msg=self.state_msg)
-
- bins_min = [rng[0] for rng in self.histo_range]
- bins_max = [rng[1] for rng in self.histo_range]
- inrange_idx = _get_in_range_indices(self.sample,
- bins_min,
- bins_max,
- minop=operator.ge,
- maxop=operator.le)
- weights_sum = \
- self.weights[inrange_idx].astype(np.float32).sum(dtype=np.float64)
- self.assertTrue(np.allclose(result_c_1[1].sum(dtype=np.float64),
- weights_sum), msg=self.state_msg)
- self.assertTrue(np.allclose(result_c_1[1].sum(dtype=np.float64),
- result_np_w_1[0].sum(dtype=np.float64)),
- msg=self.state_msg)
-
-
-class _TestHistogramnd_1d(_TestHistogramnd):
-
- """
- Unit tests of the 1D histogramnd function.
- """
-
- sample_rng = [-55., 100.]
- weights_rng = [-70., 150.]
- n_dims = 1
- filter_min = -15.6
- filter_max = 85.7
-
- histo_range = [[-30.2, 90.3]]
- n_bins = 30
-
- dtype = None
-
-
-class _TestHistogramnd_2d(_TestHistogramnd):
-
- """
- Unit tests of the 1D histogramnd function.
- """
-
- sample_rng = [-50.2, 100.99]
- weights_rng = [70., 150.]
- n_dims = 2
- filter_min = 81.7
- filter_max = 135.3
-
- histo_range = [[10., 90.], [20., 70.]]
- n_bins = 30
-
- dtype = None
-
-
-class _TestHistogramnd_3d(_TestHistogramnd):
-
- """
- Unit tests of the 1D histogramnd function.
- """
-
- sample_rng = [10.2, 200.9]
- weights_rng = [0., 100.]
- n_dims = 3
- filter_min = 31.5
- filter_max = 83.7
-
- histo_range = [[30.8, 150.2], [20.1, 90.9], [10.1, 195.]]
- n_bins = 30
-
- dtype = None
-
-
-# ################################################################
-# ################################################################
-# ################################################################
-# ################################################################
-
-
-class TestHistogramnd_1d_double_double(_TestHistogramnd_1d):
- dtype_sample = np.double
- dtype_weights = np.double
-
-
-class TestHistogramnd_1d_double_float(_TestHistogramnd_1d):
- dtype_sample = np.double
- dtype_weights = np.float32
-
-
-class TestHistogramnd_1d_double_int32(_TestHistogramnd_1d):
- dtype_sample = np.double
- dtype_weights = np.int32
-
-
-class TestHistogramnd_1d_float_double(_TestHistogramnd_1d):
- dtype_sample = np.float32
- dtype_weights = np.double
-
-
-class TestHistogramnd_1d_float_float(_TestHistogramnd_1d):
- dtype_sample = np.float32
- dtype_weights = np.float32
-
-
-class TestHistogramnd_1d_float_int32(_TestHistogramnd_1d):
- dtype_sample = np.float32
- dtype_weights = np.int32
-
-
-class TestHistogramnd_1d_int32_double(_TestHistogramnd_1d):
- dtype_sample = np.int32
- dtype_weights = np.double
-
-
-class TestHistogramnd_1d_int32_float(_TestHistogramnd_1d):
- dtype_sample = np.int32
- dtype_weights = np.float32
-
-
-class TestHistogramnd_1d_int32_int32(_TestHistogramnd_1d):
- dtype_sample = np.int32
- dtype_weights = np.int32
-
-
-class TestHistogramnd_2d_double_double(_TestHistogramnd_2d):
- dtype_sample = np.double
- dtype_weights = np.double
-
-
-class TestHistogramnd_2d_double_float(_TestHistogramnd_2d):
- dtype_sample = np.double
- dtype_weights = np.float32
-
-
-class TestHistogramnd_2d_double_int32(_TestHistogramnd_2d):
- dtype_sample = np.double
- dtype_weights = np.int32
-
-
-class TestHistogramnd_2d_float_double(_TestHistogramnd_2d):
- dtype_sample = np.float32
- dtype_weights = np.double
-
-
-class TestHistogramnd_2d_float_float(_TestHistogramnd_2d):
- dtype_sample = np.float32
- dtype_weights = np.float32
-
-
-class TestHistogramnd_2d_float_int32(_TestHistogramnd_2d):
- dtype_sample = np.float32
- dtype_weights = np.int32
-
-
-class TestHistogramnd_2d_int32_double(_TestHistogramnd_2d):
- dtype_sample = np.int32
- dtype_weights = np.double
-
-
-class TestHistogramnd_2d_int32_float(_TestHistogramnd_2d):
- dtype_sample = np.int32
- dtype_weights = np.float32
-
-
-class TestHistogramnd_2d_int32_int32(_TestHistogramnd_2d):
- dtype_sample = np.int32
- dtype_weights = np.int32
-
-
-class TestHistogramnd_3d_double_double(_TestHistogramnd_3d):
- dtype_sample = np.double
- dtype_weights = np.double
-
-
-class TestHistogramnd_3d_double_float(_TestHistogramnd_3d):
- dtype_sample = np.double
- dtype_weights = np.float32
-
-
-class TestHistogramnd_3d_double_int32(_TestHistogramnd_3d):
- dtype_sample = np.double
- dtype_weights = np.int32
-
-
-class TestHistogramnd_3d_float_double(_TestHistogramnd_3d):
- dtype_sample = np.float32
- dtype_weights = np.double
-
-
-class TestHistogramnd_3d_float_float(_TestHistogramnd_3d):
- dtype_sample = np.float32
- dtype_weights = np.float32
-
-
-class TestHistogramnd_3d_float_int32(_TestHistogramnd_3d):
- dtype_sample = np.float32
- dtype_weights = np.int32
-
-
-class TestHistogramnd_3d_int32_double(_TestHistogramnd_3d):
- dtype_sample = np.int32
- dtype_weights = np.double
-
-
-class TestHistogramnd_3d_int32_float(_TestHistogramnd_3d):
- dtype_sample = np.int32
- dtype_weights = np.float32
-
-
-class TestHistogramnd_3d_int32_int32(_TestHistogramnd_3d):
- dtype_sample = np.int32
- dtype_weights = np.int32
-
-
-# ==============================================================
-# ==============================================================
-# ==============================================================
-
-
-test_cases = (TestHistogramnd_1d_double_double,
- TestHistogramnd_1d_double_float,
- TestHistogramnd_1d_double_int32,
- TestHistogramnd_1d_float_double,
- TestHistogramnd_1d_float_float,
- TestHistogramnd_1d_float_int32,
- TestHistogramnd_1d_int32_double,
- TestHistogramnd_1d_int32_float,
- TestHistogramnd_1d_int32_int32,
- TestHistogramnd_2d_double_double,
- TestHistogramnd_2d_double_float,
- TestHistogramnd_2d_double_int32,
- TestHistogramnd_2d_float_double,
- TestHistogramnd_2d_float_float,
- TestHistogramnd_2d_float_int32,
- TestHistogramnd_2d_int32_double,
- TestHistogramnd_2d_int32_float,
- TestHistogramnd_2d_int32_int32,
- TestHistogramnd_3d_double_double,
- TestHistogramnd_3d_double_float,
- TestHistogramnd_3d_double_int32,
- TestHistogramnd_3d_float_double,
- TestHistogramnd_3d_float_float,
- TestHistogramnd_3d_float_int32,
- TestHistogramnd_3d_int32_double,
- TestHistogramnd_3d_int32_float,
- TestHistogramnd_3d_int32_int32,)
-
-
-def suite():
- loader = unittest.defaultTestLoader
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- tests = loader.loadTestsFromTestCase(test_class)
- test_suite.addTests(tests)
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")
diff --git a/silx/math/test/test_interpolate.py b/silx/math/test/test_interpolate.py
deleted file mode 100644
index 9989302..0000000
--- a/silx/math/test/test_interpolate.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2019 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Test for interpolate module"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "11/07/2019"
-
-
-import unittest
-
-import numpy
-try:
- from scipy.interpolate import interpn
-except ImportError:
- interpn = None
-
-from silx.utils.testutils import ParametricTestCase
-from silx.math import interpolate
-
-
-@unittest.skipUnless(interpn is not None, "scipy missing")
-class TestInterp3d(ParametricTestCase):
- """Test silx.math.interpolate.interp3d"""
-
- @staticmethod
- def ref_interp3d(data, points):
- """Reference implementation of interp3d based on scipy
-
- :param numpy.ndarray data: 3D floating dataset
- :param numpy.ndarray points: Array of points of shape (N, 3)
- """
- return interpn(
- [numpy.arange(dim, dtype=data.dtype) for dim in data.shape],
- data,
- points,
- method='linear')
-
- def test_random_data(self):
- """Test interp3d with random data"""
- size = 32
- npoints = 10
-
- ref_data = numpy.random.random((size, size, size))
- ref_points = numpy.random.random(npoints*3).reshape(npoints, 3) * (size -1)
-
- for dtype in (numpy.float32, numpy.float64):
- data = ref_data.astype(dtype)
- points = ref_points.astype(dtype)
- ref_result = self.ref_interp3d(data, points)
-
- for method in (u'linear', u'linear_omp'):
- with self.subTest(method=method):
- result = interpolate.interp3d(data, points, method=method)
- self.assertTrue(numpy.allclose(ref_result, result))
-
- def test_notfinite_data(self):
- """Test interp3d with NaN and inf"""
- data = numpy.ones((3, 3, 3), dtype=numpy.float64)
- data[0, 0, 0] = numpy.nan
- data[2, 2, 2] = numpy.inf
- points = numpy.array([(0.5, 0.5, 0.5),
- (1.5, 1.5, 1.5)])
-
- for method in (u'linear', u'linear_omp'):
- with self.subTest(method=method):
- result = interpolate.interp3d(
- data, points, method=method)
- self.assertTrue(numpy.isnan(result[0]))
- self.assertTrue(result[1] == numpy.inf)
-
- def test_points_outside(self):
- """Test interp3d with points outside the volume"""
- data = numpy.ones((4, 4, 4), dtype=numpy.float64)
- points = numpy.array([(-0.1, -0.1, -0.1),
- (3.1, 3.1, 3.1),
- (-0.1, 1., 1.),
- (1., 1., 3.1)])
-
- for method in (u'linear', u'linear_omp'):
- for fill_value in (numpy.nan, 0., -1.):
- with self.subTest(method=method):
- result = interpolate.interp3d(
- data, points, method=method, fill_value=fill_value)
- if numpy.isnan(fill_value):
- self.assertTrue(numpy.all(numpy.isnan(result)))
- else:
- self.assertTrue(numpy.all(numpy.equal(result, fill_value)))
-
- def test_integer_points(self):
- """Test interp3d with integer points coord"""
- data = numpy.arange(4**3, dtype=numpy.float64).reshape(4, 4, 4)
- points = numpy.array([(0., 0., 0.),
- (0., 0., 1.),
- (2., 3., 0.),
- (3., 3., 3.)])
-
- ref_result = data[tuple(points.T.astype(numpy.int32))]
-
- for method in (u'linear', u'linear_omp'):
- with self.subTest(method=method):
- result = interpolate.interp3d(data, points, method=method)
- self.assertTrue(numpy.allclose(ref_result, result))
-
-
-def suite():
- test_suite = unittest.TestSuite()
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(TestInterp3d))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/silx/math/test/test_marchingcubes.py b/silx/math/test/test_marchingcubes.py
deleted file mode 100644
index 41f7e30..0000000
--- a/silx/math/test/test_marchingcubes.py
+++ /dev/null
@@ -1,188 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-# Copyright (C) 2016 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ############################################################################*/
-"""Tests of the marchingcubes module"""
-
-from __future__ import division
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "17/01/2018"
-
-import unittest
-
-import numpy
-
-from silx.utils.testutils import ParametricTestCase
-
-from silx.math import marchingcubes
-
-
-class TestMarchingCubes(ParametricTestCase):
- """Tests of marching cubes"""
-
- def assertAllClose(self, array1, array2, msg=None,
- rtol=1e-05, atol=1e-08):
- """Assert that the 2 numpy.ndarrays are almost equal.
-
- :param str msg: Message to provide when assert fails
- :param float rtol: Relative tolerance, see :func:`numpy.allclose`
- :param float atol: Absolute tolerance, see :func:`numpy.allclose`
- """
- if not numpy.allclose(array1, array2, rtol, atol):
- raise self.failureException(msg)
-
- def test_cube(self):
- """Unit tests with a single cube"""
-
- # No isosurface
- cube_zero = numpy.zeros((2, 2, 2), dtype=numpy.float32)
-
- result = marchingcubes.MarchingCubes(cube_zero, 1.)
- self.assertEqual(result.shape, cube_zero.shape)
- self.assertEqual(result.isolevel, 1.)
- self.assertEqual(result.invert_normals, True)
-
- vertices, normals, indices = result
- self.assertEqual(len(vertices), 0)
- self.assertEqual(len(normals), 0)
- self.assertEqual(len(indices), 0)
-
- # Cube array dimensions: shape = (dim 0, dim 1, dim2)
- #
- # dim 0 (Z)
- # ^
- # |
- # 4 +------+ 5
- # /| /|
- # / | / |
- # 6 +------+ 7|
- # | | | |
- # |0 +---|--+ 1 -> dim 2 (X)
- # | / | /
- # |/ |/
- # 2 +------+ 3
- # /
- # dim 1 (Y)
-
- # isosurface perpendicular to dim 0 (Z)
- cube = numpy.array(
- (((0., 0.), (0., 0.)),
- ((1., 1.), (1., 1.))), dtype=numpy.float32)
- level = 0.5
- vertices, normals, indices = marchingcubes.MarchingCubes(
- cube, level, invert_normals=False)
- self.assertAllClose(vertices[:, 0], level)
- self.assertAllClose(normals, (1., 0., 0.))
- self.assertEqual(len(indices), 2)
-
- # isosurface perpendicular to dim 1 (Y)
- cube = numpy.array(
- (((0., 0.), (1., 1.)),
- ((0., 0.), (1., 1.))), dtype=numpy.float32)
- level = 0.2
- vertices, normals, indices = marchingcubes.MarchingCubes(cube, level)
- self.assertAllClose(vertices[:, 1], level)
- self.assertAllClose(normals, (0., -1., 0.))
- self.assertEqual(len(indices), 2)
-
- # isosurface perpendicular to dim 2 (X)
- cube = numpy.array(
- (((0., 1.), (0., 1.)),
- ((0., 1.), (0., 1.))), dtype=numpy.float32)
- level = 0.9
- vertices, normals, indices = marchingcubes.MarchingCubes(
- cube, level, invert_normals=False)
- self.assertAllClose(vertices[:, 2], level)
- self.assertAllClose(normals, (0., 0., 1.))
- self.assertEqual(len(indices), 2)
-
- # isosurface normal in dim1, dim 0 (Y, Z) plane
- cube = numpy.array(
- (((0., 0.), (0., 0.)),
- ((0., 0.), (1., 1.))), dtype=numpy.float32)
- level = 0.5
- vertices, normals, indices = marchingcubes.MarchingCubes(cube, level)
- self.assertAllClose(normals[:, 2], 0.)
- self.assertEqual(len(indices), 2)
-
- def test_sampling(self):
- """Test different sampling, comparing to reference without sampling"""
- isolevel = 0.5
- size = 9
- chessboard = numpy.zeros((size, size, size), dtype=numpy.float32)
- chessboard.reshape(-1)[::2] = 1 # OK as long as dimensions are odd
-
- ref_result = marchingcubes.MarchingCubes(chessboard, isolevel)
-
- samplings = [
- (2, 1, 1),
- (1, 2, 1),
- (1, 1, 2),
- (2, 2, 2),
- (3, 3, 3),
- (1, 3, 1),
- (1, 1, 3),
- ]
-
- for sampling in samplings:
- with self.subTest(sampling=sampling):
- sampling = numpy.array(sampling)
-
- data = 1e6 * numpy.ones(
- sampling * size, dtype=numpy.float32)
- # Copy ref chessboard in data according to sampling
- data[::sampling[0], ::sampling[1], ::sampling[2]] = chessboard
-
- result = marchingcubes.MarchingCubes(data, isolevel,
- sampling=sampling)
- # Compare vertices normalized with shape
- self.assertAllClose(
- ref_result.get_vertices() / ref_result.shape,
- result.get_vertices() / result.shape,
- atol=0., rtol=0.)
-
- # Compare normals
- # This comparison only works for normals aligned with axes
- # otherwise non uniform sampling would make different normals
- self.assertAllClose(ref_result.get_normals(),
- result.get_normals(),
- atol=0., rtol=0.)
-
- self.assertAllClose(ref_result.get_indices(),
- result.get_indices(),
- atol=0., rtol=0.)
-
-
-test_cases = (TestMarchingCubes,)
-
-
-def suite():
- test_suite = unittest.TestSuite()
- for test_class in test_cases:
- test_suite.addTests(
- unittest.defaultTestLoader.loadTestsFromTestCase(test_class))
- return test_suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest="suite")