DEMOARITHMETIC Interval arithmetic in INTLAB

Contents

Redefinition of the interval arithmetic

With Version 6 of INTLAB I redefined the interval arithmetic in INTLAB. Formerly, infinity was allowed to be a member of an interval, now intervals are strictly subsets of the set of real or complex numbers.

Most users won't recognize any change. Remarkably, no change was necessary in all previous demos in INTLAB. The new definition is more clear from a mathematical point of view. And it implies the nice rule

  0 * X = 0

for any interval X without exception.

I recommend to visit

  " DEMOINTVAL  Interval operations in INTLAB "

first before continuing with this demo.

Definition of intervals

There are two major changes: the definition of intervals, and the definition of interval arithmetic. First to the definition of intervals. Mathematically, I define a real interval to be a closed and connected subset of the real numbers. As explained in a moment, two modes are defined. With this new definition of intervals, an interval like

     intvalinit('DisplayInfSup',0)
     format short
     X = infsup(3,inf)
intval X = 
[    3.0000,       Inf] 

represents the set of real numbers greater or equal to 3:

  X = { x real : 3 <= x }

The right endpoint inf of X is not an element of X (nevertheless, X is closed in the mathematical sense). Consequently,

     Y = 0 * X
intval Y = 
[    0.0000,    0.0000] 

yields the zero interval [0,0] (formerly NaN), and

     contains_3 = in(3,X)
     contains_inf = in(inf,X)
contains_3 =
     1
contains_inf =
     0

because inf is not a member of X. A complex interval is a closed circle as a subset of the complex numbers. Again, 0*Z is the zero interval < 0,0 > for any complex interval Z.

Definition of interval arithmetic

With the new definition, there are two modes of interval arithmetic, call it the "NaN"-mode and the "ignore"-mode. The NaN-mode is the default.

The new definition concerns what happens with input out of range.

The NaN-mode, the default

In the NaN-mode, a result NaN is to be interpreted as "Not an Interval" or "an invalid operation occurred". The result of an operation is NaN if and only if the operation x op y is not well-defined for all x in X and all y in Y. A typical example is

     intvalinit('InputOutOfRangeToNaN')
     X = infsup(0,1)
     Y = 1/X
===> Result NaN for input out of range (the NaN-mode); 
        out-of-range flag is reset
intval X = 
[    0.0000,    1.0000] 
intval Y = 
[       NaN,       NaN] 

In the NaN-mode, the result is NaN no matter whether some or all input is out of range. So

     1/intval(0)
intval ans = 
[       NaN,       NaN] 

yields NaN as well. As we will see, this is the safe mode.

The ignore-mode, for experts

In the ignore-mode, a result NaN is to be interpreted as the "empty set". First, all input out of range is removed and then the remaining set of operations is included. If some input was out-of-range, a flag is set. If nothing remains, the result is empty:

     intvalinit('InputOutOfRangeIgnore')
     1/infsup(-1,1)
     0/infsup(-1,1)
     0/intval(0)
===> Caution: Input out of range ignored (the ignore-mode); out-of-range flag is reset
intval ans = 
[     - Inf,       Inf] 
intval ans = 
[    0.0000,    0.0000] 
intval ans = 
[       NaN,       NaN] 

In other words, for all operations and functions, an inclusion of

  { f(x)  :  x in X  and  x in Def(f) }

will be computed in the ignore-mode. The difference between the two modes is how an input out-of-range is handled and signalled.

Why ignoring input out of range, where is the beef?

This gives a nice and clean definition of interval operations. Consider

     X1 = infsup(-1,0)
     X2 = infsup(0,1)
intval X1 = 
[   -1.0000,    0.0000] 
intval X2 = 
[    0.0000,    1.0000] 

What is the result of 1/X1 and 1/X2? Ignoring input out of range we obtain

     Y1 = 1/X1
     Y2 = 1/X2
intval Y1 = 
[     - Inf,   -1.0000] 
intval Y2 = 
[    1.0000,       Inf] 

This seems natural. The input 0 is out of range and ignored in both cases. However, when the out-of-range input 0 would have not been ignored, then in the first case 1/0 would have been interpreted as -inf, but as +inf in the second case. This is why in previous versions of INTLAB I had to define 1/X1 and 1/X2 to be [-inf,inf].

