Somewhat similar to my previous wonderings on TI's CORDIC implementation, today I'm wondering if it's known precisely what algorithm the numeric solver (MATH -> Solver...) on the 83+ family uses.

I have a vague memory of somebody claiming it used Newton's method at some point, but I think it's more likely to use the bisection method, since EOS is unable to symbolically construct the derivative of a function to find a root of. The secant method is a feasible alternative, but seems suboptimal because it successfully converging on a solution depends strongly on the initial estimate (and it requires two points to estimate at).

That the solver can sometimes generate ERR: NO SIGN CHANGE seems like a point in favor of it using the bisection method, since "no sign change" seems to imply it needs to find two points on the specified function that have opposite sign which is also a requirement of the bisection method. If that's true however, it's unclear how it goes about finding those points initially. For many functions it could look at the signs of the initial guess and upper/lower bounds to search for a zero within, but it still seems to have no trouble with functions that have the same sign at all three of those points: for instance -(x+6)²+4 is solved successfully with the default range (-1e99 to 1e99) and a guess of 10, where the function has a negative value at all three of those X values.

Does anybody have ideas or primary sources regarding how the solver is implemented? I'd be interested to hear!
Continuing my literature survey beyond Wikipedia, the ITP method seems interesting, but it's much newer than the 8x calculators and still seems dependent on finding a sign change. The question remains of how EOS locates a sign change, which it seems to need.

Turning to Numerical Methods for Engineers, chapters 5 and 6 offer nicely-presented suggestions. Chapter 5 includes the bisection and false-position methods, which as discussed first require finding a range of a target function that has opposite sign at either end. Going into "open methods", we find the algorithms that don't require an initial known range containing a sign change. This includes the secant method and Newton's method as we've already looked at, but also suggests Brent's method that seems promising. MATLAB's documentation also notes that its implementation won't find zeroes that don't cross the X axis, and I'm not sure offhand if TI's will- if it does, that could indicate it does something different.

Brent's method is documented as the method MATLAB uses in its fzero built-in function, which seems promising and it also turns out that TI|BD also claims that solve( uses Brent's method. It's not clear where the initial guess require by EOS would be used here however, unless it's used as the initial midpoint in inverse quadratic interpolation: in general, Brent's method doesn't use an initial guess so there's some difference between the typical formulation and what TI does if indeed EOS uses Brent's method.
I think it's possible that TI uses "no sign change" as a teacher-and-student understandable name for a different failure mode of the solver, even if their solver doesn't actually require a sign change.

The TI-Nspire and other calculators use a "combination of bisection and the secant method", whatever that means.
Quote:
I consider it likely TI reused this algorithm. I've since purchased the book for all of three dollars to fuel my interest in 1970s computing and engineering; I'll let you know what I find.

Another algorithm is described for the 85/86, but I haven't evaluated if it is the same algorithm or a different one. Either way, TI seems to be willing to give out this information- consider asking?
The 85/86 approach you link sounds like it may be the same as the one mentioned for the Nspire, with the 85/86 one being described in more detail.

The discussion of finding a sign change is particularly interesting:
Quote:
Prior to getting into the routine described above, the solver attempts through some heuristics to get two "good" end points to start with. The guess provided is one of these, the other depends on a lot of factors, but the solver prefers a sign change if it can find it. The calculator spends some time "going downhill" in the neighborhood of the even multiple root at -1, then "sees" the function begin increasing. It then checks the function values at the bounds, continuing to try to find a sign change. It finds one at the right bound and then begins the routine above using the guess and the right hand bound as the end points.
This doesn't very clearly specify the heuristics, but does suggest that the "no sign change" error indicates the heuristic search for a range containing a zero failed.
iPhoenix has been able to share images of the copy of Shampine and Allen's book he got, which is moderately interesting. I haven't spent much time studying the implementation, but it seems plausible that their ZEROIN function is exactly the algorithm used by TI.

In the prose describing this implementation, they say that in general the two endpoints B and C must bracket a zero (f(B)*f(C)<0) which is as we'd expect from the normal closed methods, consistent with the description of this algorithm as combining bisection and the secant method. They go on to describe "abnormal" situations though, which include cases where the endpoints do not have opposite sign:
Numerical Computing: An Introduction, page 98 wrote:
If on input f(B)f(C) > 0, we do not know whether there are roots in the interval at all. The code bisects, always choosing the interval which has the smaller function values at its ends. If at any time a sign change is detected, it then proceeds in the usual way. If the length of the interval meets the convergence test but there is no sign change, this is reported to the user. The code may have computed a root of even multiplicity. If not, there appears to be no root in the interval.
(Recall that a root of even multiplicity is a point x where f(x)=0, but all points on either side of x have the same sign: the function does not cross the X axis.)
Here's the code I manually transcribed (because OCR on this FORTRAN didn't work very well). It's possible there are still some errors, because I don't fully understand how to read archaic FORTRAN like this.

