Skip to content

Math RealInterval

Serge Stinckwich edited this page Jun 26, 2018 · 1 revision

this package has one nasty property: It needs to overwrite some standard methods, that are also used eg by the user interface of Pharo, in order to work. I do realize that overwriting these methods is very bad style and public projects don't do it for obvious reasons: If several packages do this, only the last one loaded would work. hence, when it is loaded, it does not do this overwriting automatically:

for the package to work you have to set enableRealInterval in the settings-menu to true. if you have done your calculations you can, if you so want (it should not be really necessary), set enableRealInterval to false and the overwriting will be reversed.

for further info see this more detailed explanation.

RealIntervals & IntervalUnions

RealIntervals are simply closed intervals of real numbers. They are essentially polymorph to Numbers, when it comes to calculations (as are IntervalUnions).

I guess the most important thing to mention is, that this implementation does not take floating point errors into account! You find a more detailed discussion of this serious shortcoming in the Implementation Notes. This is the most important difference to real interval implementations. Since this does not allow the reliable computing of errors, an important and probably the most obvious use of interval calculation, one may wonder why I implemented this thing at all. But interval calculation can be used for other things as well and I will show a few not completely useless things in the Examples part.

Instance Creation

The standard way to create a RealInterval is to use inf:sup:

a:=RealInterval inf: 2.8 sup: 3."--> [2.8,3]" 
"if it is used incorrectly, usually no Error is produced 
 but an empty Interval is returned, eg:"
RealInterval inf: 2.8 sup: 2."--> [empty]"
"also of course this is not possible:"
RealInterval inf: Float infinity sup: Float infinity "-->[empty]"
"and this is the standard way to make an empty interval: "
RealInterval new."--> [empty]"
"there are a few special cases and utility cases:"
RealInterval entire ."--> [Float infinity negated,Float infinity]"
RealInterval fromNumber: 3."--> [3,3]"
RealInterval fromNumber: 3 withUncertainty: 0.8."--> [2.2,3.8]"
RealInterval big."--> [-10000000000,10000000000] this one is useful in several algos
and used as a default there, if one hasnt a special idea where to look for a solution.
Float fmax is intentionally not used, as this results often in infinite solutions in
those algos and often they cant cope decently with those results"

But generally I find it most simple to calculate a hull around numbers to produce a RealInterval :

1 hull."--> [1,1]"
2 hull: 3."--> [2,3]"
4 hull:Float infinity negated."--> [Float infinity negated,4]"

IntervalUnions are SortedCollections and I construct them usually with with:

a."--> [2.8,3]"
b:=IntervalUnion with:a with: (2.9 hull:5) with: (-2 hull: -3) 
                 with: RealInterval new with:7."-->
an IntervalUnion([-3,-2]u[2.8,5]u[7,7])"
"additionally you can use #from:to:"
IntervalUnion from: -2 to: 2."--> an IntervalUnion([-2,-2]u[-1,-1]u[0,0]u[1,1]u[2,2])"

You should not use anything from SortedCollection apart from add: or addAll: to add another Number or RealInterval or IntervalUnion, so that the sorting order does not get shuffled.

b add: (IntervalUnion with:6 with: -2.5 with: (8 hull:7)).
b."--> an IntervalUnion([-3,-2]u[2.8,5]u[6,6]u[7,8])"

Modifying an Interval

You can change a RealInterval or IntervalUnion simply by using inf: or sup:

a."-->[2.8,3]"
a inf: 2.
a."-->[2,3]"
a sup:2.7.
"#size does not work here, instead you have to use #width"
a width." 0.7000000000000002"
a."-->[2,2.7]"
a inf:Float nan.
a."-->[empty]"
a width." Float nan" 
a sup:4.9.
a."-->[4.9,4.9]"
a width."--> 0.0" 
a sup:4.
"and since it must be that sup>=inf, you get:"
a. [empty]
a sup."-->Float nan"
a inf."-->Float nan" 
b."--> an IntervalUnion([-3,-2]u[2.8,5]u[6,6]u[7,8])"
b inf: -22.
b."--> an IntervalUnion([-22,-2]u[2.8,5]u[6,6]u[7,8])"
"width simply adds the widths of the RealIntervals"
b width."--> 23.2"
"you can get the whole width this way:"
b hull width."--> 30"
b inf: 2.9.
b."--> an IntervalUnion([2.9,5]u[6,6]u[7,8])" 
b inf:5.9.
b."--> an IntervalUnion([5.9,6]u[7,8])"
b sup:Float nan.
b."-->an IntervalUnion()"
b width."-->Float nan"

Accessors

Apart from inf , sup and width there are several other accessors, I'll show just two of them:

(2 hull:3)midPoint ."-->2.5"
b:=IntervalUnion  with: (3 hull:5) with: (-2 hull: -3) with:7.
b midPoint ."-->3.0"

In IntervalUnions midpoint calculates the nearest point in that union.

