# How to set/adjust Precision for an iterative calculation?

How should I restructure this code? I generate a high-order polynomial poly with integer coefficients, then find roots and divide them out of poly one at a time. I want FindRoot[] to use a very high precision on the first pass but then just use the precision of the inputs afterward.

z[n_, c_] := If[n > 0, z[n - 1, c]^2 + c, c];
poly = PolynomialQuotient[z[10, c] - z[6, c], 1 + c^2, c];
rts = {};
Do[Print[Precision[poly]];
aa = FindRoot[poly, {c, I}, WorkingPrecision -> Min[500, Floor[Precision[poly]]]];
AppendTo[rts, c /. aa];
poly = PolynomialQuotient[poly, ((z - c)*(z - Conjugate[c]) /. aa) /. z -> c, c], {j, 1, 5}]


(* [Infinity] 319.649 135.906 0.00598703 *)

I'm losing so much precision on each pass that I can only get a few roots. I'm trying to get the roots closest to i without having to find all the roots. I encountered this precision problem while trying to fix this other problem. I'm also not clear on WHY the polynomial division loses precision so quickly.

• Why are you using PolynomialQuotient, which drops remainders? When I replaced it by a simple divide inside the loop, it worked fine. Jan 27, 2016 at 5:13
• @bbgodfrey If the roots are correct, the remainders should be zero. Jan 27, 2016 at 5:57
• What you say is correct. Nonetheless, the precision remains at 500 and the error messages stop, if the second instance of PolynomialQuotient is replaced by divide. Also, replacing c by d + I might help. Incidentally, a WorkingPrecision of 190 gives the same results as 500, but 180 does not work. Jan 27, 2016 at 11:38
• @DanielLichtblau If you're interested, I pitted the three methods against each other in a Timing/Precision test in the Answer below. Jan 27, 2016 at 20:38
• Also there is the possibility of setting \$MinPrecision in a Block when invoking PolynomialQuotient. An undocumented shortcut is to use NumericalMathFixedPrecisionEvaluate[expr,prec]. I'll show an example where I work directly with coefficient lists in finding the quotient. Jan 27, 2016 at 21:17

Precision is preserved, and error messages are eliminated by replacing the second instance of PolynomialQuotient by a simple divide.

poly = poly/ (((z - c)*(z - Conjugate[c]) /. aa) /. z -> c)


The only apparent difference is that PolynomialQuotient discards any remainder, and there is a remainder unless c is exact. In some way that I do not understand, discarding the remainder must reduce the precision of poly. So, this may not be a particularly satisfying answer, but it does produce accurate roots for a WorkingPrecision as low as Min[190, Floor[Precision[poly]]]. Replace 190 by 187, however, and the precision of poly gradually decreases to 185, whereupon the Jacobian becomes singular. For completeness, rts[[5]] for 190 is

(* -0.01660571703737496762392836921351877966202681662442570475568531876233767935059224313655985208958046768635767201058325661435694155300994327878649425388424142524075655339495830479365786600752927
+ 1.006001836522824948881217257805018657146542248017733702434346228688166066459126472333560301933200993465308529209500537579261396301606919993043401875320490162101139793328283528897296023734789 I *)

• Thanks! The loss of precision with PolynomialQuotient was surprising, but I suppose it shouldn't be given the number of division problems involved. Jan 27, 2016 at 15:41
• I pitted the three methods against each other in a Timing/Precision test in the Answer below. Jan 27, 2016 at 20:38

Follow-up from OP: Based on the very helpful Answers and comments above from @bbgodfrey and @Daniel Lichtblau, I ran the following test, comparing the three methods on speed and precision:

z[n_, c_] := If[n > 0, z[n - 1, c]^2 + c, c];
polyOrig = PolynomialQuotient[z[10, c] - z[6, c], 1 + c^2, c];
rtsPR = {}; rtsDiv = {}; rtsPQ = {};
nRoots = 3;
Do[
poly = polyOrig;
Print[Timing[
Do[Print[Precision[poly]];
aa = FindRoot[poly, {c, I}, WorkingPrecision -> Max[10, Min[300, Floor[Precision[poly]]]]];
fac = (((z - c)*(z - Conjugate[c]) /. aa) /. z -> c);
Which[
pass == 1,
AppendTo[rtsPR, c /. aa];
poly = PolynomialReduce[poly, fac, c][[1, 1]],
pass == 2,
AppendTo[rtsPQ, c /. aa];
poly = PolynomialQuotient[poly, fac, c],
pass == 3,
AppendTo[rtsDiv, c /. aa];
poly = poly/fac], {i, 1, nRoots}]]], {pass, 1, 3}]