Code:
      SUBROUTINE ZEROIN(F,B,C,ABSERR,RELERR,IFLAG)
C
C  ZEROIN COMPUTES A ROOT OF THE NONLINEAR EQUATION F(X)=0
C  WHERE F(X) IS A CONTINUOUS REAL FUNCTION OF A SINGLE REAL
C  VARIABLE X. THE METHOD USED IS A COMBINATION OF BISECTION
C  AND THE SECANT RULE.
C
C  NORMAL INPUT CONSISTS OF A CONTINUOUS FUNCTION F AND AN
C  INTERVAL (B,C) SUCH THAT F(B)*F(C).LE.1.0. EACH ITERATION
C  FINDS NEW VALUES OF B AND C SUCH THAT THE INTERVAL (B,C) IS
C  SHRUNK AND F(B)*F(C).LE.0.0. THE STOPPING CRITERION IS
C
C          ABS(B-C).LE.2.0*(RELERR*ABS(B)+ABSERR)
C
C  WHERE RELERR=RELATIVE ERROR AND ABSERR=ABSOLUTE ERROR ARE
C  INPUT QUANTITIES. AS B AND C ARE USED FOR BOTH INPUT AND
C  OUTPUT, THEY MUST BE VARIABLES IN THE CALLING PROGRAM.
C  THE FUNCTION NAME F MUST APPEAR IN AN EXTERNAL STATEMENT IN
C  THE CALLING PROGRAM.
C
C  IF 0 IS A POSSIBLE ZERO, ONE SHOULD NOT CHOOSE ABSERR=0.0.
C
C  THE OUTPUT VALUE OF B IS THE BETTER APPROXIMATION TO A ROOT
C  AS B AND C ARE ALWAYS REDEFINED SO THAT ABS(F(B)).LE.ABS(F(C)).
C
C  A FLAG, IFLAG, IS PROVIDED AS AN OUTPUT QUANTITY. IT MAY ASSUME
C  THE VALUES 1-5 WHERE
C
C     IFLAG=1  IF F(B)*F(C).LE.0 AND THE STOPPING CRITERION IS MET.
C
C          =2  IF A VALUE B IS FOUND SUCH THAT THE COMPUTED VALUE
C              F(B) IS EXACTLY ZERO.  THE INTERVAL (B,C) MAY NOT
C              SATISFY THE STOPPING CRITERION.
C
C          =3  IF ABS(F(B)) EXCEEDS THE INPUT VALUES ABS(F(B)),
C              ABS(F(C)).  IN THIS CASE IT IS LIKELY THAT B IS CLOSE
C              TO A POLE OF F.
C
C          =4  IF NO ODD ORDER ZERO WAS FOUND IN THE INTERVAL. A
C              LOCAL MINIMUM MAY HAVE BEEN OBTAINED.
C
C          =5  IF TOO MANY FUNCTION EVALUATIONS WERE MADE.
C              (AS PROGRAMMED, 500 ARE ALLOWED.)
C
C
C  SET U TO APPROXIMATELY THE UNIT ROUND-OFF OF SPECIFIC MACHINE.
C  (HERE AN IBM 360/67)
C
      U=9.0E-7
C
      RE=AMAX1(RELERR,U)
      IC=0
      ACBS=ABS(B-C)
      A=C
      FA=F(A)
      FB=F(B)
      FC=FA
      KOUNT=2
      FX=AMAX1(ABS(FB),ABS(FC))
  1   IF(ABS(FC).GE.ABS(FB))GO TO 2
C
C  INTERCHANGE B AND C SO THAT ABS(F(B)).LE.ABS(F(C)).
C
      A=B
      FA=FB
      B=C
      FB=FC
      C=A
      FC=FA
  2   CMB=0.5*(C-B)
      ACMB=ABS(CMB)
      TOL=RE*ABS(B)+ABSERR
C
C  TEST STOPPING CRITERION AND FUNCTION COUNT
C
      IF(ACMB.LE.TOL)GO TO 8
      IF(KOUNT.GE.500)GO TO 12