Moreover, as has been mentioned, multiplication by zero is always zero:

     Z1 = 0 * Y1
     Z2 = 0 * Y2
     Z3 = 0 * infsup(-inf,inf)
intval Z1 = 
[    0.0000,    0.0000] 
intval Z2 = 
[    0.0000,    0.0000] 
intval Z3 = 
[    0.0000,    0.0000] 

Why is a flag necessary?

In a number of applications it is useful to ignore input out of range. Although this definition is nice and clean, it needs some care. Mathematical assertions may only be true if no input out-of-range appeared during the calculation: A number of verification methods use Brouwer's fixed point theorem. It says that a continuous self-mapping of a non-empty, compact subset of the R^n has a fixed point. Consider

  f(x) = sqrt(x) - 1 .

There is no real x with f(x)=x. Ignoring input out of range, we obtain (for "RealStdFctsExcptnMode", see below)

     intvalinit('InputOutOfRangeIgnore')
     intvalinit('RealStdFctsExcptnMode')
     X = infsup(-2,9)
     Y = sqrt(X)-1
===> Caution: Input out of range ignored (the ignore-mode); out-of-range flag is reset
===> Input out-of-range for standard functions handled according to 
        NaN/ignore-mode (currently ignore-mode)
intval X = 
[   -2.0000,    9.0000] 
intval Y = 
[   -1.0000,    2.0000] 

and Y is contained in X, pretending f(X) is contained in X. No error message occurred. Hence we might conclude that f has a fixed point in X.

For a safe mathematical assertion, Y in X is not enough: We also need to check that X is non-empty and no input out-of-range occurred. Since the latter may have happened anywhere in a complicated computation, an out-of-range flag is mandatory.

Note that the flag is set independent of the mode. Changing the mode resets the flag, and hecking the flag also resets it.

Why two modes?

A casual user may forget to check the out-of-range flag (or might not even know of it). Therefore I found it necessary to define a safe default mode. This is the NaN-mode. Any operation with NaN produces a NaN. Also a NaN can never be enclosed in some interval, hence in(Y,X) is false in the example above.

How to use the NaN-mode?

The default NaN-mode can be used without any precautions. If during the computation of a result all operations are well-defined, the computed result is a true inclusion of the correct result.

If during the computation the input of some operation was out-of-range, the computed result will be NaN. More precisely, the corresponding components will be NaN, as in

     intvalinit('InputOutOfRangeToNaN')
     X = -1:1
     Y = 1./intval(X)
===> Result NaN for input out of range (the NaN-mode); 
        out-of-range flag is reset
X =
    -1     0     1
intval Y = 
[   -1.0000,   -1.0000] [       NaN,       NaN] [    1.0000,    1.0000] 

How to use the ignore-mode?

First switch to the ignore-mode. This will also reset the out-of-range flag.

     intvalinit('InputOutOfRangeIgnore')
===> Caution: Input out of range ignored (the ignore-mode); out-of-range flag is reset

If during a computation some input was out of range and therefore ignored, the flag will be set. For example,

     intvalinit('display_')
     X = intval(1:3)
     Y = X-2
     flag1 = GetAndResetOutOfRangeFlag
     Z = 1./Y
     flag2 = GetAndResetOutOfRangeFlag
===> Default display of intervals with uncertainty (e.g. 3.14_), changed 
        to inf/sup or mid/rad if input too wide 
intval X = 
    1.0000    2.0000    3.0000
intval Y = 
   -1.0000    0.0000    1.0000
flag1 =
     0
intval Z = 
   -1.0000       NaN    1.0000
flag2 =
     1

If the flag is not set, then no out-of-range operation occurred in the whole computation. Again note that the flag is set both in the NaN- and the ignore-mode. For example,

     intvalinit('displayinfsup',0)
     intvalinit('InputOutOfRangeToNaN')
     flag1 = GetAndResetOutOfRangeFlag
     X = midrad( -1:1 , 5e-5 )
     1./intval(X)
     flag2 = GetAndResetOutOfRangeFlag
