[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [srfi-70] Limit

>  Function: limit proc z1 z2
>      Proc must be a procedure taking a single inexact argument.
>      z2 should be chosen so that proc is expected to be monotonic or
>      constant on arguments between z1 and z1 + z2.
>      Limit computes the limit of proc as its argument approaches z1
>      from z1 + z2.  Limit returns a complex number or real infinity
>      or `#f'.

This I like a lot better.

> LIMIT was created so that static choices for limit cases like:
>   (expt 0 0)                                    ==> 1
> or
>   (expt 0 0)                                    ==> 0/0
> don't necessitate workarounds when computing with functions like
> (lambda (x) (expt x x)):

>   (limit (lambda (x) (expt x x)) 0 1e-9)        ==> 1/0

Unfortunately, my experience is that this approach is highly unreliable.
In the end, I spent more time doing analytical sanity checks myself than
it took to write the proper numerical code directly after understanding
the limits properly.

Example: An important function from information theory is

        f(x) =  -x log(x).

This function is in principle well behaved (smooth, analytic, etc.) on (0,1],
but its derivative does not exist at x = 0. Moreover, f(0) cannot directly be
computed numerically because the underflow from log(x) is not cancelled by the
multiplication with zero. Practical numerical code: IF x < xmin THEN 0 ELSE x log(x),
where xmin is chosen minimal such that log(xmin) is an ordinary number and
not -infinity.

Using LIMIT in this case is not a good idea for two reasons:
a) It is expensive and unnecessary, except for very small x.
b) At least the reference implementation of LIMIT doesn't get it right:

        (limit (lambda (x) (* -1 x (log x))) 0 1e-9) => -inf.0

This may be a bug in the reference implementation, but it is certainly a
violation of the new specification as f(x) is monotonic on [0,1/e].

When you try to fix the reference implementation, you will find that it
cannot be fixed because it comes from the "black box" procedure:
At a certain moment f(x) becomes -inf.0, so that must be the limit.

I can relate another experience: The Mathematica system has an operation
Limit[], which finds limits symbolically, and a function NLimit[] which finds
limits numerically. Limits[] turns out to be useful sometimes, but it took
many years and many releases until it became something I nearly trust.
NLimits[] on the other hand is tricky, even though it makes an effort to report
when it fails, e.g. NLimit[x Log[x], x -> 0.] => "cannot recognize limit".

Bottom line: In the end, LIMIT might do more harm than it is worth.
You might want to reconsider if it is a feature that is essential for
the Scheme programming language itself.