4 # remez.jl - implementation of the Remez algorithm for polynomial approximation
6 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
7 # See https://llvm.org/LICENSE.txt for license information.
8 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
12 # ----------------------------------------------------------------------
13 # Helper functions to cope with different Julia versions.
14 if VERSION >= v"0.7.0"
15 array1d(T, d) = Array{T, 1}(undef, d)
16 array2d(T, d1, d2) = Array{T, 2}(undef, d1, d2)
18 array1d(T, d) = Array(T, d)
19 array2d(T, d1, d2) = Array(T, d1, d2)
24 if VERSION >= v"0.6.0"
25 # Use Base.invokelatest to run functions made using eval(), to
26 # avoid "world age" error
27 run(f, x...) = Base.invokelatest(f, x...)
29 # Prior to 0.6.0, invokelatest doesn't exist (but fortunately the
30 # world age problem also doesn't seem to exist)
31 run(f, x...) = f(x...)
34 # ----------------------------------------------------------------------
35 # Global variables configured by command-line options.
36 floatsuffix = "" # adjusted by --floatsuffix
37 xvarname = "x" # adjusted by --variable
38 epsbits = 256 # adjusted by --bits
39 debug_facilities = Set() # adjusted by --debug
40 full_output = false # adjusted by --full
41 array_format = false # adjusted by --array
42 preliminary_commands = array1d(String, 0) # adjusted by --pre
44 # ----------------------------------------------------------------------
45 # Diagnostic and utility functions.
47 # Enable debugging printouts from a particular subpart of this
51 # facility Name of the facility to debug. For a list of facility names,
52 # look through the code for calls to debug().
54 # Return value is a BigFloat.
55 function enable_debug(facility)
56 push!(debug_facilities, facility)
62 # facility Name of the facility for which this is a debug message.
63 # printargs Arguments to println() if debugging of that facility is
65 macro debug(facility, printargs...)
67 print("[", $facility, "] ")
76 if $facility in debug_facilities
83 # Evaluate a polynomial.
86 # coeffs Array of BigFloats giving the coefficients of the polynomial.
87 # Starts with the constant term, i.e. coeffs[i] is the
88 # coefficient of x^(i-1) (because Julia arrays are 1-based).
89 # x Point at which to evaluate the polynomial.
91 # Return value is a BigFloat.
92 function poly_eval(coeffs::Array{BigFloat}, x::BigFloat)
99 return coeffs[1] + x * poly_eval(coeffs[2:n], x)
103 # Evaluate a rational function.
106 # ncoeffs Array of BigFloats giving the coefficients of the numerator.
107 # Starts with the constant term, and 1-based, as above.
108 # dcoeffs Array of BigFloats giving the coefficients of the denominator.
109 # Starts with the constant term, and 1-based, as above.
110 # x Point at which to evaluate the function.
112 # Return value is a BigFloat.
113 function ratfn_eval(ncoeffs::Array{BigFloat}, dcoeffs::Array{BigFloat},
115 return poly_eval(ncoeffs, x) / poly_eval(dcoeffs, x)
118 # Format a BigFloat into an appropriate output format.
120 # x BigFloat to format.
122 # Return value is a string.
123 function float_to_str(x)
124 return string(x) * floatsuffix
127 # Format a polynomial into an arithmetic expression, for pasting into
128 # other tools such as gnuplot.
131 # coeffs Array of BigFloats giving the coefficients of the polynomial.
132 # Starts with the constant term, and 1-based, as above.
134 # Return value is a string.
135 function poly_to_string(coeffs::Array{BigFloat})
140 return float_to_str(coeffs[1])
142 return string(float_to_str(coeffs[1]), "+", xvarname, "*(",
143 poly_to_string(coeffs[2:n]), ")")
147 # Format a rational function into a string.
150 # ncoeffs Array of BigFloats giving the coefficients of the numerator.
151 # Starts with the constant term, and 1-based, as above.
152 # dcoeffs Array of BigFloats giving the coefficients of the denominator.
153 # Starts with the constant term, and 1-based, as above.
155 # Return value is a string.
156 function ratfn_to_string(ncoeffs::Array{BigFloat}, dcoeffs::Array{BigFloat})
157 if length(dcoeffs) == 1 && dcoeffs[1] == 1
158 # Special case: if the denominator is just 1, leave it out.
159 return poly_to_string(ncoeffs)
161 return string("(", poly_to_string(ncoeffs), ")/(",
162 poly_to_string(dcoeffs), ")")
166 # Format a list of x,y pairs into a string.
169 # xys Array of (x,y) pairs of BigFloats.
171 # Return value is a string.
172 function format_xylist(xys::Array{Tuple{BigFloat,BigFloat}})
174 join([" "*string(x)*" -> "*string(y) for (x,y) in xys], "\n") *
178 # ----------------------------------------------------------------------
179 # Matrix-equation solver for matrices of BigFloat.
181 # I had hoped that Julia's type-genericity would allow me to solve the
182 # matrix equation Mx=V by just writing 'M \ V'. Unfortunately, that
183 # works by translating the inputs into double precision and handing
184 # off to an optimised library, which misses the point when I have a
185 # matrix and vector of BigFloat and want my result in _better_ than
186 # double precision. So I have to implement my own specialisation of
187 # the \ operator for that case.
189 # Fortunately, the point of using BigFloats is that we have precision
190 # to burn, so I can do completely naïve Gaussian elimination without
191 # worrying about instability.
194 # matrix_in 2-dimensional array of BigFloats, representing a matrix M
195 # in row-first order, i.e. matrix_in[r,c] represents the
196 # entry in row r col c.
197 # vector_in 1-dimensional array of BigFloats, representing a vector V.
199 # Return value: a 1-dimensional array X of BigFloats, satisfying M X = V.
201 # Expects the input to be an invertible square matrix and a vector of
202 # the corresponding size, on pain of failing an assertion.
203 function \(matrix_in :: Array{BigFloat,2},
204 vector_in :: Array{BigFloat,1})
205 # Copy the inputs, because we'll be mutating them as we go.
209 # Input consistency criteria: matrix is square, and vector has
213 @assert(size(M) == (n,n))
215 @debug("gausselim", "starting, n=", n)
218 # Straightforward Gaussian elimination: find the largest
219 # non-zero entry in column i (and in a row we haven't sorted
220 # out already), swap it into row i, scale that row to
221 # normalise it to 1, then zero out the rest of the column by
222 # subtracting a multiple of that row from each other row.
224 @debug("gausselim", "matrix=", repr(M))
225 @debug("gausselim", "vector=", repr(V))
227 # Find the best pivot.
231 if abs(M[j,i]) > bestval
236 @assert(bestrow > 0) # make sure we did actually find one
238 @debug("gausselim", "bestrow=", bestrow)
240 # Swap it into row i.
243 M[bestrow,k],M[i,k] = M[i,k],M[bestrow,k]
245 V[bestrow],V[i] = V[i],V[bestrow]
248 # Scale that row so that M[i,i] becomes 1.
251 M[i,k] = M[i,k] / divisor
253 V[i] = V[i] / divisor
256 # Zero out all other entries in column i, by subtracting
257 # multiples of this row.
262 M[j,k] = M[j,k] - M[i,k] * factor
264 V[j] = V[j] - V[i] * factor
270 @debug("gausselim", "matrix=", repr(M))
271 @debug("gausselim", "vector=", repr(V))
272 @debug("gausselim", "done!")
274 # Now we're done: M is the identity matrix, so the equation Mx=V
275 # becomes just x=V, i.e. V is already exactly the vector we want
280 # ----------------------------------------------------------------------
281 # Least-squares fitting of a rational function to a set of (x,y)
284 # We use this to get an initial starting point for the Remez
285 # iteration. Therefore, it doesn't really need to be particularly
286 # accurate; it only needs to be good enough to wiggle back and forth
287 # across the target function the right number of times (so as to give
288 # enough error extrema to start optimising from) and not have any
289 # poles in the target interval.
291 # Least-squares fitting of a _polynomial_ is actually a sensible thing
292 # to do, and minimises the rms error. Doing the following trick with a
293 # rational function P/Q is less sensible, because it cannot be made to
294 # minimise the error function (P/Q-f)^2 that you actually wanted;
295 # instead it minimises (P-fQ)^2. But that should be good enough to
296 # have the properties described above.
298 # Some theory: suppose you're trying to choose a set of parameters a_i
299 # so as to minimise the sum of squares of some error function E_i.
300 # Basic calculus says, if you do this in one variable, just
301 # differentiate and solve for zero. In this case, that works fine even
302 # with multiple variables, because you _partially_ differentiate with
303 # respect to each a_i, giving a system of equations, and that system
304 # turns out to be linear so we just solve it as a matrix.
306 # In this case, our parameters are the coefficients of P and Q; to
307 # avoid underdetermining the system we'll fix Q's constant term at 1,
308 # so that our error function (as described above) is
310 # E = \sum (p_0 + p_1 x + ... + p_n x^n - y - y q_1 x - ... - y q_d x^d)^2
312 # where the sum is over all (x,y) coordinate pairs. Setting dE/dp_j=0
313 # (for each j) gives an equation of the form
315 # 0 = \sum 2(p_0 + p_1 x + ... + p_n x^n - y - y q_1 x - ... - y q_d x^d) x^j
317 # and setting dE/dq_j=0 gives one of the form
319 # 0 = \sum 2(p_0 + p_1 x + ... + p_n x^n - y - y q_1 x - ... - y q_d x^d) y x^j
321 # And both of those row types, treated as multivariate linear
322 # equations in the p,q values, have each coefficient being a value of
323 # the form \sum x^i, \sum y x^i or \sum y^2 x^i, for various i. (Times
324 # a factor of 2, but we can throw that away.) So we can go through the
325 # list of input coordinates summing all of those things, and then we
326 # have enough information to construct our matrix and solve it
327 # straight off for the rational function coefficients.
330 # f The function to be approximated. Maps BigFloat -> BigFloat.
331 # xvals Array of BigFloats, giving the list of x-coordinates at which
333 # n Degree of the numerator polynomial of the desired rational
335 # d Degree of the denominator polynomial of the desired rational
337 # w Error-weighting function. Takes two BigFloat arguments x,y
338 # and returns a scaling factor for the error at that location.
339 # A larger value indicates that the error should be given
340 # greater weight in the square sum we try to minimise.
341 # If unspecified, defaults to giving everything the same weight.
343 # Return values: a pair of arrays of BigFloats (N,D) giving the
344 # coefficients of the returned rational function. N has size n+1; D
345 # has size d+1. Both start with the constant term, i.e. N[i] is the
346 # coefficient of x^(i-1) (because Julia arrays are 1-based). D[1] will
348 function ratfn_leastsquares(f::Function, xvals::Array{BigFloat}, n, d,
349 w = (x,y)->BigFloat(1))
350 # Accumulate sums of x^i y^j, for j={0,1,2} and a range of x.
351 # Again because Julia arrays are 1-based, we'll have sums[i,j]
352 # being the sum of x^(i-1) y^(j-1).
353 maxpow = max(n,d) * 2 + 1
354 sums = zeros(BigFloat, maxpow, 3)
360 sums[i,j] += x^(i-1) * y^(j-1) * weight
365 @debug("leastsquares", "sums=", repr(sums))
367 # Build the matrix. We're solving n+d+1 equations in n+d+1
368 # unknowns. (We actually have to return n+d+2 coefficients, but
369 # one of them is hardwired to 1.)
370 matrix = array2d(BigFloat, n+d+1, n+d+1)
371 vector = array1d(BigFloat, n+d+1)
373 # Equation obtained by differentiating with respect to p_i,
374 # i.e. the numerator coefficient of x^i.
377 matrix[row, 1+j] = sums[1+i+j, 1]
380 matrix[row, 1+n+j] = -sums[1+i+j, 2]
382 vector[row] = sums[1+i, 2]
385 # Equation obtained by differentiating with respect to q_i,
386 # i.e. the denominator coefficient of x^i.
389 matrix[row, 1+j] = sums[1+i+j, 2]
392 matrix[row, 1+n+j] = -sums[1+i+j, 3]
394 vector[row] = sums[1+i, 3]
397 @debug("leastsquares", "matrix=", repr(matrix))
398 @debug("leastsquares", "vector=", repr(vector))
400 # Solve the matrix equation.
401 all_coeffs = matrix \ vector
403 @debug("leastsquares", "all_coeffs=", repr(all_coeffs))
405 # And marshal the results into two separate polynomial vectors to
407 ncoeffs = all_coeffs[1:n+1]
408 dcoeffs = vcat([1], all_coeffs[n+2:n+d+1])
409 return (ncoeffs, dcoeffs)
412 # ----------------------------------------------------------------------
413 # Golden-section search to find a maximum of a function.
416 # f Function to be maximised/minimised. Maps BigFloat -> BigFloat.
417 # a,b,c BigFloats bracketing a maximum of the function.
420 # a,b,c are in order (either a<=b<=c or c<=b<=a)
421 # a != c (but b can equal one or the other if it wants to)
422 # f(a) <= f(b) >= f(c)
424 # Return value is an (x,y) pair of BigFloats giving the extremal input
425 # and output. (That is, y=f(x).)
426 function goldensection(f::Function, a::BigFloat, b::BigFloat, c::BigFloat)
427 # Decide on a 'good enough' threshold.
428 threshold = abs(c-a) * 2^(-epsbits/2)
430 # We'll need the golden ratio phi, of course. Or rather, in this
431 # case, we need 1/phi = 0.618...
432 one_over_phi = 2 / (1 + sqrt(BigFloat(5)))
434 # Flip round the interval endpoints so that the interval [a,b] is
435 # at least as large as [b,c]. (Then we can always pick our new
436 # point in [a,b] without having to handle lots of special cases.)
437 if abs(b-a) < abs(c-a)
441 # Evaluate the function at the initial points.
446 @debug("goldensection", "starting")
448 while abs(c-a) > threshold
449 @debug("goldensection", "a: ", a, " -> ", fa)
450 @debug("goldensection", "b: ", b, " -> ", fb)
451 @debug("goldensection", "c: ", c, " -> ", fc)
454 @assert(a <= b <= c || c <= b <= a)
455 @assert(fa <= fb >= fc)
457 # Subdivide the larger of the intervals [a,b] and [b,c]. We've
458 # arranged that this is always [a,b], for simplicity.
459 d = a + (b-a) * one_over_phi
461 # Now we have an interval looking like this (possibly
466 # and we know f(b) is bigger than either f(a) or f(c). We have
467 # two cases: either f(d) > f(b), or vice versa. In either
468 # case, we can narrow to an interval of 1/phi the size, and
469 # still satisfy all our invariants (three ordered points,
470 # [a,b] at least the width of [b,c], f(a)<=f(b)>=f(c)).
472 @debug("goldensection", "d: ", d, " -> ", fd)
475 fa, fb, fc = fa, fd, fb
476 @debug("goldensection", "adb case")
479 fa, fb, fc = fc, fb, fd
480 @debug("goldensection", "cbd case")
484 @debug("goldensection", "done: ", b, " -> ", fb)
488 # ----------------------------------------------------------------------
489 # Find the extrema of a function within a given interval.
492 # f The function to be approximated. Maps BigFloat -> BigFloat.
493 # grid A set of points at which to evaluate f. Must be high enough
494 # resolution to make extrema obvious.
496 # Returns an array of (x,y) pairs of BigFloats, with each x,y giving
497 # the extremum location and its value (i.e. y=f(x)).
498 function find_extrema(f::Function, grid::Array{BigFloat})
500 extrema = array1d(Tuple{BigFloat, BigFloat}, 0)
502 # We have to provide goldensection() with three points
503 # bracketing the extremum. If the extremum is at one end of
504 # the interval, then the only way we can do that is to set two
505 # of the points equal (which goldensection() will cope with).
509 # Find our three pairs of (x,y) coordinates.
510 xp, xi, xn = grid[prev], grid[i], grid[next]
511 yp, yi, yn = f(xp), f(xi), f(xn)
513 # See if they look like an extremum, and if so, ask
514 # goldensection() to give a more exact location for it.
516 push!(extrema, goldensection(f, xp, xi, xn))
517 elseif yp >= yi <= yn
518 x, y = goldensection(x->-f(x), xp, xi, xn)
519 push!(extrema, (x, -y))
525 # ----------------------------------------------------------------------
526 # Winnow a list of a function's extrema to give a subsequence of a
527 # specified length, with the extrema in the subsequence alternating
528 # signs, and with the smallest absolute value of an extremum in the
529 # subsequence as large as possible.
531 # We do this using a dynamic-programming approach. We work along the
532 # provided array of extrema, and at all times, we track the best set
533 # of extrema we have so far seen for each possible (length, sign of
534 # last extremum) pair. Each new extremum is evaluated to see whether
535 # it can be added to any previously seen best subsequence to make a
536 # new subsequence that beats the previous record holder in its slot.
539 # extrema An array of (x,y) pairs of BigFloats giving the input extrema.
540 # n Number of extrema required as output.
542 # Returns a new array of (x,y) pairs which is a subsequence of the
543 # original sequence. (So, in particular, if the input was sorted by x
544 # then so will the output be.)
545 function winnow_extrema(extrema::Array{Tuple{BigFloat,BigFloat}}, n)
546 # best[i,j] gives the best sequence so far of length i and with
547 # sign j (where signs are coded as 1=positive, 2=negative), in the
548 # form of a tuple (cost, actual array of x,y pairs).
549 best = fill((BigFloat(0), array1d(Tuple{BigFloat,BigFloat}, 0)), n, 2)
557 # A zero-valued extremum cannot possibly contribute to any
558 # optimal sequence, so we simply ignore it!
563 # See if we can create a new entry for best[i,sign] by
564 # appending our current (x,y) to some previous thing.
566 # Special case: we don't store a best zero-length
568 candidate = (abs(y), [(x,y)])
570 othersign = 3-sign # map 1->2 and 2->1
571 oldscore, oldlist = best[i-1, othersign]
572 newscore = min(abs(y), oldscore)
573 newlist = vcat(oldlist, [(x,y)])
574 candidate = (newscore, newlist)
576 # If our new candidate improves on the previous value of
577 # best[i,sign], then replace it.
578 if candidate[1] > best[i,sign][1]
579 best[i,sign] = candidate
584 # Our ultimate return value has to be either best[n,1] or
585 # best[n,2], but it could be either. See which one has the higher
587 if best[n,1][1] > best[n,2][1]
592 # Make sure we did actually _find_ a good answer.
593 @assert(length(ret) == n)
597 # ----------------------------------------------------------------------
598 # Construct a rational-function approximation with equal and
599 # alternating weighted deviation at a specific set of x-coordinates.
602 # f The function to be approximated. Maps BigFloat -> BigFloat.
603 # coords An array of BigFloats giving the x-coordinates. There should
605 # n, d The degrees of the numerator and denominator of the desired
607 # prev_err A plausible value for the alternating weighted deviation.
608 # (Required to kickstart a binary search in the nonlinear case;
609 # see comments below.)
610 # w Error-weighting function. Takes two BigFloat arguments x,y
611 # and returns a scaling factor for the error at that location.
612 # The returned approximation R should have the minimum possible
613 # maximum value of abs((f(x)-R(x)) * w(x,f(x))). Optional
614 # parameter, defaulting to the always-return-1 function.
616 # Return values: a pair of arrays of BigFloats (N,D) giving the
617 # coefficients of the returned rational function. N has size n+1; D
618 # has size d+1. Both start with the constant term, i.e. N[i] is the
619 # coefficient of x^(i-1) (because Julia arrays are 1-based). D[1] will
621 function ratfn_equal_deviation(f::Function, coords::Array{BigFloat},
622 n, d, prev_err::BigFloat,
623 w = (x,y)->BigFloat(1))
624 @debug("equaldev", "n=", n, " d=", d, " coords=", repr(coords))
625 @assert(length(coords) == n+d+2)
628 # Special case: we're after a polynomial. In this case, we
629 # have the particularly easy job of just constructing and
630 # solving a system of n+2 linear equations, to find the n+1
631 # coefficients of the polynomial and also the amount of
632 # deviation at the specified coordinates. Each equation is of
635 # p_0 x^0 + p_1 x^1 + ... + p_n x^n ± e/w(x) = f(x)
637 # in which the p_i and e are the variables, and the powers of
638 # x and calls to w and f are the coefficients.
640 matrix = array2d(BigFloat, n+2, n+2)
641 vector = array1d(BigFloat, n+2)
650 matrix[i, n+2] = currsign / w(x,y)
654 @debug("equaldev", "matrix=", repr(matrix))
655 @debug("equaldev", "vector=", repr(vector))
657 outvector = matrix \ vector
659 @debug("equaldev", "outvector=", repr(outvector))
661 ncoeffs = outvector[1:n+1]
662 dcoeffs = [BigFloat(1)]
663 return ncoeffs, dcoeffs
665 # For a nontrivial rational function, the system of equations
666 # we need to solve becomes nonlinear, because each equation
669 # p_0 x^0 + p_1 x^1 + ... + p_n x^n
670 # --------------------------------- ± e/w(x) = f(x)
671 # x^0 + q_1 x^1 + ... + q_d x^d
673 # and multiplying up by the denominator gives you a lot of
674 # terms containing e × q_i. So we can't do this the really
675 # easy way using a matrix equation as above.
677 # Fortunately, this is a fairly easy kind of nonlinear system.
678 # The equations all become linear if you switch to treating e
679 # as a constant, so a reasonably sensible approach is to pick
680 # a candidate value of e, solve all but one of the equations
681 # for the remaining unknowns, and then see what the error
682 # turns out to be in the final equation. The Chebyshev
683 # alternation theorem guarantees that that error in the last
684 # equation will be anti-monotonic in the input e, so we can
685 # just binary-search until we get the two as close to equal as
689 # Try a given value of e, derive the coefficients of the
690 # resulting rational function by setting up equations
691 # based on the first n+d+1 of the n+d+2 coordinates, and
692 # see what the error turns out to be at the final
694 matrix = array2d(BigFloat, n+d+1, n+d+1)
695 vector = array1d(BigFloat, n+d+1)
700 y_adj = y - currsign * e / w(x,y)
705 matrix[i,1+n+j] = -x^j * y_adj
711 @debug("equaldev", "trying e=", e)
712 @debug("equaldev", "matrix=", repr(matrix))
713 @debug("equaldev", "vector=", repr(vector))
715 outvector = matrix \ vector
717 @debug("equaldev", "outvector=", repr(outvector))
719 ncoeffs = outvector[1:n+1]
720 dcoeffs = vcat([BigFloat(1)], outvector[n+2:n+d+1])
724 last_e = (ratfn_eval(ncoeffs, dcoeffs, x) - y) * w(x,y) * -currsign
726 @debug("equaldev", "last e=", last_e)
728 return ncoeffs, dcoeffs, last_e
731 threshold = 2^(-epsbits/2) # convergence threshold
733 # Start by trying our previous iteration's error value. This
734 # value (e0) will be one end of our binary-search interval,
735 # and whatever it caused the last point's error to be, that
736 # (e1) will be the other end.
738 @debug("equaldev", "e0 = ", e0)
739 nc, dc, e1 = try_e(e0)
740 @debug("equaldev", "e1 = ", e1)
741 if abs(e1-e0) <= threshold
742 # If we're _really_ lucky, we hit the error right on the
743 # nose just by doing that!
747 @debug("equaldev", "s = ", s)
749 # Verify by assertion that trying our other interval endpoint
750 # e1 gives a value that's wrong in the other direction.
751 # (Otherwise our binary search won't get a sensible answer at
753 nc, dc, e2 = try_e(e1)
754 @debug("equaldev", "e2 = ", e2)
755 @assert(sign(e2-e1) == -s)
757 # Now binary-search until our two endpoints narrow enough.
759 while abs(e1-e0) > threshold
761 nc, dc, enew = try_e(emid)
762 if sign(enew-emid) == s
769 @debug("equaldev", "final e=", emid)
774 # ----------------------------------------------------------------------
775 # Top-level function to find a minimax rational-function approximation.
778 # f The function to be approximated. Maps BigFloat -> BigFloat.
779 # interval A pair of BigFloats giving the endpoints of the interval
780 # (in either order) on which to approximate f.
781 # n, d The degrees of the numerator and denominator of the desired
783 # w Error-weighting function. Takes two BigFloat arguments x,y
784 # and returns a scaling factor for the error at that location.
785 # The returned approximation R should have the minimum possible
786 # maximum value of abs((f(x)-R(x)) * w(x,f(x))). Optional
787 # parameter, defaulting to the always-return-1 function.
789 # Return values: a tuple (N,D,E,X), where
791 # N,D A pair of arrays of BigFloats giving the coefficients
792 # of the returned rational function. N has size n+1; D
793 # has size d+1. Both start with the constant term, i.e.
794 # N[i] is the coefficient of x^(i-1) (because Julia
795 # arrays are 1-based). D[1] will be 1.
796 # E The maximum weighted error (BigFloat).
797 # X An array of pairs of BigFloats giving the locations of n+2
798 # points and the weighted error at each of those points. The
799 # weighted error values will have alternating signs, which
800 # means that the Chebyshev alternation theorem guarantees
801 # that any other function of the same degree must exceed
802 # the error of this one at at least one of those points.
803 function ratfn_minimax(f::Function, interval::Tuple{BigFloat,BigFloat}, n, d,
804 w = (x,y)->BigFloat(1))
805 # We start off by finding a least-squares approximation. This
806 # doesn't need to be perfect, but if we can get it reasonably good
807 # then it'll save iterations in the refining stage.
809 # Least-squares approximations tend to look nicer in a minimax
810 # sense if you evaluate the function at a big pile of Chebyshev
811 # nodes rather than uniformly spaced points. These values will
812 # also make a good grid to use for the initial search for error
813 # extrema, so we'll keep them around for that reason too.
815 # Construct the grid.
816 lo, hi = minimum(interval), maximum(interval)
821 nnodes = 16 * (n+d+1)
822 pi = 2*asin(BigFloat(1))
823 grid = [ mid - halfwid * cos(pi*i/nnodes) for i=0:1:nnodes ]
826 # Find the initial least-squares approximation.
827 (nc, dc) = ratfn_leastsquares(f, grid, n, d, w)
828 @debug("minimax", "initial leastsquares approx = ",
829 ratfn_to_string(nc, dc))
831 # Threshold of convergence. We stop when the relative difference
832 # between the min and max (winnowed) error extrema is less than
835 # This is set to the cube root of machine epsilon on a more or
836 # less empirical basis, because the rational-function case will
837 # not converge reliably if you set it to only the square root.
838 # (Repeatable by using the --test mode.) On the assumption that
839 # input and output error in each iteration can be expected to be
840 # related by a simple power law (because it'll just be down to how
841 # many leading terms of a Taylor series are zero), the cube root
842 # was the next thing to try.
843 threshold = 2^(-epsbits/3)
847 # Find all the error extrema we can.
848 function compute_error(x)
850 approx_y = ratfn_eval(nc, dc, x)
851 return (approx_y - real_y) * w(x, real_y)
853 extrema = find_extrema(compute_error, grid)
854 @debug("minimax", "all extrema = ", format_xylist(extrema))
856 # Winnow the extrema down to the right number, and ensure they
857 # have alternating sign.
858 extrema = winnow_extrema(extrema, n+d+2)
859 @debug("minimax", "winnowed extrema = ", format_xylist(extrema))
861 # See if we've finished.
862 min_err = minimum([abs(y) for (x,y) = extrema])
863 max_err = maximum([abs(y) for (x,y) = extrema])
864 variation = (max_err - min_err) / max_err
865 @debug("minimax", "extremum variation = ", variation)
866 if variation < threshold
867 @debug("minimax", "done!")
868 return nc, dc, max_err, extrema
871 # If not, refine our function by equalising the error at the
872 # extrema points, and go round again.
873 (nc, dc) = ratfn_equal_deviation(f, map(x->x[1], extrema),
875 @debug("minimax", "refined approx = ", ratfn_to_string(nc, dc))
879 # ----------------------------------------------------------------------
880 # Check if a polynomial is well-conditioned for accurate evaluation in
881 # a given interval by Horner's rule.
883 # This is true if at every step where Horner's rule computes
884 # (coefficient + x*value_so_far), the constant coefficient you're
885 # adding on is of larger magnitude than the x*value_so_far operand.
886 # And this has to be true for every x in the interval.
889 # coeffs The coefficients of the polynomial under test. Starts with
890 # the constant term, i.e. coeffs[i] is the coefficient of
891 # x^(i-1) (because Julia arrays are 1-based).
892 # lo, hi The bounds of the interval.
894 # Return value: the largest ratio (x*value_so_far / coefficient), at
895 # any step of evaluation, for any x in the interval. If this is less
896 # than 1, the polynomial is at least somewhat well-conditioned;
897 # ideally you want it to be more like 1/8 or 1/16 or so, so that the
898 # relative rounding error accumulated at each step are reduced by
899 # several factors of 2 when the next coefficient is added on.
901 function wellcond(coeffs, lo, hi)
902 x = max(abs(lo), abs(hi))
905 for i = length(coeffs):-1:1
906 coeff = abs(coeffs[i])
909 thisval = so_far / coeff
910 worst = max(worst, thisval)
917 # ----------------------------------------------------------------------
918 # Small set of unit tests.
924 function approx_eq(x, y, limit=1e-6)
925 return abs(x - y) < limit
928 function test(condition)
937 # Test Gaussian elimination.
938 println("Gaussian test 1:")
939 m = BigFloat[1 1 2; 3 5 8; 13 34 21]
940 v = BigFloat[1, -1, 2]
942 println(" ",repr(ret))
943 test(approx_eq(ret[1], 109/26))
944 test(approx_eq(ret[2], -105/130))
945 test(approx_eq(ret[3], -31/26))
947 # Test leastsquares rational functions.
948 println("Leastsquares test 1:")
950 a = array1d(BigFloat, n+1)
952 a[1+i] = i/BigFloat(n)
954 (nc, dc) = ratfn_leastsquares(x->exp(x), a, 2, 2)
955 println(" ",ratfn_to_string(nc, dc))
957 test(approx_eq(exp(x), ratfn_eval(nc, dc, x), 1e-4))
960 # Test golden section search.
961 println("Golden section test 1:")
962 x, y = goldensection(x->sin(x),
963 BigFloat(0), BigFloat(1)/10, BigFloat(4))
964 println(" ", x, " -> ", y)
965 test(approx_eq(x, asin(BigFloat(1))))
966 test(approx_eq(y, 1))
968 # Test extrema-winnowing algorithm.
969 println("Winnow test 1:")
970 extrema = [(x, sin(20*x)*sin(197*x))
971 for x in BigFloat(0):BigFloat(1)/1000:BigFloat(1)]
972 winnowed = winnow_extrema(extrema, 12)
973 println(" ret = ", format_xylist(winnowed))
978 test(prevy * y <= 0) # tolerates initial prevx having no sign
983 # Test actual minimax approximation.
984 println("Minimax test 1 (polynomial):")
985 (nc, dc, e, x) = ratfn_minimax(x->exp(x), (BigFloat(0), BigFloat(1)), 4, 0)
987 println(" ",ratfn_to_string(nc, dc))
989 for x = 0:BigFloat(1)/1000:1
990 test(abs(ratfn_eval(nc, dc, x) - exp(x)) <= e * 1.0000001)
993 println("Minimax test 2 (rational):")
994 (nc, dc, e, x) = ratfn_minimax(x->exp(x), (BigFloat(0), BigFloat(1)), 2, 2)
996 println(" ",ratfn_to_string(nc, dc))
998 for x = 0:BigFloat(1)/1000:1
999 test(abs(ratfn_eval(nc, dc, x) - exp(x)) <= e * 1.0000001)
1002 println("Minimax test 3 (polynomial, weighted):")
1003 (nc, dc, e, x) = ratfn_minimax(x->exp(x), (BigFloat(0), BigFloat(1)), 4, 0,
1006 println(" ",ratfn_to_string(nc, dc))
1008 for x = 0:BigFloat(1)/1000:1
1009 test(abs(ratfn_eval(nc, dc, x) - exp(x))/exp(x) <= e * 1.0000001)
1012 println("Minimax test 4 (rational, weighted):")
1013 (nc, dc, e, x) = ratfn_minimax(x->exp(x), (BigFloat(0), BigFloat(1)), 2, 2,
1016 println(" ",ratfn_to_string(nc, dc))
1018 for x = 0:BigFloat(1)/1000:1
1019 test(abs(ratfn_eval(nc, dc, x) - exp(x))/exp(x) <= e * 1.0000001)
1022 println("Minimax test 5 (rational, weighted, odd degree):")
1023 (nc, dc, e, x) = ratfn_minimax(x->exp(x), (BigFloat(0), BigFloat(1)), 2, 1,
1026 println(" ",ratfn_to_string(nc, dc))
1028 for x = 0:BigFloat(1)/1000:1
1029 test(abs(ratfn_eval(nc, dc, x) - exp(x))/exp(x) <= e * 1.0000001)
1032 total = passes + fails
1033 println(passes, " passes ", fails, " fails ", total, " total")
1036 # ----------------------------------------------------------------------
1042 remez.jl [options] <lo> <hi> <n> <d> <expr> [<weight>]
1048 Bounds of the interval on which to approximate the target
1049 function. These are parsed and evaluated as Julia expressions,
1050 so you can write things like '1/BigFloat(6)' to get an
1051 accurate representation of 1/6, or '4*atan(BigFloat(1))' to
1052 get pi. (Unfortunately, the obvious 'BigFloat(pi)' doesn't
1057 The desired degree of polynomial(s) you want for your
1058 approximation. These should be non-negative integers. If you
1059 want a rational function as output, set <n> to the degree of
1060 the numerator, and <d> the denominator. If you just want an
1061 ordinary polynomial, set <d> to 0, and <n> to the degree of
1062 the polynomial you want.
1066 A Julia expression giving the function to be approximated on
1067 the interval. The input value is predefined as 'x' when this
1068 expression is evaluated, so you should write something along
1069 the lines of 'sin(x)' or 'sqrt(1+tan(x)^2)' etc.
1073 If provided, a Julia expression giving the weighting factor
1074 for the approximation error. The output polynomial will
1075 minimise the largest absolute value of (P-f) * w at any point
1076 in the interval, where P is the value of the polynomial, f is
1077 the value of the target function given by <expr>, and w is the
1078 weight given by this function.
1080 When this expression is evaluated, the input value to P and f
1081 is predefined as 'x', and also the true output value f(x) is
1082 predefined as 'y'. So you can minimise the relative error by
1083 simply writing '1/y'.
1085 If the <weight> argument is not provided, the default
1086 weighting function always returns 1, so that the polynomial
1087 will minimise the maximum absolute error |P-f|.
1089 Computation options:
1093 Evaluate the Julia expression <predef_expr> before starting
1094 the computation. This permits you to pre-define variables or
1095 functions which the Julia expressions in your main arguments
1096 can refer to. All of <lo>, <hi>, <expr> and <weight> can make
1097 use of things defined by <predef_expr>.
1099 One internal remez.jl function that you might sometimes find
1100 useful in this expression is 'goldensection', which finds the
1101 location and value of a maximum of a function. For example,
1102 one implementation strategy for the gamma function involves
1103 translating it to put its unique local minimum at the origin,
1104 in which case you can write something like this
1106 --pre='(m,my) = goldensection(x -> -gamma(x),
1107 BigFloat(1), BigFloat(1.5), BigFloat(2))'
1109 to predefine 'm' as the location of gamma's minimum, and 'my'
1110 as the (negated) value that gamma actually takes at that
1111 point, i.e. -gamma(m).
1113 (Since 'goldensection' always finds a maximum, we had to
1114 negate gamma in the input function to make it find a minimum
1115 instead. Consult the comments in the source for more details
1116 on the use of this function.)
1118 If you use this option more than once, all the expressions you
1119 provide will be run in sequence.
1123 Specify the accuracy to which you want the output polynomial,
1124 in bits. Default 256, which should be more than enough.
1126 --bigfloatbits=<bits>
1128 Turn up the precision used by Julia for its BigFloat
1129 evaluation. Default is Julia's default (also 256). You might
1130 want to try setting this higher than the --bits value if the
1131 algorithm is failing to converge for some reason.
1137 Instead of just printing the approximation function itself,
1138 also print auxiliary information:
1139 - the locations of the error extrema, and the actual
1140 (weighted) error at each of those locations
1141 - the overall maximum error of the function
1142 - a 'well-conditioning quotient', giving the worst-case ratio
1143 between any polynomial coefficient and the largest possible
1144 value of the higher-order terms it will be added to.
1146 The well-conditioning quotient should be less than 1, ideally
1147 by several factors of two, for accurate evaluation in the
1148 target precision. If you request a rational function, a
1149 separate well-conditioning quotient will be printed for the
1150 numerator and denominator.
1152 Use this option when deciding how wide an interval to
1153 approximate your function on, and what degree of polynomial
1156 --variable=<identifier>
1158 When writing the output polynomial or rational function in its
1159 usual form as an arithmetic expression, use <identifier> as
1160 the name of the input variable. Default is 'x'.
1164 When writing the output polynomial or rational function in its
1165 usual form as an arithmetic expression, write <suffix> after
1166 every floating-point literal. For example, '--suffix=F' will
1167 generate a C expression in which the coefficients are literals
1168 of type 'float' rather than 'double'.
1172 Instead of writing the output polynomial as an arithmetic
1173 expression in Horner's rule form, write out just its
1174 coefficients, one per line, each with a trailing comma.
1175 Suitable for pasting into a C array declaration.
1177 This option is not currently supported if the output is a
1178 rational function, because you'd need two separate arrays for
1179 the numerator and denominator coefficients and there's no
1180 obviously right way to provide both of those together.
1182 Debug and test options:
1186 Enable debugging output from various parts of the Remez
1187 calculation. <facility> should be the name of one of the
1188 classes of diagnostic output implemented in the program.
1189 Useful values include 'gausselim', 'leastsquares',
1190 'goldensection', 'equaldev', 'minimax'. This is probably
1191 mostly useful to people debugging problems with the script, so
1192 consult the source code for more information about what the
1193 diagnostic output for each of those facilities will be.
1195 If you want diagnostics from more than one facility, specify
1196 this option multiple times with different arguments.
1200 Run remez.jl's internal test suite. No arguments needed.
1202 Miscellaneous options:
1206 Display this text and exit. No arguments needed.
1211 # ----------------------------------------------------------------------
1215 nargs = length(argwords)
1216 if nargs != 5 && nargs != 6
1217 error("usage: remez.jl <lo> <hi> <n> <d> <expr> [<weight>]\n" *
1218 " run 'remez.jl --help' for more help")
1221 for preliminary_command in preliminary_commands
1222 eval(Meta.parse(preliminary_command))
1225 lo = BigFloat(eval(Meta.parse(argwords[1])))
1226 hi = BigFloat(eval(Meta.parse(argwords[2])))
1227 n = parse(Int,argwords[3])
1228 d = parse(Int,argwords[4])
1229 f = eval(Meta.parse("x -> " * argwords[5]))
1231 # Wrap the user-provided function with a function of our own. This
1232 # arranges to detect silly FP values (inf,nan) early and diagnose
1233 # them sensibly, and also lets us log all evaluations of the
1234 # function in case you suspect it's doing the wrong thing at some
1235 # special-case point.
1238 @debug("f", x, " -> ", y)
1240 error("f(" * string(x) * ") returned non-finite value " * string(y))
1246 # Wrap the user-provided weight function similarly.
1247 w = eval(Meta.parse("(x,y) -> " * argwords[6]))
1248 function wrapped_weight(x,y)
1251 error("w(" * string(x) * "," * string(y) *
1252 ") returned non-finite value " * string(ww))
1256 weight = wrapped_weight
1258 weight = (x,y)->BigFloat(1)
1261 (nc, dc, e, extrema) = ratfn_minimax(func, (lo, hi), n, d, weight)
1264 functext = join([string(x)*",\n" for x=nc],"")
1266 # It's unclear how you should best format an array of
1267 # coefficients for a rational function, so I'll leave
1268 # implementing this option until I have a use case.
1269 error("--array unsupported for rational functions")
1272 functext = ratfn_to_string(nc, dc) * "\n"
1275 # Print everything you might want to know about the function
1276 println("extrema = ", format_xylist(extrema))
1277 println("maxerror = ", string(e))
1279 println("wellconditioning_numerator = ",
1280 string(wellcond(nc, lo, hi)))
1281 println("wellconditioning_denominator = ",
1282 string(wellcond(dc, lo, hi)))
1284 println("wellconditioning = ", string(wellcond(nc, lo, hi)))
1286 print("function = ", functext)
1288 # Just print the text people will want to paste into their code
1293 # ----------------------------------------------------------------------
1294 # Top-level code: parse the argument list and decide what to do.
1299 argwords = array1d(String, 0)
1301 global doing_opts, what_to_do, argwords
1302 global full_output, array_format, xvarname, floatsuffix, epsbits
1303 if doing_opts && startswith(arg, "-")
1306 elseif arg == "--help"
1308 elseif arg == "--test"
1310 elseif arg == "--full"
1312 elseif arg == "--array"
1314 elseif startswith(arg, "--debug=")
1315 enable_debug(arg[length("--debug=")+1:end])
1316 elseif startswith(arg, "--variable=")
1317 xvarname = arg[length("--variable=")+1:end]
1318 elseif startswith(arg, "--suffix=")
1319 floatsuffix = arg[length("--suffix=")+1:end]
1320 elseif startswith(arg, "--bits=")
1321 epsbits = parse(Int,arg[length("--bits=")+1:end])
1322 elseif startswith(arg, "--bigfloatbits=")
1323 set_bigfloat_precision(
1324 parse(Int,arg[length("--bigfloatbits=")+1:end]))
1325 elseif startswith(arg, "--pre=")
1326 push!(preliminary_commands, arg[length("--pre=")+1:end])
1328 error("unrecognised option: ", arg)
1331 push!(argwords, arg)