projectionFrom: returns the nearest number in the receiver of the same type as the argument, or Float nan if such a number does not exist.

(a:=(1.8 hull:3)) projectionFrom: 0."-->2"
(c:=(0.4 hull:0.8)) projectionFrom: 1."-->Float nan" "no Integer in there"
c projectionFrom: (3/2)."-->(3602879701896397/4503599627370496)" "<g> well ok,
the common floating point error: 0.8-(3602879701896397/4503599627370496) -->0.0"
a projectionFrom: 2.3."-->2.3"
b projectionFrom: Float infinity ."-->7.0"
b projectionFrom: 2 . "-->3"

Set Operations

Apart from hull and hull:, that I already demonstrated, there are the usual union:, intersection: and difference:.

b."--> an IntervalUnion([-3,-2]u[3,5]u[7,7])"
1 union:b."--> an IntervalUnion([-3,-2]u[1,1]u[3,5]u[7,7])"
(1 hull:2)union: (3 hull:4)."--> an IntervalUnion([1,2]u[3,4])"
b union: (-2.8 hull:8)."--> an IntervalUnion([-3,8])"

Of course it would have been nicer to get [-3,8] as a result here, but the calculation would be slower, and the form of the result does not matter:

(IntervalUnion with: (-3 hull:8)) = (-3 hull:8)."-->true"
(IntervalUnion with: -8) = -8. "-->true"
3 hull = 3. "-->true"

Let's continue with some intersection: examples:

(4 hull:8) intersection: b."--> an IntervalUnion([4,5]u[7,7])"
b intersection: (3 hull: -2)."-->an IntervalUnion([-2,-2]u[3,3])"

With difference: there is some speciality, or bug - if you so want -, insofar as this operation would usually produce a half-open interval, but this is not taken care of and the usual RealInterval is returned. This has advantages in the later described algos. The same general principle is for example applied if an operation results in an interval with a single number excluded, which should result in an IntervalUnion of two adjacent half-open intervals, but such an operation usually returns one simple RealInterval!

(3 hull:5) difference:4."--> [3,5]"
(3 hull:5) difference: (2hull:4)."--> [4,5]"
b difference:(-2.5 hull:4)."--> an IntervalUnion([-3,-2.5]u[4,5]u[7,7])"
b difference: (IntervalUnion with:(-2.5 hull:4)with:(4.4 hull:8))."-->
 an IntervalUnion([-3,-2.5]u[4,4.4])"

Mathematical Operations

Binary mathematic operations apply the operator to each combination of numbers in the receiver and numbers in the argument. Numbers, RealIntervals and IntervalUnions can be mixed. There exist a few exceptions where the argument needs to be a number eg raisedToInteger:, where the argument needs to be an Integer. Most common operations are implemented. A few examples:

a:=(2 hull: 3).
1+a."--> [3,4]"
a+a."--> [4,6]"
a*2."--> [4,6]"
a negated."--> [-3,-2]"
b := a / (-1 hull:1) / 2."--> 
an IntervalUnion([Float infinity negated,-1]u[1,Float infinity])"
a sign."--> 1"
b sign."--> an IntervalUnion([-1,-1]u[1,1])"
b add:0.
b sign."--> an IntervalUnion([-1,-1]u[0,0]u[1,1])"
2.5 max:a."--> [2.5,3]"
(-2 hull:1)abs."--> [0,2]"
a raisedTo: (-1 hull:1). "--> [(1/3),3]"
(-1.5 hull:3) raisedTo: 3."--> [-3.375,27]"
a sup:5.
"with the trigonometric functions one needs to be very careful with 
interval-boundaries near to multiples of half pi, one can get really surprising
results due to floating point errors"
a sin."--> [-1,0.9092974268256817]"
a sup:4.
a exp ln."--> [2.0,4.0]"
a tan."--> [-2.1850398632615193,1.157821282349578]"
a:=(-1hull:1)."--> [-1,1]"
a cos."--> [0.5403023058681397,1]"
a sqrt."--> [0,1]"
a inf: -2.
a squared."--> [0,4]"

IntervalBox

An IntervalBox is simply an Array of RealIntervals or IntervalUnions, that can be used as a vector of intervals in the algos, as these algos need some additional convenience methods not implemented in Array. Their principal difference to IntervalUnions is that they are considered to be multidimensional vectors not one-dimensional unions of intervals.

a:=IntervalBox new:3."--> an IntervalBox([empty] [empty] [empty])"
b:=IntervalBox with:5 with: (3 hull:4) with: (2 hull:1)."--> 
 an IntervalBox([5,5] [3,4] [1,2])"
a hull:b."--> an IntervalBox([5,5] [3,4] [1,2])"
b includes:a."--> true"
a includes: b."--> false"
b inf."--> #(5 3 1)"
b midPoint ."--> #(5.0 3.5 1.5)"

Apart from a few convenience methods they have exactly the same functionality as Arrays, hence they need no further explanation.

Their use is shown in the Examples.