Output: (* [Infinity]

300.

300.

{525.989,Null}

[Infinity]

119.649

0.00743297

FindRoot::precw: The precision of the argument function (<<75>>+<<967>>) is less than WorkingPrecision (10.). >>

{0.967206,Null}

[Infinity]

300.

300.

{7.16045,Null} *)

Verdict: PolynomialReduce retains precision but is slow, PolynomialQuotient loses precision very quickly, Divide is fast and precise but leaves you with a potential 0/0 problem.

Does anyone see anything else in these results I didn't, or have an idea for an improvement?

As promised in a comment, here is a variant that works in fixed precision. We do get three good roots below.

First some code to do quotients of polynomials represented by their coefficient lists. This was taken from internal code for PolynomialSmithDecomposition in some Control Theory context. (I'm allowed to do that, it was my code and written on a weekend.)

lpQuoRem[p1_, p2_] :=
Module[{p2top = p2[[-1]], top, quo, quolist, rem = p1, len, max},
top = Length[p1] - Length[p2] + 1;
If[top <= 0, Return[{{}, rem}]];
quolist = ConstantArray[0, top];
While[Length[rem] >= Length[p2], max = Max[Abs[p1]];
quo = rem[[-1]]/p2top;
quolist[[Length[rem] - Length[p2] + 1]] = quo;
rem = Most[rem] - quo*PadLeft[Most[p2], Length[rem] - 1];
len = Length[rem];
While[len > 0 && rem[[len]] == 0, len--];
rem = Take[rem, len];];
{quolist, rem}]

z[n_, c_] := If[n > 0, z[n - 1, c]^2 + c, c];
polyOrig = PolynomialQuotient[z[10, c] - z[6, c], 1 + c^2, c];
rtsPR = {};
nRoots = 3;
prec = 300;
poly = polyOrig;
Do[
Print[Timing[
Print[{Precision[poly], Exponent[poly, c]}];
aa =
FindRoot[poly, {c, I},
WorkingPrecision ->
Max[10, Min[300, Floor[Precision[poly]]]]];
fac = {c*Conjugate[c], -(c + Conjugate[c]), 1} /. aa;
Print[N@{aa, fac}];
AppendTo[rtsPR, c /. aa];
lpoly = CoefficientList[poly, c];
lpoly =
NumericalMathFixedPrecisionEvaluate[lpQuoRem[lpoly, fac], prec];
poly = Expand[FromDigits[Reverse[lpoly[[1]]], c]];
]];
, {i, 1, nRoots}];

(* {\[Infinity],1022}

{{c->-0.000732220309309+1.00453713135 I},{1.00909538441 +0. I,0.00146444061862 +0. I,1.}}

{0.908594,Null}

{300.,1020}

{{c->-0.00956676851273+1.00673513946 I},{1.01360716408 +0. I,0.0191335370255 +0. I,1.}}

{1.250491,Null}

{300.,1018}

{{c->0.00279929953246 +1.00502058481 I},{1.01007421197 +0. I,-0.00559859906491+0. I,1.}}

{1.548567,Null} *)


Caveat: There is the possibility that eventually an accumulation of error will cause the results to be bad in the sense of not being roots to the original polynomial. A production environment would check for that and maybe raise precision when necessary. This does not require a full restart. One just polishes the "good" roots with FindRoot to higher precision than were earlier obtained, and redoes the quotients at higher precision.

• What exactly is NumericalMathFixedPrecisionEvaluate? Is it intended to be used as a magical function that cures all numerical instabilities? Nov 29, 2020 at 19:10
• It's what its name suggests: evaluate numerically in fixed precision. It disables the precision tracking that is used by default. There is no cure for all numeric instabilities. Evaluation at high precision can sometimes help handle ill-conditioning. Nov 29, 2020 at 21:20