C
C  CALCULATE NEW ITERATE IMPLICITLY AS B+P/Q
C  WHERE WE ARRANGE P.GE.0.  THE IMPLICIT
C  FORM IS USED TO PREVENT OVERFLOW.
C
      P=(B-A)*FB
      Q=FA-FB
      IF(P.GE.0.0)GO TO 3
      P=-P
      Q=-Q
C
C  UPDATE A, CHECK IF REDUCTION IN THE SIZE OF BRACKETING
C  INTERVAL IS SATISFACTORY.  IF NOT, BISECT UNTIL IT IS.
C
  3   A=B
      FA=FB
      IC=IC+1
      IF(IC.LT.4)GO TO 4
      IF(8.0*ACMB.GE.ACBS)GO TO 6
      IC=0
      ACBS=ACMB
C
C  TEST FOR TOO SMALL A CHANGE.
C
  4   IF(P.GT.ABS(Q)*TOL)GO TO 5
C
C  INCREMENT BY TOLERANCE.
C
      B=B+SIGN(TOL,CMB)
      GO TO 7
C
C  ROOT OUGHT TO BE BETWEEN B AND (C+B)/2.
C
  5   IF(P.GE.CMB*Q)GO TO 6
C
C  USE SECANT RULE.
C
      B=B+P/Q
      GO TO 7
C
C  USE BISECTION
C
  6   B=0.5*(C+B)
C
C  HAVE COMPLETED COMPUTATION FOR NEW ITERATE B.
C
  7   FB=F(B)
      IF(FB.EQ.0.0)GO TO 9
      KOUNT=KOUNT+1
      IF(SIGN(1.0,FB).NE.SIGN(1.0,FC))GO TO 1
      C=A
      FC=FA
      GO TO 1
C
C  FINISHED.  SET IFLAG.
C
  8   IF(SIGN(1.0,FB).EQ.SIGN(1.0,FC))GO TO 11
      IF(ABS(FB).GT.FX)GO TO 10
      IFLAG=1
      RETURN
  9   IFLAG=2
      RETURN
 10   IFLAG=3
      RETURN
 11   IFLAG=4
      RETURN
 12   IFLAG=5
      RETURN
      END
This seems like it ticks the major boxes: it still works if the provided endpoints have the same sign, and gives up after a while if unable to find a sign change (ERR: NO SIGN CHANGE). It doesn't permit the caller to provide an initial guess, but the TI-83 Plus manual appears to provide us with enough information to understand how it gets incorporated:
TI-83 Plus / TI-83 Plus Silver Edition Graphing Calculator Guidebook, page 75 wrote:
Enter an initial guess for the variable for which you are solving. This is optional, but it may help find the solution more quickly. Also, for equations with multiple roots, the TI-83 Plus will attempt to display the solution that is closest to your guess.

The default guess is calculated as (upper+lower)/2.

So it seems like the initial guess would replace the midpoint between the two endpoints as the point at which to bisect; the first iteration won't strictly be bisecting, depending on what guess is provided.

A moderately interesting prediction made by these observations is that because roots of even multiplicity cannot be exactly pinpointed, it should be relatively easy to construct functions where the calculator returns answers that are off by a few least-significant digits, depending on the permitted relative and absolute errors.
Just verifying that I kind of understand how to compile and run the FORTRAN code we got, add the following main function (the example for the function's use provided in the book) to a file zeroin.f then compile as gfortran zeroin.f.

Code:
      PROGRAM main
        EXTERNAL F
        B=0.0
        C=1.0
        CALL ZEROIN(F,B,C,0.0,1.e-5,IFLAG)
        GO TO (1,2,2,2,3),IFLAG
   1    RESIDL=F(B)
        PRINT 100,B,RESIDL,IFLAG
 100    FORMAT (1P2E20.7,I5)
        STOP
   2    PRINT 101,B,IFLAG
 101    FORMAT (1PE20.7,I5)
        STOP
   3    PRINT 100,B,C,IFLAG
        STOP
      END

      FUNCTION F(X)
        F=EXP(-X)-X
        RETURN
      END
Running it on my x86 machine prints a slightly different result from what the book says, but it's an accurate result. I think the difference is that the book expects things to be run on an IBM 360/67 that appears to have used 32-bit HFP format, whereas I'll be using an IEEE 754 single-precision float today (or possibly double-precision, but I think the type for an implicit float is still REAL(4) which should be single-precision). My machine prints:
Code:
       5.6714332E-01      -5.9604645E-08    1
whereas the book says the result printed is
Code:
       5.671433E-01       2
where the difference is that the IBM machine finds what it considers to be an exact result, even though both are off the infinite-precision exact result by around 3e-7.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement