Degenerate Conic

Algorithms • Modern Fortran Programming • Orbital Mechanics

May 30, 2026

Polynomial Roots

Consider the problem of finding all the roots of a polynomial. This involves solving the following equation:

$$a_0 + a_1 x + a_2 x^2 + \cdots + a_n x^n = 0$$

This problem occurs in many different applications, such as computing atmospheric density in a spacecraft orbit simulation. In general, both the coefficients and roots can be complex numbers. A special case involves only real coefficients. General methods exist for any degree polynomial, and custom methods also exist for polynomials of specific degree. The quadratic formula is the most famous analytical solution for polynomials of degree 2. In general, numerical methods are necessary to solve polynomials of degree 5 and higher.

Newton's method is frequently used, which requires an initial guess and an iterative procedure. Another elegant way to solve for all the roots at once is to find the eigenvalues of the companion matrix.

Fortran library

polyroots-fortran

My polyroots-fortran library includes various methods to solve this problem. Many of the implementations have legacy going back decades (for example, from SLATEC). It's easy to incorporate into other codes using the Fortran Package Manager. The available methods in the library are:

Method name Polynomial type Coefficients Reference
cpoly General complex Jenkins & Traub (1972)
rpoly General real Jenkins & Traub (1975)
rpzero General real SLATEC
cpzero General complex SLATEC
rpqr79 General real SLATEC
cpqr79 General complex SLATEC
dqtcrt Quartic real NSWC Library
dcbcrt Cubic real NSWC Library
dqdcrt Quadratic real NSWC Library
quadpl Quadratic real NSWC Library
dpolz General real MATH77 Library
cpolz General complex MATH77 Library
polyroots General real LAPACK
cpolyroots General complex LAPACK
rroots_chebyshev_cubic Cubic real Lebedev (1991)
qr_algeq_solver General real Edelman & Murakami (1995)
polzeros General complex Bini (1996)
cmplx_roots_gen General complex Skowron & Gould (2012)
fpml General complex Cameron (2019)

Example

As an example, let's generate a plot of all the roots for all \(11^{th}\) degree polynomials with coefficients either +1 or -1. That is:

\(a_0\) \(a_1\) \(a_2\) \(a_3\) \(a_4\) \(a_5\) \(a_6\) \(a_7\) \(a_8\) \(a_9\) \(a_{10}\) \(a_{11}\)
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 1
-1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 1
-1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1
\(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\) \(\vdots\)
1 1 1 1 1 1 1 1 1 1 1 1

We'll use the polyroots method, which solves for the roots of a real polynomial equation by computing the eigenvalues of the companion matrix (using the LAPACK eigen solver DGEEV). To get all the coefficient permutations, we will use a recursive algorithm, and pyplot-fortran to generate the plot. Here is the code:

program main

use polyroots_module, only: polyroots, wp => polyroots_module_rk
use pyplot_module,    only: pyplot

implicit none

integer,parameter :: degree = 11 !! polynomial degree
integer,dimension(2) :: icoeffs = [-1,1] !! set of coefficients
integer,dimension(degree+1) :: a !! coefficients of polynomial
type(pyplot) :: plt  !! for making the plot
integer      :: ierr !! error code from [[polyroots]]
integer      :: j    !! counter

call plt%initialize(grid=.true.,xlabel='$\\Re(z)$',ylabel='$\\Im(z)$',&
                    title='Degree 11 Polynomial Roots',usetex=.true.,&
                    font_size=30, axes_labelsize=30, &
                    xtick_labelsize=30, ytick_labelsize=30, &
                    figsize=[20,10])
call generate(1)
call plt%showfig()

contains

    recursive subroutine generate(i)
    integer, intent(in) :: i
    integer :: ix
    real(wp),dimension(degree) :: zr, zi !! roots
    if (i > degree+1) then
        ! solve for this root and add it to the plot:
        call polyroots(degree,real(a,wp),zr,zi,ierr)
        call plt%add_plot(zr,zi,label='',linestyle='bo',markersize=2)
    else
        do ix = 1,size(icoeffs)
            a(i) = icoeffs(ix)
            call generate(i+1)
        end do
    end if
    end subroutine generate

end program main

The first set of roots, for \(a = [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]\), are: [\(-1\), \(\pm i\), \(\pm \sqrt3/2 \pm 0.5 i\), \(\pm 0.5 \pm \sqrt3/2\)].

The resultant plot with all the roots is shown here:

roots_11

References

Jan 26, 2025

Root Finding

root3-300x295

*From [A FORTRAN Coloring Book](https://archive.org/details/9780262610261) by Roger Kaufman (1978)*

Finding the \(x\) where a scalar function \(f(x) = 0\) is a common problem in numerical programming. There are numerous applications to this. In orbital mechanics, for example, we may want to find the time at which a spacecraft reaches a certain altitude, or enters the Earth's shadow. For these cases, the independent variable is time, and the function \(f\) requires integration of the equations of motion of the spacecraft (for example, by using an IVP solver such as DDEABM). Integrating to an event is often referred to as a "G-stop" feature in these types of applications.

One way to solve this problem is using a derivative-free bracketing method. For these, only function evaluations are used, and the algorithm begins within a set of bounds (\([x_a, x_b]\)) that bracket the root (i.e., \(sign~f(x_a) \ne sign~f(x_b)\)). Over at the FORTRAN 77 Netlib graveyard, the classic zeroin root finding method can be found that implements such an algorithm, namely Brent's method by Australian mathematician Richard Brent from 1971.

roots-fortran-logo

In my roots-fortran library, I have modern Fortran implementations of this method and many others, as well as a lot of benchmark test cases. The library can be compiled for single (real32), double (real64), or quad (real128) precision reals. For real64 numbers and convergence tolerance \(\epsilon\) = 1.0e-15, the following table lists the number of function evaluations for various methods to converge to the root for various test functions:

\(f(x)\) \([x_a, x_b]\) \(x_{root}\) BS PE ITP TM BR AB MU CH
\((x - 1) e^{-x}\) \([0,1.5]\) 1.0 46 10 11 9 11 10 9 9
\(\cos x - x\) \([0,1.7]\) 0.7390851332151607 47 8 9 8 8 8 7 8
\(e^{x^2 + 7x - 30} - 1\) \([2.6,3.5]\) 3.0 44 20 17 18 12 31 11 11
\(x^3\) \([-0.5,1/3]\) 0.0 17 50 16 46 40 38 61 17
\(x^5\) \([-0.5,1/3]\) 0.0 11 53 12 20 19 39 35 11
\(\cos x - x^3\) \([0,4]\) 0.8654740331016144 48 13 11 13 14 15 10 11
\(x^3 - 1\) \([0.51,1.5]\) 1.0 46 10 11 9 10 9 7 8
\(x^2 ( x^2/3 + \sqrt{2} \sin x ) - \sqrt{3/18}\) \([0.1,1]\) 0.3994222917109682 47 12 17 10 12 12 9 10
\(x^3 + 1\) \([-1.8,0]\) -1.0 47 10 10 11 10 12 9 9
\(\sin((x-7.14)^3)\) \([7,8]\) 7.14 18 51 20 48 46 38 52 18
\(e^{(x-3)^5} - 1\) \([2.6,3.6]\) 3.0 11 55 12 24 26 41 29 11
\(e^{(x-3)^5} - e^{x-1}\) \([4,5]\) 4.267168304542125 44 53 45 11 14 58 20 14
\(\pi - 1/x\) \([0.05,5]\) \(1 / \pi\) 50 14 49 18 12 5 13 13
\(4 - \tan x\) \([0,1.5]\) 1.325817663668033 46 14 47 15 13 12 16 15
\(2 x e^{-5} - 2 e^{-5x} + 1\) \([0,10]\) 0.1382571550568241 52 13 16 13 13 11 13 13

The methods are: BS = Bisection, PE = Pegasus, ITP, TM = TOMS 748, BR = Brent, AB = Anderson Bjorck, MU = Muller, and CH = Chandrupatla.

There seems to be cottage industry of creating slight variations of this type of method. There are any number of recent papers on this topic, all claiming some improvement. In reality, a lot of the newer methods really aren't great. An obvious trick is a choose functions where your method behaves well. Another is to only compare against very simple methods (such as bisection or regula falsi, which are known to be inefficient). Finally, the most devious trick is to list the number of iterations of the method, rather than the number of function evaluations, which is basically a useless comparison that obscures the real cost for methods that have multiple function evaluations per iteration.

The Brent method is still one of the best performers. The Chandrupatla method from 1997 is also quite good. For the full set of test cases, see the roots-fortran git repo.

Minimization methods

A related type of algorithm are derivative-free minimization methods. The FMIN function at Netlib is based on Brent's original localmin algorithm (modern version here). The PRIMA library implements modern versions of M.J.D. Powell's derivative-free minimizers for multidimensional functions.

See also