===> Result NaN for input out of range (the NaN-mode); 
        out-of-range flag is reset
flag1 =
     0
intval X = 
[   -1.0001,   -0.9999] [   -0.0001,    0.0001] [    0.9999,    1.0001] 
intval ans = 
[   -1.0001,   -0.9999] [       NaN,       NaN] [    0.9999,    1.0001] 
flag2 =
     1

What is the difference between the two modes?

In the NaN-mode, the default and safe mode, the individual interval components carry the information whether or not some input out-of-range was ignored.

     intvalinit('InputOutOfRangeToNaN',0);
     X = [ infsup(2,3) infsup(-1,4) -2 ]
     sqrt(X)
intval X = 
[    2.0000,    3.0000] [   -1.0000,    4.0000] [   -2.0000,   -2.0000] 
intval ans = 
[    1.4142,    1.7321] [       NaN,       NaN] [       NaN,       NaN] 

In the ignore-mode, the expert mode, only the global information whether or not somewhere an input out-of-range occurred is available. However, the individual components show the result for all input inside-range.

     intvalinit('InputOutOfRangeIgnore',0);
     X = [ infsup(2,3) infsup(-1,4) -2 ]
     sqrt(X)
     flag = GetAndResetOutOfRangeFlag
intval X = 
[    2.0000,    3.0000] [   -1.0000,    4.0000] [   -2.0000,   -2.0000] 
intval ans = 
[    1.4142,    1.7321] [    0.0000,    2.0000] [       NaN,       NaN] 
flag =
     1

Real standard functions

Previously, I introduced an extra flag "RealStdFctsExcptnIgnore" to indicate that input out-of-range for a real standard function is to be ignored. This is now obsolete and merged into the NaN- and ignore-mode. For example,

     intvalinit('RealStdFctsExcptnMode')
     X = [ infsup(2,3) infsup(-1,4) ]
     intvalinit('InputOutOfRangeToNaN',0);
     sqrt(X)
     flag1 = GetAndResetOutOfRangeFlag
     intvalinit('InputOutOfRangeIgnore',0);
     sqrt(X)
     flag2 = GetAndResetOutOfRangeFlag
===> Input out-of-range for standard functions handled according to 
        NaN/ignore-mode (currently ignore-mode)
intval X = 
[    2.0000,    3.0000] [   -1.0000,    4.0000] 
intval ans = 
[    1.4142,    1.7321] [       NaN,       NaN] 
flag1 =
     1
intval ans = 
[    1.4142,    1.7321] [    0.0000,    2.0000] 
flag2 =
     1

Note, however, that real standard functions can be switched automatically to their complex pendant as follows:

     intvalinit('RealStdFctsExcptnWarn')
     X = [ infsup(2,3) infsup(-1,4) ]
     intvalinit('InputOutOfRangeToNaN',0);
     midrad(sqrt(X))
     flag = GetAndResetOutOfRangeFlag
===> Complex interval stdfct used automatically for real interval input 
        out of range, but with warning
intval X = 
[    2.0000,    3.0000] [   -1.0000,    4.0000] 
intval  = 
<   1.5731 +  0.0000i,  0.1590> <   1.2247 +  0.0000i,  1.5812> 
flag =
     0

The warning may be suppressed using "intvalinit('RealStdFctsExcptnAuto')".

Complex standard functions

It seems difficult to find a consistent definition when to set the out-of-range flag for complex standard functions. In particular, how should it be interpreted when the input interval covers the branch cut, or is at its boundary. Therefore I choose not to set the out-of-range flag for complex standard functions, independent of the input.

The empty set

In contrast to the ignore-mode, the empty set does not exist in the NaN-mode. This is a necessary compromise. Since NaN is interpreted as Not-an-Interval, another representation of the empty set would have been necessary.

However, the only operation in the NaN-mode, the result of which may be the empty set is intersection. However, numerous case distinctions in many programs would have been necessary to distinguish between NaN and the empty set - whatever the representation of the latter may be. All operations would be slowed down by that.

I think this is not worth it. If in some application computations with the empty set are important, please use the ignore-mode.

Enjoy INTLAB.

     intlablogo(35)