3
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ Why are you using PolynomialQuotient, which drops remainders? When I replaced it by a simple divide inside the loop, it worked fine. $\endgroup$ – bbgodfrey Jan 27 '16 at 5:13
  • $\begingroup$ @bbgodfrey If the roots are correct, the remainders should be zero. $\endgroup$ – Jerry Guern Jan 27 '16 at 5:57
  • $\begingroup$ 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. $\endgroup$ – bbgodfrey Jan 27 '16 at 11:38
  • 1
    $\begingroup$ @DanielLichtblau If you're interested, I pitted the three methods against each other in a Timing/Precision test in the Answer below. $\endgroup$ – Jerry Guern Jan 27 '16 at 20:38
  • 1
    $\begingroup$ 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. $\endgroup$ – Daniel Lichtblau Jan 27 '16 at 21:17
3
$\begingroup$

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 *)
$\endgroup$
  • $\begingroup$ Thanks! The loss of precision with PolynomialQuotient was surprising, but I suppose it shouldn't be given the number of division problems involved. $\endgroup$ – Jerry Guern Jan 27 '16 at 15:41
  • $\begingroup$ I pitted the three methods against each other in a Timing/Precision test in the Answer below. $\endgroup$ – Jerry Guern Jan 27 '16 at 20:38
1
$\begingroup$

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?

$\endgroup$
1
$\begingroup$

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 = 
      NumericalMath`FixedPrecisionEvaluate[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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.