// Copyright (c) CNES 2008 // // This software is part of CelestLab, a CNES toolbox for Scilab // // This software is governed by the CeCILL license under French law and // abiding by the rules of distribution of free software. You can use, // modify and/ or redistribute the software under the terms of the CeCILL // license as circulated by CEA, CNRS and INRIA at the following URL // 'http://www.cecill.info'. function [xres, yres] = CL__detectZero(x, y, fct, args, direct, ytol, xbounds, nsub, meth, impr) // Zero crossings computation (low level) // // Calling Sequence // [xres, yres] = CL__detectZero(x, y, fct, args, direct, ytol, xbounds, nsub, meth, impr) // // Description // //

Computes zero crossings, that is abscissas x such that y(x) = 0.

//

x and y are 2 row vectors. If y is empty => computed. y can be computed by:

//

y = fct(x, ind, args) [ind unused).

//

//

The other arguments are:

//

- direct: Specifies the direction of y: 1=increasing, -1=decreasing, 0=both (default).

//

- ytol: Desired accuracy on f(x).

//

- meth: Method used to finely compute the zeros of y(x): "s": secant, "d": dichotomy, "ds": mix of // secant and dichotomy (see CL_fsolveb).

//

- impr: True (%t) if the improved detection mechanism is active.

//

//

The method consists in selecting the abscissas for which the sign of y changes, and then computing a more // exact value using CL_fsolveb. If the improved detection mechanism is active, the function first detects when // y reaches a extremum, which improves the zero detection capability. The improved detection works however if the // function is twice differentiable.

//
//

// // //

Note:

//

- The abscissae x must be strictly increasing.

//

- The results may be degraded is the data are not continuous.

//

