public LModeExt(LMode mode, double remoteLeft, double remoteRight) : base(mode.X, mode.H, mode.Found) { this.Remote = Tools.Combine(remoteLeft, remoteRight); }
} //# end of max.fastTrack internal LModeExt GetPoints() { //# Arguments: //# ---------------------------------------------- //# o: same nature as o in dens.gen.icdf arguments //# start: EITHER a vector of length 2 [two potential starting points] //# or a scalar //# f.ratio.remote: scalar //# epsilon: scalar //# range: vector of length 2 //# -------------------------------------------------------------------------- //# If range is finite and log.f not inestimable at lower end, //# then we first look for values at both ends before searching for a mode //mode = list(x = numeric(0), h = numeric(0), found = false) # modif_0.11 LMode mode = new LMode(); if ((this.Range[0].IsFinite() && this.Range[1].IsFinite() && !this.InestimableLowerLimit)) { double[] logFPrime = new double[] { this.O.LogFPrime(this.Range[0], A), this.O.LogFPrime(this.Range[1], A) }; //# If slope (f') is the same at both ends, then there is no mode between the 2 endpoints (assuming a unimodal distrn) //# and we then assume that the highest end value for log.f is hence the local mode double[] logFPrimeSign = new double[] { Math.Sign(logFPrime[0]), Math.Sign(logFPrime[1]) }; if (logFPrime.Prod() > 0) { double[] logF = new double[] { this.O.LogF(this.Range[0], A), this.O.LogF(this.Range[1], A) }; int w = (logF[0] >= logF[1]) ? 0 : 1; mode = new LMode(w == 0 ? this.Range[0] : this.Range[1], logF[w], true); } } // if ((this.Range[0] SecuredNRSearch nr = null; double startingPoint; if (!mode.Found) { //# Choose a starting point among suggested starting points, if more than one point was suggested if (this.Start.Length > 1) { this.Start = Tools.Combine(this.Start, this.Start.Mean()); //# Add the middle point to the two potential starting points double[] logF = new double[this.Start.Length]; for (int i = 0; i < this.Start.Length; i++) { logF[i] = this.O.LogF(this.Start[i], A); } //double[] logF = new double[] { this.O.LogF(this.Start[0], A), this.O.LogF(this.Start[1], A), this.O.LogF(this.Start.Mean(), A) }; //TODO peut planter startingPoint = this.Start[logF.WhichMax()]; } //if (this.Start.Length > 1) else { startingPoint = this.Start[0]; } //# Find mode by Newton-Raphson SecuredNRA oMode = new SecuredNRA(this.O.LogFPrime, this.O.LogFSecond, this.Range); nr = new SecuredNRSearch(oMode, A, startingPoint, this.Epsilon, this.Range); // 4 derniers paramètres prennent les valeurs par défaut: // maxPoint = null, target = 0, inestimableLowerLimit = false, expectedHpSign = -1 if (nr.Converged) { double hs = this.O.LogFSecond(nr.X, A); if (hs > 0) { //# we have found a mimimum point (between two local modes): //# we redo the search on both sides of this dip mode.X = HigherLocalMax(this.O, A, nr.X, this.Range, this.Epsilon); } //if (hs > 0) else { mode.X = nr.X; } //if (hs > 0) mode.Found = true; } //if (nr.Converged) else { double[] xA = nr.Bounds.X; double[] hA = nr.Bounds.H; bool hOnBothSides = hA.Aggregate(1, (x, y) => x * Math.Sign(y)) < 0; double diffX = xA.Diff()[0]; if ((diffX < this.Epsilon) && hOnBothSides) { mode.X = xA.Mean(); mode.Found = true; } //if ((diffX < this.Epsilon) && hOnBothSides) else { bool converged = false; if ((nr.Bounds.X.Diff()[0] < this.Epsilon) && nr.Bounds.H.All(a => a < 0)) { if (this.Range[0].IsFinite()) { //# We try a little bit further to find the actual mode, //# since it looks like we are almost there... converged = true; MaxFastTrackObject tmp = MaxFastTrack(this.O, A, this.Range[1], nr.Bounds.X[0], nr.Bounds.H[0]); //# yes: 4th argument is bounds$h (and not hp!) if (tmp.Converged) { mode.X = tmp.X; mode.Found = true; } //if (tmp.Converged) else { mode.X = this.Range[1]; mode.Found = !this.InestimableLowerLimit; } //if (tmp.Converged) } //if (this.Range[0].IsFinite()) else { double x = nr.Bounds.X[0]; double h = nr.Bounds.H[0]; double tmp = Math.Abs(h / this.O.LogFSecond(x, this.A)); if (tmp < 1e-12) { mode = new LMode(x, h, true); converged = true; } //if (tmp < 1e-12) } //if (this.Range[0].IsFinite()) } if (!converged) { throw new WEException("Algorithm did not converge."); } //if (!converged) } //if ((diffX < this.Epsilon) && hOnBothSides) } //if (nr.Converged) if (mode.Found) { mode.H = this.O.LogF(mode.X, this.A); } } //if (!mode.Found) double logFRatioRemote = Math.Log(this.FRatioRemote);// # new_0.11 double target = Tools.ND; // R n'a pas vraiment de notion de scope if (mode.Found) { target = mode.H + logFRatioRemote; } this.A.Mode = mode.X; // # some 'remote.*' functions need that information //# modif_0.11 //# Find remote points on both sides of mode double remoteLeft = 0, remoteRight = 0; bool[] modeOnBorder = new bool[2]; double fs = Tools.ND; if (mode.Found) { //TODO pourquoi la définition et l'affectation de target ne serait-elle pas ici? double[] x = new double[] { Tools.NA, Tools.NA }; double[] f = new double[] { Tools.NA, Tools.NA }; double[] fp = new double[] { Tools.NA, Tools.NA }; fs = this.O.LogFSecond(mode.X, A);// # scalar double d = 10 / Math.Abs(fs); //# new_0.11 double[] remoteStart = this.O.LogFInvRemote(target, this.A).ToArray(); // # vector // TODO start est un paramètre de la fonction en R, vérifier l'impact de modifier la référence d'objet ici. this.Start = SplitStartingValuesLeftRight(this.O, target, A, this.Range, remoteStart); // # vector of length 2 modeOnBorder = new bool[2] { this.Range[0] == mode.X, this.Range[1] == mode.X }; //# new_0.11 List <int> jSeq = new List <int>(); for (int i = 0; i < 2; i++) { if (!modeOnBorder[i] && this.Start[i].IsNA()) { jSeq.Add(i); } } foreach (int j in jSeq) { //# modif_0.11 (first block that appeared here was moved above) // condition inutile if (Tools.IsNA(this.Start[j])) { int dir = (j == 0) ? -1 : 1; double tmp = mode.X + dir * d; bool accepted = (tmp > this.Range[0]) && (tmp < this.Range[1]); if (!accepted) { double myD = d; while (!accepted) { myD = myD / 2; tmp = mode.X + dir * myD; accepted = (tmp > this.Range[0]) && (tmp < this.Range[1]); } } fp[0] = this.O.LogFPrime(tmp, A); accepted = fp[0].IsFinite(); while (!accepted) { tmp = (tmp + mode.X) / 2; fp[0] = this.O.LogFPrime(tmp, A); accepted = fp[0].IsFinite(); } double a = fp[0] / d; // # estimated rate of slope acceleration double d2 = Math.Sqrt(2 * Math.Abs(Math.Log(this.FRatioRemote) / a)); // # estimated distance we need to get from mode, //# at the rate above, to reach the f.ratio.remote distance double x2 = mode.X + dir * d2; accepted = (x2 > this.Range[0]) && (x2 < this.Range[1]); while (!accepted) { d2 = d2 / 2; x2 = mode.X + dir * d2; accepted = x2 > this.Range[0] & x2 < this.Range[1]; } double fp2 = this.O.LogFPrime(x2, A); accepted = fp2.IsFinite(); while (!accepted) { d2 = d2 / 2; x2 = mode.X + dir * d2; fp2 = this.O.LogFPrime(x2, A); accepted = Tools.IsFinite(fp2); } x = new double[2] { tmp, x2 }; f = new double[2] { Tools.NA, Tools.NA }; for (int i = 0; i < 2; i++) { f[i] = this.O.LogF(x[i], A); } fp[1] = fp2; //# Approximating the shape of log.f.prime by a straight line, //# we can estimate the point where the distance //# prescribed by f.ratio.remote is reached double[,] xSolved = Matrix2x2.Solve2x2(new double[] { x[0], x[1], 1, 1 }, byCol: true); // # 2 x 2 matrix double[] theta = Matrix2x2.Product(xSolved, fp); double qA = theta[0]; double qB = theta[1]; // ifelse(xor(j==1, x[1]>x[2]), 2, 1) int innerSide = (j == 0) ^ (x[0] > x[1]) ? 1 : 0; double innerX = x[innerSide]; double qC = -qA *Math.Pow(innerX, 2) - qB * innerX - (target - f[innerSide]); double l; double u; if (j == 0) { l = this.Range[0]; u = mode.X; } else { l = mode.X; u = this.Range[1]; } List <double> roots = QuadraticEquation.GetRealRoots(new double[] { qC, qB, qA }, l: l, u: u, ROrder: true);// # modif_0.11 switch (roots.Count) { case 0: this.Start[j] = x[1 - innerSide]; break; case 1: this.Start[j] = roots[0]; break; default: if (j == 1) { this.Start[j] = roots.Max(); } else { this.Start[j] = roots.Min(); } break; } } } //# new_0.11 //# Suggest another starting point (on both sides) //# based on the 2nd degree polynomial approximation (Taylor series devpmt) of log.f f = new double[0]; if (!modeOnBorder.Any(pX => pX)) { double delta = Math.Sqrt(2 * logFRatioRemote / fs); double[] altStart; //# Left double xScalar = mode.X - delta; if (xScalar > this.Range[0]) { altStart = new double[] { this.Start[0], xScalar }; f = new double[] { Math.Abs(this.O.LogF(altStart[0], A) - target), Math.Abs(this.O.LogF(altStart[1], A) - target) }; IEnumerable <double> lstD = f.Substract(target).Abs(); int w = (lstD.First() < lstD.Last()) ? 0 : 1; this.Start[0] = altStart[w]; } //# Right side xScalar = mode.X + delta; if (xScalar < this.Range[1]) { altStart = new double[] { this.Start[1], xScalar }; f = new double[] { Math.Abs(this.O.LogF(altStart[0], A) - target), Math.Abs(this.O.LogF(altStart[1], A) - target) }; IEnumerable <double> lstD = f.Substract(target).Abs(); int w = (lstD.First() < lstD.Last()) ? 0 : 1; this.Start[1] = altStart[w]; } } //oh <- list(h=o$log.f, hp=o$log.f.prime, hs=o$log.f.second) SecuredNRA oh = new SecuredNRA(this.O.LogF, this.O.LogFPrime, this.O.LogFSecond);// # modif_0.11 if (Tools.IsNA(this.Start[0])) { remoteLeft = mode.X; } else { double[] range = new double[] { this.Range[0], mode.X }; //LMaxPoint lm = new LMaxPoint(mode); nr = new SecuredNRSearch(oh, A, this.Start[0], this.Epsilon, range, maxPoint: mode, target: target, inestimableLowerLimit: this.InestimableLowerLimit); // le dernier paramètre a une valeur par défaut: expectedHpSign = -1 if (nr.Converged) { remoteLeft = nr.X; } else if (this.Range[0].IsFinite()) { remoteLeft = this.Range[0]; } else { //TODO Exception throw new WEException("Fin temporaire!"); } } if (Tools.IsNA(this.Start[1])) { remoteRight = mode.X; } // if else { this.Range = new double[] { mode.X, this.Range[1] }; nr = new SecuredNRSearch(oh, A, this.Start[1], this.Epsilon, this.Range, maxPoint: mode, target: target); // les 2 derniers paramètres prennent des valeurs par défaut: // inestimableLowerLimit = false, expectedHpSign = -1 if (nr.Converged) { remoteRight = nr.X; } else { throw new WEException("Algorithm did not converge.");// # should not happen } } } else { //# new_0.11 //# mode was not found remoteLeft = Tools.NA; remoteRight = Tools.NA; } //list(mode = mode, remote = c(remote.left, remote.right)) LModeExt lmext = new LModeExt(mode, remoteLeft, remoteRight); return(lmext); } //# end of ref.points
internal SecuredNRSearch( ISecuredNR o, SGNFnAParam A, double start, double epsilon, double[] range, LMode maxPoint = null, double target = 0, bool inestimableLowerLimit = false, // int maxNIter = 500, int expectedHpSign = -1) //max.point=list(x= numeric(0), h= numeric(0)), { //# Throughout this function, 'bounds' is a list with the following elements/dimensions: //# x, h, hp, range: numeric (vectors of length 2) //# higherSide: numeric (1) //# IncludeSoln: logical (1) //TODO l'accès à h2Modeless est difficile à comprendre en R qui permet l'accès à des variables non initialisé ... quitte à planter! //pour ce faire on introduit ici h2Modeless ... //-->secured.NR.search ,start=9.24215468292111 ,target=0 ,range=( 0, Inf ) ,inestimable.lower.limit=FALSE ,max.point$x= ,max.point$h= ,expected.hpsign=-1 double h2Modeless = Tools.NA; if (maxPoint == null) { maxPoint = new LMode(); } bool modeSearch = Tools.IsNA(maxPoint.X); bool monotonic = !modeSearch; LLimit limit = new LLimit(); limit.X = range.Copy(); // les autres champs prennent les valeurs par défaut désirée limit.Chckd[0] = inestimableLowerLimit; //# new_0.11 Bounds bounds = new Bounds( Tools.Rep(Tools.NA, 2), //x Tools.Rep(Tools.NA, 2), //h Tools.Rep(Tools.NA, 2), //hp null, // range -1, // higherSide false); // includeSoln) bool found2Bounds; bool correct4HpSign; int higherSide; if (!Tools.IsNA(maxPoint.X)) { higherSide = (start < maxPoint.X) ? 1 : 0; // higherSide est un indice bounds.X[higherSide] = maxPoint.X; bounds.H[higherSide] = maxPoint.H; bounds.Hp[higherSide] = 0.0; found2Bounds = true; expectedHpSign = higherSide == 0 ? -1 : 1; correct4HpSign = start > maxPoint.X; h2Modeless = (maxPoint.H * 2.0) - target; } else { found2Bounds = false; higherSide = 0; correct4HpSign = true; } int lowerSide = 1 - higherSide; double x = start; //# make sure that x is not out of range if (x < limit.X[0]) { x = (maxPoint.X + limit.X[0]) / 2; } bounds.Range = range.Copy(); bounds.HigherSide = higherSide; //# Register initial x into bounds double h = o.H(x, A); double hp = o.HPrime(x, A); int side; if (!Tools.IsNA(maxPoint.X)) { bounds.IncludeSoln = h < target; side = lowerSide; } else { side = 0; } bounds.X[side] = x; bounds.H[side] = h; bounds.Hp[side] = hp; Visits visitedX = new Visits(SecuredNRSearch.MaxNIter); visitedX.Add(x); //# Do a first step bool cntn; if (correct4HpSign) { hp = expectedHpSign * Math.Abs(hp); } double change = (h - target) / hp; x = x - change; //# cntn until convergence bool unreachableMode = false; cntn = true; int count = 0; bool converged = false; int debCount = 0; while (cntn) { debCount++; if (x.IsNaN()) { if (1 == 1) { } } bool computedH = false; bool accepted = false; count = count + 1; LWhere wb; // = WithinBounds(x, bounds); while (!accepted) { wb = WithinBounds(x, bounds); if (bounds.IncludeSoln) { if (wb.Within) { accepted = true; } else { x = CubicExtrapolation.Extrapolate(target, bounds, monotonic, range); accepted = !Tools.IsNA(x); } } else if (wb.Above) { //# bounds do not include solution and we are looking above bounds if (x > limit.X[1]) { if (limit.Chckd[1]) { accepted = true; h = limit.H[1]; if (UnreachedTarget(target, h, higherSide, monotonic, modeSearch)) { //# force end of while-loop, as target is not reached even at this end x = limit.X[1]; h = target; computedH = true; } else if (limit.Usable[1]) { x = bounds.Range[1]; hp = limit.Hp[1]; computedH = true; } else { x = (x + change + bounds.Range[1]) / 2; } } else { limit.Chckd[1] = true; h = o.H(limit.X[1], A); if (UnreachedTarget(target, h, higherSide, monotonic, modeSearch)) { //# force end of while-loop, as target is not reached even at this end x = limit.X[1]; h = target; accepted = true; computedH = true; } else { limit.H[1] = h; if (h.IsFinite()) { hp = o.HPrime(limit.X[1], A); limit.Hp[1] = hp; limit.Usable[1] = hp.IsFinite(); accepted = limit.Usable[1]; } else { accepted = false; } if (accepted) { x = bounds.Range[1]; computedH = true; unreachableMode = modeSearch && h > 0;//# new_0.11 } else { x = (x + change + bounds.Range[1]) / 2; } } } } else { ParamB01 tmp = NewBound(x, bounds, monotonic, o, A, target, range, wb.Above); x = tmp.X; h = tmp.H; hp = tmp.Hp; computedH = true; accepted = true; } } else { //# bounds do not include solution and we are looking below bounds if (x <= limit.X[0]) { //# we are looking out of the variable domain (bad!) if (!limit.Chckd[0]) { limit.Chckd[0] = true; h = o.H(limit.X[0], A); limit.H[0] = h; if (h.IsFinite()) { if (UnreachedTarget(target, h, higherSide, monotonic, modeSearch, leftSide: true)) { //# force end of while-loop, as target is not reached even at this end x = limit.X[0]; h = target; accepted = true; } else { hp = o.HPrime(limit.X[0], A); limit.Hp[0] = hp; limit.Usable[0] = hp.IsFinite(); accepted = limit.Usable[0]; } computedH = accepted; if (accepted) { x = limit.X[0]; } } else { limit.Usable[0] = false; x = (bounds.X.Where(a => !Tools.IsNA(a)).Min() + limit.X[0]) / 2; } } else if (limit.Usable[0]) { x = limit.X[0]; h = limit.H[0]; hp = limit.Hp[0]; computedH = true; accepted = true; } else { //# not limit$usable[1] x = (bounds.X.Min() + limit.X[0]) / 2; } } else { ParamB01 tmp = NewBound(x, bounds, monotonic, o, A, target, range, wb.Above); x = tmp.X; h = tmp.H; hp = tmp.Hp; computedH = true; accepted = true; // # new_0.11 unreachableMode = modeSearch && (((x == limit.X[1]) && (h > 0)) || ((x == limit.X[0]) && (h < 0))); } } } // while(!accepted) if (!computedH) { h = o.H(x, A); hp = o.HPrime(x, A); } converged = (Math.Abs(h - target) < epsilon) || unreachableMode;// # modif_0.11 cntn = !converged; if (cntn) { //# register results in bounds bool hLtTarget = h < target; if (bounds.IncludeSoln) { side = hLtTarget ? lowerSide : higherSide; } else { bounds.IncludeSoln = hLtTarget ^ (bounds.H[0] < target); if (found2Bounds) { bool changeOppositeSideBounds = true; if (modeSearch) { side = hLtTarget ? lowerSide : higherSide; changeOppositeSideBounds = true; } else if (bounds.IncludeSoln) { //# bounds newly include solution (happening for the first time with current x) wb = WithinBounds(x, bounds); if (wb.Within) { if (higherSide == 1) { side = lowerSide; changeOppositeSideBounds = false; if (hp < 0) { bounds.X[lowerSide] = x; bounds.H[lowerSide] = h; bounds.Hp[lowerSide] = hp; ParamB01 tmp = LeftSideFastScan(bounds, o, A, target, range[0], h2Modeless); x = tmp.X; h = tmp.H; hp = tmp.Hp; cntn = tmp.Cntn; } } else { throw new WEException("Scénario imprévu."); } } else if (wb.Below) { side = 0; changeOppositeSideBounds = true; } else { //# wb.above side = 1; changeOppositeSideBounds = true; } } else { //# bounds still do not include solution wb = WithinBounds(x, bounds); if (wb.Within) { changeOppositeSideBounds = false; if (h > bounds.H.Max()) { side = higherSide; } else if (h < bounds.H.Min()) { side = hp < 0 ? lowerSide : higherSide; } else { side = higherSide; } } else if (wb.Below) { //Modifié à 1 if (higherSide == 1) { if (hp < 0) { bounds.X[lowerSide] = x; bounds.H[lowerSide] = h; bounds.Hp[lowerSide] = hp; ParamB01 tmp = LeftSideFastScan(bounds, o, A, target, range[lowerSide], h2Modeless); x = tmp.X; h = tmp.H; hp = tmp.Hp; cntn = tmp.Cntn; changeOppositeSideBounds = true; } else if (h < bounds.H.Min()) { side = lowerSide; changeOppositeSideBounds = true; } else { L1 leftSidePotentialStartPoint = new L1(x, h, hp); double[] lim = new double[] { x, bounds.X[lowerSide] }; x = lim.Mean(); hp = o.HPrime(x, A); while (hp > 0) { h = o.H(x, A); side = h > bounds.H[lowerSide] ? 0 : 1; lim[side] = x; x = lim.Mean(); hp = o.HPrime(x, A); } h = o.H(x, A); bounds.X[higherSide] = bounds.X[lowerSide]; bounds.H[higherSide] = bounds.H[lowerSide]; bounds.Hp[higherSide] = bounds.Hp[lowerSide]; bounds.X[lowerSide] = x; bounds.H[lowerSide] = h; bounds.Hp[lowerSide] = hp; ParamB01 tmp = LeftSideFastScan(bounds, o, A, target, range[lowerSide], h2Modeless, leftSidePotentialStartPoint); x = tmp.X; h = tmp.H; hp = tmp.Hp; cntn = tmp.Cntn; side = higherSide; changeOppositeSideBounds = false; } } else { throw new WEException("Scénario imprévu."); } } else { //# wb.above side = lowerSide; changeOppositeSideBounds = true; } } if (changeOppositeSideBounds) { int oppositeSide = 1 - side; bounds.X[oppositeSide] = bounds.X[side]; bounds.H[oppositeSide] = bounds.H[side]; bounds.Hp[oppositeSide] = bounds.Hp[side]; } }//if(found2bounds) else { //# !found2bounds found2Bounds = true; side = h < bounds.H[0] ? lowerSide : higherSide; if (side == 0) { bounds.X[1] = bounds.X[0]; bounds.H[1] = bounds.H[0]; bounds.Hp[1] = bounds.Hp[0]; } } } bounds.X[side] = x; bounds.H[side] = h; bounds.Hp[side] = hp; visitedX.Add(x); converged = (Math.Abs(h - target) < epsilon) || unreachableMode;// # modif_0.11 if (!converged) { cntn = count < SecuredNRSearch.MaxNIter;// # new_0.11: changed <= for < if (!cntn) { //# Before we give up, we check one last thing: //# if the last few steps were all leaning in the same direction, //# then convergence may only be a question of time & patience! //# Give it (yet) another chance! //TODO BIEN VÉRIFIER LA LOGIQUE Visits.DirectionChange[] directionChanges = visitedX.GetDirectionChanges(); if (directionChanges.Length > 0) { Visits.DirectionChange lastChangeDirection = directionChanges.Last(); // IEnumerable <Visits.DirectionChange> inOppositeDirection = directionChanges.Where(a => a.Direction == -lastChangeDirection.Direction); if (inOppositeDirection.Count() == 0) { cntn = true; } else { Visits.DirectionChange lastInOppositeDirection = inOppositeDirection.Last(); if ((count - lastInOppositeDirection.Index) > 20) { cntn = true; } } } else { //TODO ne devrait pas se produire. } if (cntn) { count = 0; visitedX = new Visits(SecuredNRSearch.MaxNIter); } } //if(!cntn) } // if (!converged) if (cntn) { found2Bounds = true; double absHp = Math.Abs(hp); if (correct4HpSign) { hp = expectedHpSign * absHp; } change = (h - target) / hp; double pctChange = Math.Abs(change) / bounds.X.Diff()[0]; if ((pctChange < 1e-4) && absHp > 1000) { double[] p; if (change < 0) { p = new double[] { 0.9, 0.1 }; } else { p = new double[] { 0.1, 0.9 }; } x = p.Multiply(bounds.X).Sum(); } else { x = x - change; } } //if (cntn) //#cat('count=', count, '\n') //#cat('x = ', x, '\n') //#cat('h = ', h, '\n') //#cat('target = ', target, '\n') //#cat('abs(h-target)', abs(h-target), '\n') //#cat('bounds.x = ', bounds.x, '\n') //#cat('diff(bounds.x) = ', diff(bounds.x), '\n') //#if (diff(bounds.x) < 0) cat('***************\n') } // if (cntn) } //while (cntn) this.X = x; this.Converged = converged; this.Bounds = bounds; //list(x = x, converged = converged, bounds = bounds); } //# end of secured.NR.search