/////////////////////////////////////////////////////////////////////////////////////////////////// // Facade to Apache Commons Math /////////////////////////////////////////////////////////////////////////////////////////////////// importClass(java.util.List) importClass(java.lang.Class) FastMath = Java.type('org.apache.commons.math3.util.FastMath') Pair = Java.type('org.apache.commons.math3.util.Pair') Complex = Java.type('org.apache.commons.math3.complex.Complex') DifferentiableUnivariateFunction = Java.type('org.apache.commons.math3.analysis.DifferentiableUnivariateFunction') Gaussian = Java.type('org.apache.commons.math3.analysis.function.Gaussian') HarmonicOscillator = Java.type('org.apache.commons.math3.analysis.function.HarmonicOscillator') DerivativeStructure = Java.type('org.apache.commons.math3.analysis.differentiation.DerivativeStructure') FiniteDifferencesDifferentiator = Java.type('org.apache.commons.math3.analysis.differentiation.FiniteDifferencesDifferentiator') SimpsonIntegrator = Java.type('org.apache.commons.math3.analysis.integration.SimpsonIntegrator') TrapezoidIntegrator = Java.type('org.apache.commons.math3.analysis.integration.TrapezoidIntegrator') RombergIntegrator = Java.type('org.apache.commons.math3.analysis.integration.RombergIntegrator') MidPointIntegrator = Java.type('org.apache.commons.math3.analysis.integration.MidPointIntegrator') PolynomialFunction = Java.type('org.apache.commons.math3.analysis.polynomials.PolynomialFunction') PolynomialFunctionLagrangeForm = Java.type('org.apache.commons.math3.analysis.polynomials.PolynomialFunctionLagrangeForm') LaguerreSolver = Java.type('org.apache.commons.math3.analysis.solvers.LaguerreSolver') UnivariateFunction = Java.type('org.apache.commons.math3.analysis.UnivariateFunction') SplineInterpolator = Java.type('org.apache.commons.math3.analysis.interpolation.SplineInterpolator') LinearInterpolator = Java.type('org.apache.commons.math3.analysis.interpolation.LinearInterpolator') NevilleInterpolator = Java.type('org.apache.commons.math3.analysis.interpolation.NevilleInterpolator') LoessInterpolator = Java.type('org.apache.commons.math3.analysis.interpolation.LoessInterpolator') DividedDifferenceInterpolator = Java.type('org.apache.commons.math3.analysis.interpolation.DividedDifferenceInterpolator') AkimaSplineInterpolator = Java.type('org.apache.commons.math3.analysis.interpolation.AkimaSplineInterpolator') GaussianCurveFitter = Java.type('org.apache.commons.math3.fitting.GaussianCurveFitter') PolynomialCurveFitter = Java.type('org.apache.commons.math3.fitting.PolynomialCurveFitter') HarmonicCurveFitter = Java.type('org.apache.commons.math3.fitting.HarmonicCurveFitter') WeightedObservedPoint = Java.type('org.apache.commons.math3.fitting.WeightedObservedPoint') MultivariateJacobianFunction = Java.type('org.apache.commons.math3.fitting.leastsquares.MultivariateJacobianFunction') LeastSquaresBuilder = Java.type('org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder') LevenbergMarquardtOptimizer = Java.type('org.apache.commons.math3.fitting.leastsquares.LevenbergMarquardtOptimizer') GaussNewtonOptimizer = Java.type('org.apache.commons.math3.fitting.leastsquares.GaussNewtonOptimizer') SimpleRegression = Java.type('org.apache.commons.math3.stat.regression.SimpleRegression') FastFourierTransformer = Java.type('org.apache.commons.math3.transform.FastFourierTransformer') DftNormalization = Java.type('org.apache.commons.math3.transform.DftNormalization') TransformType = Java.type('org.apache.commons.math3.transform.TransformType') ArrayRealVector = Java.type('org.apache.commons.math3.linear.ArrayRealVector') Array2DRowRealMatrix = Java.type('org.apache.commons.math3.linear.Array2DRowRealMatrix') MatrixUtils = Java.type('org.apache.commons.math3.linear.MatrixUtils') /////////////////////////////////////////////////////////////////////////////////////////////////// //Derivative and interpolation /////////////////////////////////////////////////////////////////////////////////////////////////// function get_values(f, xdata){ /* Return list of values of a function Args: f(UnivariateFunction): function xdata(float array or list): Domain values Returns: List of doubles */ v = [] for (var x in xdata){ v.push(f.value(xdata[x])) } return v } function interpolate(data, xdata, interpolation_type){ /* Interpolate data array or list to a UnivariateFunction Args: data(float array or list): The values to interpolate xdata(float array or list, optional): Domain values interpolation_type(str , optional): "linear", "cubic", "akima", "neville", "loess", "newton" Returns: UnivariateDifferentiableFunction object */ if (!is_defined(xdata)) xdata =null if (!is_defined(interpolation_type)) interpolation_type ="linear" if (xdata == null){ xdata = range(0, data.length, 1.0) } if ((data.length != xdata.length) || (data.length<2)){ throw "Dimension mismatch" } if (interpolation_type == "cubic"){ i = new SplineInterpolator() } else if (interpolation_type == "linear"){ i = new LinearInterpolator() } else if (interpolation_type == "akima"){ i = new AkimaSplineInterpolator() } else if (interpolation_type == "neville"){ i = new NevilleInterpolator() } else if (interpolation_type == "loess"){ i = new LoessInterpolator() } else if (interpolation_type == "newton"){ i = new DividedDifferenceInterpolator() }else{ throw "Invalid interpolation type" } return i.interpolate(to_array(xdata,'d'), to_array(data,'d')) } function deriv(f, xdata, interpolation_type){ /* Calculate derivative of UnivariateFunction, array or list. Args: f(UnivariateFunction or array): The function object. If array it is interpolated. xdata(float array or list, optional): Domain values to process. interpolation_type(str , optional): "linear", "cubic", "akima", "neville", "loess", "newton" Returns: List with the derivative values for xdata */ if (!is_defined(xdata)) xdata =null if (!is_defined(interpolation_type)) interpolation_type ="linear" if (! (f instanceof UnivariateFunction)){ if (xdata == null){ xdata = range(0, f.length, 1.0) } f = interpolate(f, xdata, interpolation_type) } if (xdata == null){ if (f instanceof DifferentiableUnivariateFunction){ return f.derivative() } throw "Domain range not defined" } d = [] for (var x in xdata){ var xds = new DerivativeStructure(1, 2, 0, x) var yds = f.value(xds) d.push( yds.getPartialDerivative(1)) } return d } function integrate(f, range, xdata , interpolation_type , integrator_type){ /* Integrate UnivariateFunction, array or list in an interval. Args: f(UnivariateFunction or array): The function object. If array it is interpolated. range(list, optional): integration range ([min, max]). xdata(float array or list, optional): disregarded if f is UnivariateFunction. interpolation_type(str , optional): "linear", "cubic", "akima", "neville", "loess", "newton" integrator_type(str , optional): "simpson", "trapezoid", "romberg" or "midpoint" Returns: Integrated value (Float) */ if (!is_defined(range)) range =null if (!is_defined(xdata)) xdata =null if (!is_defined(interpolation_type)) interpolation_type ="linear" if (!is_defined(integrator_type)) integrator_type ="simpson" if (! (f instanceof UnivariateFunction)){ if (xdata == null){ xdata = range(0, f.length, 1.0) } if (range == null){ range = xdata } f = interpolate(f, xdata, interpolation_type) } if (range == null){ throw "Domain range not defined" } d = [] if (integrator_type == "simpson"){ integrator = new SimpsonIntegrator() } else if (integrator_type == "trapezoid"){ integrator = new TrapezoidIntegrator() } else if (integrator_type == "romberg"){ integrator = new RombergIntegrator() } else if (integrator_type == "midpoint"){ integrator = new MidPointIntegrator() throw "Invalid integrator type" } max_eval = 1000000 lower = Math.min.apply(null, range) upper = Math.max.apply(null, range) return integrator.integrate(max_eval, f, lower, upper) } /////////////////////////////////////////////////////////////////////////////////////////////////// //Fitting and peak search /////////////////////////////////////////////////////////////////////////////////////////////////// MAX_FLOAT = 1.7976931348623157e+308 MAX_ITERATIONS = 1000 MAX_EVALUATIONS = 1000 function calculate_peaks(func, start_value, end_value, positive){ /* Calculate peaks of a DifferentiableUnivariateFunction in a given range by finding the roots of the derivative Args: function(DifferentiableUnivariateFunction): The function object. start_value(float): start of range end_value(float, optional): end of range positive (boolean, optional): True for searching positive peaks, False for negative. Returns: List of peaks in the interval */ if (!is_defined(end_value)) end_value =MAX_FLOAT if (!is_defined(positive)) positive =true derivative = func.derivative() derivative2 = derivative.derivative() var peaks = [] solver = new LaguerreSolver() var ret = solver.solveAllComplex(derivative.coefficients, start_value) for (var complex in ret){ var r = ret[complex].getReal() if ((start_value < r) && (r < end_value)){ if ((positive && (derivative2.value(r) < 0)) || ( (!positive) && (derivative2.value(r) > 0))) peaks.push(r) } } return peaks } function estimate_peak_indexes(data, xdata, threshold, min_peak_distance, positive){ /* Estimation of peaks in an array by ordering local maxima according to given criteria. Args: data(float array or list) xdata(float array or list, optional): if not null must have the same length as data. threshold(float, optional): if specified filter peaks below this value min_peak_distance(float, optional): if specified defines minimum distance between two peaks. if xdata == null, it represents index counts, otherwise in xdata units. positive (boolean, optional): True for searching positive peaks, False for negative. Returns: List of peaks indexes. */ if (!is_defined(xdata)) xdata =null if (!is_defined(threshold)) threshold =null if (!is_defined(min_peak_distance)) min_peak_distance =null if (!is_defined(positive)) positive =true peaks = [] indexes = sort_indexes(data, positive) for (var index in indexes){ first = (indexes[index] == 0) last = (indexes[index] == (data.length-1)) val=data[indexes[index]] prev = first ? Number.NaN : data[indexes[index]-1] next = last ? Number.NaN : data[indexes[index]+1] if (threshold != null){ if ((positive && (valthreshold))) break } if ( ( (positive) && (first || val>prev ) && (last || val>=next ) ) || ( (!positive) && (first || valfy.length))) throw "Invalid data for fit" } function fit_gaussians(fy, fx, peak_indexes){ /* Fits data on multiple gaussians on the given peak indexes. Args: x(float array or list) y(float array or list) peak_indexes(list of int) Returns: List of tuples of gaussian parameters: (normalization, mean, sigma) */ fx = to_array(fx) fy = to_array(fy) _assert_valid_for_fit(fy,fx) ret = [] minimum = Math.min.apply(null, fy) for (var peak in peak_indexes){ //Copy data data = fy.slice(0) //Remover data from other peaks for (var p in peak_indexes){ limit = Math.floor(Math.round((peak_indexes[p]+peak_indexes[peak])/2)) if (peak_indexes[p] > peak_indexes[peak]){ for (var x = limit; x< fy.length; x++){ data[x] = minimum } } else if (peak_indexes[p] < peak_indexes[peak]){ for (var x = 0; x< limit; x++){ data[x] = minimum } } } //Build fit point list values = create_fit_point_list(data, fx) maximum = Math.max.apply(null, data) gaussian_fitter = GaussianCurveFitter.create().withStartPoint([(maximum-minimum)/2,fx[peak_indexes[peak]],1.0]).withMaxIterations(MAX_ITERATIONS) //Fit return parameters: (normalization, mean, sigma) try{ ret.push(to_array(gaussian_fitter.fit(values))) } catch(ex) { ret.push(null) //Fitting error } } return ret } function create_fit_point_list(fy, fx, weights){ if (!is_defined(weights)) weights =null values = [] for (var i = 0; i< fx.length; i++) if (weights == null){ values.push(new WeightedObservedPoint(1.0, fx[i], fy[i])) } else { values.push(new WeightedObservedPoint(weights[i], fx[i], fy[i])) } return values } function fit_polynomial(fy, fx, order, start_point, weights){ /* Fits data into a polynomial. Args: x(float array or list): observed points x y(float array or list): observed points y order(int): if start_point is provided order parameter is disregarded - set to len(start_point)-1. start_point(optional tuple of float): initial parameters (a0, a1, a2, ...) weights(optional float array or list): weight for each observed point Returns: Tuples of polynomial parameters: (a0, a1, a2, ...) */ if (!is_defined(start_point)) start_point =null if (!is_defined(weights)) weights =null _assert_valid_for_fit(fy,fx) fit_point_list = create_fit_point_list(fy, fx, weights) if (start_point == null){ polynomial_fitter = PolynomialCurveFitter.create(order).withMaxIterations(MAX_ITERATIONS) } else { polynomial_fitter = PolynomialCurveFitter.create(0).withStartPoint(start_point).withMaxIterations(MAX_ITERATIONS) } try{ return to_array(polynomial_fitter.fit(fit_point_list)) } catch(ex) { throw "Fitting failure" } } function fit_gaussian(fy, fx, start_point, weights){ /* Fits data into a gaussian. Args: x(float array or list): observed points x y(float array or list): observed points y start_point(optional tuple of float): initial parameters (normalization, mean, sigma) weights(optional float array or list): weight for each observed point Returns: Tuples of gaussian parameters: (normalization, mean, sigma) */ if (!is_defined(start_point)) start_point =null if (!is_defined(weights)) weights =null _assert_valid_for_fit(fy,fx) fit_point_list = create_fit_point_list(fy, fx, weights) //If start point not provided, start on peak if (start_point == null){ peaks = estimate_peak_indexes(fy, fx) minimum = Math.min.apply(null, fy) maximum = Math.max.apply(null, fy) start_point = [(maximum-minimum)/2,fx[peaks[0]],1.0] } gaussian_fitter = GaussianCurveFitter.create().withStartPoint(start_point).withMaxIterations(MAX_ITERATIONS) try{ return to_array(gaussian_fitter.fit(fit_point_list)) // (normalization, mean, sigma) } catch(ex) { throw "Fitting failure" } } function fit_harmonic(fy, fx, start_point, weights){ /* Fits data into an harmonic. Args: x(float array or list): observed points x y(float array or list): observed points y start_point(optional tuple of float): initial parameters (amplitude, angular_frequency, phase) weights(optional float array or list): weight for each observed point Returns: Tuples of harmonic parameters: (amplitude, angular_frequency, phase) */ if (!is_defined(start_point)) start_point =null if (!is_defined(weights)) weights =null _assert_valid_for_fit(fy,fx) fit_point_list = create_fit_point_list(fy, fx, weights) if (start_point == null){ harmonic_fitter = HarmonicCurveFitter.create().withMaxIterations(MAX_ITERATIONS) } else { harmonic_fitter = HarmonicCurveFitter.create().withStartPoint(start_point).withMaxIterations(MAX_ITERATIONS) } try{ return to_array(harmonic_fitter.fit(fit_point_list)) // (amplitude, angular_frequency, phase) } catch(ex) { throw "Fitting failure" } } /////////////////////////////////////////////////////////////////////////////////////////////////// //Least squares problem /////////////////////////////////////////////////////////////////////////////////////////////////// function optimize_least_squares(model, target, initial, weights){ if (is_array(weights)){ weights = MatrixUtils.createRealDiagonalMatrix(weights) } problem = new LeastSquaresBuilder().start(initial).model(model).target(target).lazyEvaluation(false).maxEvaluations(MAX_EVALUATIONS).maxIterations(MAX_ITERATIONS).weight(weights).build() optimizer = new LevenbergMarquardtOptimizer() optimum = optimizer.optimize(problem) parameters=to_array(optimum.getPoint().toArray()) residuals = to_array(optimum.getResiduals().toArray()) rms = optimum.getRMS() evals = optimum.getEvaluations() iters = optimum.getIterations() return [parameters, residuals, rms, evals, iters] } /////////////////////////////////////////////////////////////////////////////////////////////////// //FFT /////////////////////////////////////////////////////////////////////////////////////////////////// function is_power_of_2(n){ return n && (n & (n - 1)) === 0; } function bit_length(num) { return num.toString(2).length } function is_complex(v) { return v instanceof Complex } function pad_to_power_of_two(data){ if (is_power_of_2(data.length)){ return data } pad =(1 << bit_length(data.length)) - data.length elem = is_complex(data[0]) ? new Complex(0,0) : [0.0,] for (var i=0; i