// // Parameters // x: Abscissae. (1xN) // y: Ordinates, or []. (1xN) // fct: y = fct(x, ind, args). // args: (any type) argument for fct. // direct: 1=increasing, -1=decreasing, 0=both. // ytol: Accuracy on ordinates or empty (empty <=> default value). // xbounds: Abscissae intervals. (2xQ) // nsub: (integer) Number of sub-division points (>= 0). // meth: (string) Method for zero computation: "s", "d" or "ds". // impr: (boolean) True if improved detection mechanism is selected. // xres: Abscissae of solutions (1xP) // yres: Ordinates of solutions (should be close to zero) (1xP) // // Authors // CNES - DCT/SB // // See also // CL_detectSign // CL_fsolveb // // Examples // x = linspace(0, 10, 30); // // function [y] = myfunction(x,ind,args) // y = sin(x) - a; // endfunction // // args = 0.99; // direct = "any"; // ytol = 1.e-6; // xbounds = [-%inf, %inf]; // nsub = 0; // meth = "ds"; // impr = %t; // xres = CL_detectZero(x, [], myfunction, args, direct, ytol, xbounds, nsub, meth, impr); // ================================================= // Declarations // ================================================= // factor for computation of ytol ATOL_FACTOR = 1.e-8; RTOL_FACTOR = 1.e-4; // not too small to avoid non-convergence // generate simulation abscissae => in xbounds (including margin) // at least 2 points added at beginning/end of each interval (for CL__detectLocExtr) // %nan inserted between sequences => avoid detection of "false" extrema (in CL__detectLocExtr) or zeros // interv: no intersection between one another and assumed included in [xref(1), xref($)] // xref: reference abscissae (1xN) // interv: abscissa intervals (2xP) // nsub: number of added intermediate abscissae (1x1) function [x] = detectZero_genx(xref, interv, nsub) N = size(xref,2); // i1: min index such that: interv(2,:) <= xref(i1) i1 = dsearch(interv(1,:), xref) + 1; // => in 2 .. N I = find(interv(1,:) == xref(i1-1)); if (I <> []); i1(I) = i1(I) - 1; end // i2: max index such that: xref(i2) <= interv(1,:) i2 = dsearch(interv(2,:), xref); // => in 1 .. N-1 I = find(interv(2,:) == xref(i2+1)); if (I <> []); i2(I) = i2(I) + 1; end nmargin = 2; if (nsub > 0); nmargin = 1; end ind_interv = CL_intervUnion([max(1, i1 - nmargin); min(N, i2 + nmargin)]); ind = zeros(1, N+1); ind(ind_interv(1,:)) = 1; ind(ind_interv(2,:) + 1) = -1; indcum = cumsum(ind); x = %nan * ones(ind); I = find(indcum > 0); x(I) = xref(I); I = find(indcum > 0 | ind == -1); x = x(I); if (size(x, 2) >= 2 & nsub > 0) x_mat = CL_intervLinspace([x(1:$-1); x(2:$)], 2+nsub); x = [matrix(x_mat(1:$-1,:),1,-1), x_mat($,$)]; I = find(isnan(x(1:$-1)) & isnan(x(2:$))); x(I) = []; end endfunction // select values in interv // interv: no intersection between one another function [xres, yres] = detectZero_selectInterv(xres, yres, interv) val = matrix(interv, 1, -1); ind = dsearch(xres, val); I = find(ind <> 0 & modulo(ind, 2) == 0 & xres == val(ind+1)); if (I <> []); ind(I) = ind(I) + 1; end I = find(ind <> 0 & modulo(ind, 2) == 1); xres = xres(I); yres = yres(I); endfunction // ------------------------------------------------- // Arguments Checking // ------------------------------------------------- if (argn(2) <> 10) CL__error("Invalid number of input arguments (10 expected)"); end if (direct <> round(direct) | abs(direct) > 1) CL__error("Invalid value for argument direct"); end if (typeof(ytol) <> "constant" | size(ytol,"*") > 1) CL__error("Invalid size for argument ytol"); end // xbounds: 0 or 2 rows if (size(xbounds,1) <> 2 & size(xbounds,1) <> 0) CL__error("Invalid size for argument xbounds"); end if (nsub <> round(nsub) | nsub < 0) CL__error("Invalid value for argument nsub"); end if (meth <> "s" & meth <> "d" & meth <> "ds") CL__error("Invalid value for argument meth"); end if (typeof(impr) <> "boolean") CL__error("Invalid type for argument impr"); end if (size(x,1) > 1 | size(y,1) > 1 | (size(x,2) <> size(y,2) & y <> [])) CL__error("Invalid size for argument x or y"); end // Check x is strictly increasing if (size(x, 2) >= 2) if (find(x(2:$) - x(1:$-1) <= 0) <> []) CL__error("Invalid abscissas: not strictly increasing"); end end // check there is no Nan value if (find(isnan(x)) <> [] | find(isnan(y)) <> []) CL__error("Invalid data (data contain Nan values)"); end // ------------------------------------------------- // Computation // ------------------------------------------------- xres = []; yres = []; // make xbounds in [x(1), x($)) + eliminates intersections // NB: includes validation of xbounds (beginning <= end) xbounds = CL_intervInters([x(1); x($)], CL_intervUnion(xbounds)); // special case: x contains 0 or 1 value or xbounds is [] if (size(x, 2) <= 1 | xbounds == []) return; // <= RETURN end // computes abscissae if necessary (in xbounds, including margin) // => x may include Nan values if (xbounds(1,1) > x(1) | xbounds(2,$) < x($) | nsub <> 0) x = detectZero_genx(x, xbounds, nsub); y = []; // forces recomputation of y end // computes y if empty // if x is %nan => y = %nan; if (y == []) y = %nan * ones(x); I = find(~isnan(x)); if (I <> []); y(I) = fct(x(I), [], args); end end // computes ytol if not initialized if (ytol == []) ytol = min(max(abs(y)) * ATOL_FACTOR, (max(y)-min(y)) * RTOL_FACTOR); end // ------------------------------------------------- // Zero detection // ------------------------------------------------- // if improved detection: update data (local extrema) if (impr & size(x, 2) >= 3) n = 5; dytol = ytol / 5; [xextr, yextr, I] = CL__detectLocExtr(x, y, fct, args, dytol, n); if (I <> []) x(I) = xextr; y(I) = yextr; end end if (direct == 1) // increasing I = find(y(1:$-1) .* y(2:$) <= 0 & y(1:$-1) < y(2:$)); elseif (direct == -1) // decreasing I = find(y(1:$-1) .* y(2:$) <= 0 & y(1:$-1) > y(2:$)); else // if (direct == 0) I = find(y(1:$-1) .* y(2:$) <= 0 & y(1:$-1) <> y(2:$)); end if (I <> []) [xres, yres] = CL_fsolveb(fct, x(I), x(I+1), args, ytol=ytol, meth=meth, y1=y(I), y2=y(I+1)); end // keep solution in xbounds only [xres, yres] = detectZero_selectInterv(xres, yres, xbounds); endfunction