Interval Computations¶
This chapter present the basic structures used to represent sets and the operations directly related to them.
Intervals, vectors and matrices¶
Ibex allows you to create interval, interval vectors, interval matrices and array of interval matrices. All these objects represent sets. These objects are respectively of the following classes:
Interval
IntervalVector
IntervalMatrix
IntervalMatrixArray
.
It should be emphasized that, mainly for efficiency reasons, the previous list is not an inheritance hierarchy.
So, an Interval
is not a particular IntervalVector
and so on, altough one might legitimately expect this
(this would also be a perfectly valid design). This is in contrast with Matlab, where, basically, everything is
an array of interval matrices.
Some functions return points, not intervals; this is typically the case for the function mid() that, given
an interval, returns the midpoint. The returned type of Interval::mid()
is theferore double
.
In the case of an interval vector, the function mid() returns a vector of points. In order to keep our system
homogeneous, a type has been introduced to represent a vector of reals, namely, Vector
and similarly for matrices
or matrices arrays.
So the following types allow to represent real (floating-point) values:
double
(primitive type)Vector
Matrix
MatrixArray
.
The empty set¶
One unpleasant consequence of our inheritance-free design is that the empty set does not have a unique representation in Ibex. The empty set is “typed” so that an empty interval is not the same object as an empty interval vector or an empty interval matrix. But, of course, they can all be interpreted as the same mathematical object, the empty set.
Worse, an empty interval vector has a size just as any other vectors (and similarly, an empty interval matrix has a certain number of rows/columns, etc.). So the empty vector of 3 components is not the same object as the empty vector of 4 components. Imposing a size for the empty vector may seem clumsy at first glance, but it actually simplifies programming in several situations. For instance, if one asks for the complementary of the empty set represented by a n-sized vector, the result will be the n-dimensional box \((-\infty,\infty)\times\ldots\times(-\infty,\infty)\). Anyway, this multiple representation is harmless in practice because we always manipulate sets of a priori known dimensions.
It holds the same with matrices and arrays of matrices.
Intervals¶
Intervals are represented by the class Interval
(this class wraps the actual class of the underlying sub-library that implements interval arithmetic).
Here are examples of interval defintions.
In particular, it is possible to give a single value to define a degenerated interval (reduced to a point).
Note the usage of the constants NEG_INFINITY
and POS_INFINITY
for infinite bounds.
Note: In Ibex, an interval should never contain a “NaN” (not a number), whatever operation you perform with it. If this occurs, something is wrong.
Interval x1; // (-oo,+oo)
Interval x2(2); // [2,2]
Interval x3(x2); // [2,2]
Interval x4=x2; // [2,2]
Interval x5(1,2); // [1,2]
Interval x6(1,POS_INFINITY); // [1,+oo)
Interval x7(NEG_INFINITY,-1); // (-oo,-1]
Interval x8=Interval::empty_set(); // empty set
Interval Constants¶
Some commonly used intervals are already defined as static variables:
|
a thin enclosure of \(\pi\) |
|
a thin enclosure of \(\pi\) |
|
a thin enclosure of \(\pi/2\) |
|
\(\emptyset\) |
|
\((-\infty,+\infty)\) |
|
\([0,0]\) |
|
\([1,1]\) |
|
\([0,+\infty)\) |
|
\((-\infty,0]\) |
Let us print all these constants:
output << " EMPTY_SET =\t " << Interval::empty_set() << endl;
output << " PI =\t\t " << Interval::pi() << endl;
output << " 2 PI =\t\t " << Interval::two_pi() << endl;
output << " 1/2 PI =\t " << Interval::half_pi() << endl;
output << " ONE =\t\t " << Interval::one() << endl;
output << " ZERO =\t\t " << Interval::zero() << endl;
output << " ALL_REALS =\t " << Interval::all_reals() << endl;
output << " POS_REALS =\t " << Interval::pos_reals() << endl;
output << " NEG_REALS =\t " << Interval::neg_reals() << endl;
The output is:
EMPTY_SET = [empty]
PI = [3.141592653589793, 3.141592653589794]
2 PI = [6.283185307179586, 6.283185307179588]
1/2 PI = [1.570796326794896, 1.570796326794897]
ONE = <1, 1>
ZERO = <0, 0>
ALL_REALS = [-inf, inf]
POS_REALS = [0, inf]
NEG_REALS = [-inf, 0]
Vectors or Boxes¶
Vectors of reals are represented by the class Vector
and interval vectors (or “boxes”) are represented by the class IntervalVector
.
Creation and initialization of interval vectors can either be done separately (you first create a vector of the desired size and then fill it with the intervals) or
simultaneously, by giving an array of double
in argument of the constructor.
The following example creates twice ([0,1],[2,3],[4,5]):
// creates ([0,1],[2,3],[4,5]) in several steps.
IntervalVector x1(3); // so far x1 is ((-oo,oo),(-oo,oo),(-oo,oo))
x1[0]=Interval(0,1); // we initialize each component
x1[1]=Interval(2,3);
x1[2]=Interval(4,5);
// creates the same vector in two steps only
double _x2[3][2]={{0,1},{2,3},{4,5}};
IntervalVector x2(3,_x2);
This is the same for vectors:
// creates (0.1,0.2,0.3) in several steps.
Vector x1(3); // so far x1 is (0,0,0)
x1[0]=0.1; // we initialize each component
x1[1]=0.2;
x1[2]=0.3;
// creates the same vector in two steps only
double _x2[3]={0.1,0.2,0.3};
Vector x2(3,_x2);
You can also create an (interval) vector by duplicating n times the same double/interval and copy another (interval) vector:
IntervalVector x2(3,Interval(1,2)); // create ([1,2],[1,2],[1,2])
IntervalVector x3(3,0.1); // create ([0.1,0.1],[0.1,0.1],[0.1,0.1])
Vector x4(3,0.1); // create (0.1,0.1,0.1)
IntervalVector x5(x2); // create a copy of x2
Vector x6(x4); // create a copy of x4
Finally, you can create an empty interval vector of a given size (see The empty set) or create a degenerated box from a vector of reals:
IntervalVector x1=IntervalVector::empty(3); // create a vector of 3 empty intervals
Vector x(3,0.1);
IntervalVector x2(x); // create ([0.1,0.1],[0.1,0.1],[0.1,0.1])
Matrices and Array of matrices¶
They work exactly the same way as vectors.
Here are first listed different ways to build a Matrix
:
// create [(1,2,3);(4,5,6)] in several steps.
Matrix m1(2,3); // a 2x3 matrix filled with zeros
m1[0][0]=1; // we initialize each component
m1[0][1]=2;
m1[0][2]=3;
m1[1][0]=4;
m1[1][1]=5;
m1[1][2]=6;
output << "m1=" << m1 << endl << endl;
// create the same matrix in two steps only
double _m2[2*3]={1,2,3,
4,5,6};
Matrix m2(2,3,_m2);
output << "m2=" << m2 << endl << endl;
// create a 2x3 matrix filled with ones
Matrix m3(2,3,1.0);
output << "m3=" << m3 << endl << endl;
// the same matrix using a built-in static function
Matrix m4=Matrix::ones(2,3);
// create a copy of m3
Matrix m5(m3);
output << "m5=" << m5 << endl;
The output is:
m1=((1 ; 2 ; 3)
(4 ; 5 ; 6))
m2=((1 ; 2 ; 3)
(4 ; 5 ; 6))
m3=((1 ; 1 ; 1)
(1 ; 1 ; 1))
m5=((1 ; 1 ; 1)
(1 ; 1 ; 1))
Now, for interval matrices, we shall take as example a matrix that frequently appears, the “perturbed” idendity, that is:
double eps=1e-02;
// create in several steps.
IntervalMatrix m1(3,3); // a 3x3 matrix filled with (-oo,oo)
m1=Matrix::eye(3); // set m1 to the identity matrix
m1+=Interval(-eps,eps)*Matrix::ones(3); // add [-eps,eps] to each component
output << "m1=" << m1 << endl << endl;
// create the same matrix with a matrix of double
// warning: the matrix of double has a number of rows
// equal to the total number of components (9) and
// 2 columns (left bound, right bound).
double _m2[3*3][2]={{1-eps,1+eps},{-eps,eps},{-eps,eps},
{-eps,eps},{1-eps,1+eps},{-eps,eps},
{-eps,eps},{-eps,eps},{1-eps,1+eps}};
IntervalMatrix m2(3,3,_m2);
output << "m2=" << m2 << endl << endl;
// create a 3x3 matrix filled with [-eps,eps]
IntervalMatrix m3(3,3,Interval(-eps,eps));
m3+=Matrix::eye(3);
output << "m3=" << m3 << endl << endl;
// create a copy of m3
IntervalMatrix m4(m3);
output << "m4=" << m4 << endl;
The output is:
m1=(([0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001]))
m2=(([0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001]))
m3=(([0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001]))
m4=(([0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001] ; [-0.01000000000000001, 0.01000000000000001])
([-0.01000000000000001, 0.01000000000000001] ; [-0.01000000000000001, 0.01000000000000001] ; [0.9899999999999999, 1.010000000000001]))
Container operations¶
This section presents operations on interval vectors and interval matrices viewed as containers.
Most of the operations are also possible with vectors (Vector
) and matrices (Matrix
) by
replacing Interval
with double
and so on. They are not detailed again here, please refer
to the API.
Return type |
C++ code |
Meaning |
---|---|---|
int |
x.size() |
The size (number of components) |
Interval& |
x[i] (int i) |
The ith component. By reference. |
IntervalVector |
x.subvector(i,j) (int i, int j) |
Return the subvector of length (j-i+1) starting at index i and ending at index j (both included). By copy. [x] must be non-empty. |
void |
x.resize(n) (int n) |
Resize the vector. If the vector has been enlarged the extra components are set to \((-\infty,+\infty)\) even if the vector is empty. |
void |
x.put(i, y) (int i, IntervalVector& y) |
Write the vector [y] in [x] at index i. |
void |
x.clear() |
Set all the elements to [0,0] (even if [x] is empty). Emptiness is “overridden”. |
void |
x.init(y) (Interval& y) |
Set all the elements to [y] (even if [x] is empty). Emptiness is “overridden”. |
Return type |
C++ code |
Meaning |
---|---|---|
int |
m.nb_rows() |
Number of rows |
int |
m.nb_cols() |
Number of column |
Interval& |
m[i][j] (int i, int j) |
The (i,j)th entry. By reference. |
IntervalVector& |
m.row(i) (int i) |
The ith row. By reference (fast). |
IntervalVector |
m.col(j) (int j) |
The jth column. By copy (slow, in O(n)) |
IntervalMatrix |
m.rows(i1,i2) (int i1, int i2) |
The submatrix obtained by selecting rows between index i1 and i2. By copy. |
IntervalMatrix |
m.cols(j1,j2) (int j1, int j2) |
The submatrix obtained by selecting columns between index j1 and j2. By copy. |
IntervalMatrix |
m.submatrix(i1,i2,j1,j2) (int i1, int i2, int j1, int j2) |
The submatrix by selecting rows from index i1 to i2 (both included) and columns from index j1 to j2 (both included). |
void |
m.resize(n1,n2) (int n1, int n2) |
Resize the matrix to a n1xn2 matrix. Extra components are set to \((-\infty,+\infty)\) even if the matrix is empty. |
void |
m.set_row(i, y) (int i, IntervalVector& y) |
Write the vector [y] in the ith row of [m]. |
void |
m.set_col(i, y) (int i, IntervalVector& y) |
Write the vector [y] in the ith column of [m]. |
void |
m.put(i,j,m2) (int i, int j, IntervalMatrix& m2) |
Write the matrix [m2] in [m] at (i,j) and subsequent indices |
void |
m.put(i,j,v,b) (int i, int j, IntervalVector& v, bool b) |
Write the vector at (i,j) either in row (if b==true) or in column (if b==false). |
void |
m.clear() |
Set all the elements to [0,0] (even if [m] is empty). Emptiness is “overridden”. |
void |
m.init(y) (Interval& y) |
Set all the elements to [y] (even if [m] is empty). Emptiness is “overridden”. |
Set-membership operations¶
The operations described here are valid for any “sets” [x] and [y] and any “point” p, where a “set” designates here one object of either of the following classes:
Interval
IntervalVector
IntervalMatrix
IntervalMatrixArray
.
Similarly, a “point” either refers to a double
or an instance of:
Vector
Matrix
MatrixArray
.
Given equidimensional sets [x], [y] and a point p, the following table gives the set-membership predicates currently available in Ibex (from release 2.8) and their mathematical intepretation:
C++ code |
Meaning |
available from |
---|---|---|
x==y |
\([x]=[y]\) |
|
x!=y |
\([x]\neq [y]\) |
|
x.is_empty() |
\([x]=\emptyset\) |
|
x.is_subset(y) |
\([x]\subseteq [y]\) |
|
x.is_strict_subset(y) |
\([x]\subseteq [y]\wedge [x]\neq [y]\) |
|
x.is_interior_subset(y) |
\([x]\subseteq \mathring{[y]}\) |
release 2.8 |
x.is_strict_interior_subset(y) |
\([x]\subseteq \mathring{[y]}\wedge [x]\neq [y]\) |
release 2.8 |
x.is_superset(y) |
\([x]\supseteq [y]\) |
|
x.is_strict_superset(y) |
\([x]\supseteq [y]\wedge [x]\neq [y]\) |
|
x.contains(p) |
\(p\in [x]\) |
|
x.interior_contains(p) |
\(p\in \mathring{[x]}\) |
release 2.8 |
x.intersects(y) |
\([x]\cap [y]\neq\emptyset\) |
release 2.8 |
x.overlaps(y) |
\(\mathring{[x]}\cap \mathring{[y]}\neq\emptyset\) |
release 2.8 |
x.is_disjoint(y) |
\([x]\cap [y]=\emptyset\) |
release 2.8 |
Possible set-membership operations between sets are:
C++ code |
Meaning |
x&y |
\([x]\cap [y]\) |
x|y |
\(\square([x]\cup [y])\) |
x.set_empty() |
\([x]\leftarrow \emptyset\) |
x=y |
\([x]\leftarrow [y]\) |
x&=y |
\([x]\leftarrow ([x]\cap [y])\) |
x|=y |
\([x]\leftarrow \square([x] \cup [y])\) |
Finally, given two interval vectors [x] and [y] one can call:
cart_prod(x,y)
to create the Cartesian product.
Complementary and set difference¶
These set operations are only available for intervals (from release 2.8) and interval vectors.
In the case of intervals, the function complementary
stores the complementary of *this
in two intervals c1
and c2
. More exactly, the result is the
closure of the complementary since open bounds are not representable (except for infinity and minus infinity). E.g, the “complementary” of \((-\infty,0]\)
is \([0,+\infty)\).
Interval x(0,1);
Interval c1,c2; // to store the result
int n=x.complementary(c1,c2);
output << "complementary of " << x << " = " << c1;
if (n>1) output << " and " << c2;
output << endl;
The output is:
complementary of [0, 1] = [-inf, 0] and [1, inf]
In the case of interval vectors, the function complementary
calculates the complementary under the form of a union of non-overlapping interval vectors,
and stores this union into an array that is allocated by the function itself (the variable result
in the code below). The function returns the size of the union/array.
To illustrate this, let us first build a function that prints the complementary of an interval vector:
void print_compl(const IntervalVector& x) {
IntervalVector* result;
int n=x.complementary(result);
output << "complementary of " << x << " = " << endl;
for (int i=0; i<n; i++) {
output << "\t" << result[i] << endl;
}
delete[] result; // don't forget to free memory!
}
We can call it now with different vectors. Note that when the vector is the empty set with n components, the complementary is
a n-dimensional box: \((-\infty,\infty)\times\ldots\times(-\infty,\infty)\).
Note also that if the difference is empty, result
is an array of one element set to the empty box. It is not a zero-sized array containing no element
(this is illegal in ISO C++). However the returned number is 0 (not 1). The interesting point is that you can call delete[]
safely, in all cases.
print_compl(IntervalVector::empty(3));
print_compl(IntervalVector(3));
print_compl(IntervalVector(3,Interval(0,1)));
The output is:
complementary of empty vector =
([-inf, inf] ; [-inf, inf] ; [-inf, inf])
complementary of ([-inf, inf] ; [-inf, inf] ; [-inf, inf]) =
complementary of ([0, 1] ; [0, 1] ; [0, 1]) =
([-inf, 0] ; [-inf, inf] ; [-inf, inf])
([1, inf] ; [-inf, inf] ; [-inf, inf])
([0, 1] ; [-inf, 0] ; [-inf, inf])
([0, 1] ; [1, inf] ; [-inf, inf])
([0, 1] ; [0, 1] ; [-inf, 0])
([0, 1] ; [0, 1] ; [1, inf])
The set difference works exactly the same way except that the function takes as first argument another set [y].
Example with intervals:
// set difference between two intervals
Interval x(0,3);
Interval y(1,2);
Interval c1,c2; // to store the result
int n=x.diff(y,c1,c2);
output << x << " \\ " << y << " = " << c1;
if (n>1) output << " and " << c2;
output << endl;
[0, 3] \ [1, 2] = [0, 1] and [2, 3]
Example with interval vectors:
// set difference between two boxes
IntervalVector x(2,Interval(0,3));
IntervalVector y(2,Interval(1,2));
IntervalVector* result; // to store the result
int n=x.diff(y,result);
output << x << " \\ " << y << " = " << endl;
for (int i=0; i<n; i++) {
output << "\t" << result[i] << endl;
}
delete[] result; // don't forget to free memory!
([0, 3] ; [0, 3]) \ ([1, 2] ; [1, 2]) =
([0, 1] ; [0, 3])
([2, 3] ; [0, 3])
([1, 2] ; [0, 1])
([1, 2] ; [2, 3])
Geometric operations¶
Here are the functions that can be applied to an interval [x], seen as a segment of the line.
Return type |
C++ code |
Meaning |
---|---|---|
double |
x.lb() |
\(\underline{x}\), the lower (left) bound of [x] |
double |
x.ub() |
\(\overline{x}\), the upper (right) bound of [x] |
double |
x.diam() |
diameter, \(|\overline{x}-\underline{x}|\) |
double |
x.rad() |
radius, half of the diameter |
double |
x.mid() |
the midpoint, (\((\underline{x}+\overline{x})/2\)) |
Interval |
x.inflate(eps) (double eps) |
an interval with the same midpoint and radius increased by eps |
double |
distance(x,y) (Interval& y) |
the (Hausdorff) distance between [x] and [y]. The distance is 0 iff [x]==[y]. Otherwise, it returns the minimal value by which one of the intervals ([x] or [y)) has to be inflated so that it entirely overlaps the other interval. |
double |
x.rel_distance(y) (Interval& y) |
the “relative” distance, that is, distance(x,y)/x.diam() |
bool |
x.is_unbounded() |
true iff [x] has one of its bounds infinite. |
bool |
x.is_bisectable() |
true iff [x], as an interval of floating point numbers, can be bisected in two non-degenerated intervals of floating point numbers. The empty interval or an interval of two consecutive floating points are not bisectable. |
Here are the functions that can be applied to an interval vector [x], seen as a box.
Return type |
C++ code |
Meaning |
---|---|---|
Vector |
x.lb() |
lower-left corner (vector of lower bounds of [x]) |
Vector |
x.ub() |
upper-right corner (vector of upper bounds of [x]) |
Vector |
x.diam() |
vector of diameters, \(|\overline{x_i}-\underline{x_i}|\) |
double |
x.min_diam() |
minimal diameter, among all components of [x] |
double |
x.max_diam() |
maximal diameter, among all components of [x] |
int |
x.extr_diam_index(b) (bool b) |
|
void |
x.sort_indices(b, tab) (bool b, int tab[]) |
Write into The array |
Vector |
x.rad() |
vector of radii (halves of diameters) |
Vector |
x.mid() |
the midpoint, (\((\underline{x}+\overline{x})/2\)) |
double |
x.volume() |
the volume of the box |
double |
x.perimeter() |
the perimeter of the box |
bool |
x.is_flat() |
true if the volume is null (one dimension is degenerated) |
IntervalVector |
x.inflate(eps) (double eps) |
a box with the same midpoint and each radius increased by eps% |
double |
distance(x,y) (IntervalVector& y) |
the (Hausdorff) distance between [x] and [y]. The distance is 0 iff [x]==[y]. Otherwise, it returns the minimal value by which one of the boxes ([x] or [y]) has to be inflated so that it entirely overlaps the other box. |
double |
x.rel_distance(y) (IntervalVector& y) |
the “relative” distance, that is, distance(x,y)/x.max_diam() |
bool |
x.is_unbounded() |
true iff [x] has one of its bounds infinite. |
bool |
x.is_bisectable() |
true iff at least one component of [x] is bisectable (see above). |
Bisection¶
Bisecting a box is a fundamental operation in Ibex. The choice of the component to be bisected is often critical and defining a strategy for chosing this component is the purpose of Bisectors.
To bisect the ith component, the bisect
function can be used.
The first arugment is the index of the dimension i along which the box has to be splitted.
The second argument is optionnal and gives at which point the ith interval is bisected.
This argument is entered as a ratio of the interval diameter, 0.5 means “midpoint” (and the default value is precisely 0.5).
Example:
IntervalVector x(3,Interval(0,1)); // [0,1]x[0,1]x[0,1]
std::pair<IntervalVector,IntervalVector> p = x.bisect(1,0.4); // bisect the second component with ratio 0.4
output << "first box=" << p.first << endl;
output << "second box=" << p.second << endl;
The output is:
first box=([0, 1] ; [0, 0.4000000000000001] ; [0, 1])
second box=([0, 1] ; [0.4, 1] ; [0, 1])
Miscellaneous¶
Given an interval [x]:
C++ code |
Meaning |
---|---|
x.mig() |
Mignitude, \(\min_{x\in[x]} |x|\) (a double). Also exists for interval vectors and matrices (gives a vector or matrix of mignitudes). |
x.mag() |
Magnitude, \(\max_{x\in[x]} |x|\) (a double). Also exists for interval vectors and matrices (gives a vector or matrix of magnitudes). |
integer(x) |
The largest interval [a,b] of integers included in [x]. |
Finally, for an interval vector [v]:
v.random() |
A random point inside x |
Interval arithmetic¶
Interval arithmetic is the main device upon wich Ibex is built.
For a complete introduction of interval arithmetic and interval analysis, see [Moore 1966], [Neumaier 1990] or [Jaulin et al. 2001].
The interval arithmetic defines for each elementary function \(f:\mathbb{R}^n\to\mathbb{R}\) an interval function \([f]:\mathbb{IR}^n\to\mathbb{IR}\) according to the formula:
where \(\square\) (the “hull” symbol) in front of a set means “the smallest box enclosing this set”.
The hull symbol is here to highlight the fact that an enclosure of the possibly discontinuous set is returned. Note that, among all the functions below,
only tan
and sign
fail to be continuous. So, in the other cases, the “hull” operator can be ommitted.
The possible roundoff error accounts for the \(\supseteq\) symbol.
The function [f] is called an “interval extension” of the original function f (applied on real numbers).
We start by detailing the binary operators (+,-,*,/) and then proceed with the nonlinear ones.
Addition, subtraction, multiplication¶
The following tables sumarize the basic operations of linear algebra in Ibex. It is important to notice that (so far?), theses operations are not optimized, as compared to specialized softwares like Intlab. They are all based on naive implementations.
Symbols x and y denote scalars (
double
or instances ofInterval
);Symbols v and w denote vectors (instances of
Vector
orIntervalVector
)Symbols A and B denotes matrices (instances of
Matrix
orIntervalMatrix
).
Notice that it is not possible (at least in the current release) to appply such operations with objects of type IntervalMatrixArray
, the latter
being considered as a pure “set” or, more pragmmatically, as a matrix container. In other words, arrays of interval matrices are not a 3rd order tensor.
Possible additions and subtractions (with optionally assignments) are:
-x |
x+y |
x-y |
x+=y |
x-=y |
-v |
v+w |
v-w |
v+=w |
v-=w |
-A |
A+B |
A-B |
A+=B |
A-=B |
Possible multiplications are:
C++ code |
Meaning |
x*y |
multiplication of two “scalars” (double/intervals) |
x*=y |
multiplication and assignment |
x*v |
scalar multiplication of a vector |
x*A |
scalar multiplication of a matrix |
v*w |
dot product (\(v^Tw\)) |
hadamard_product(v,w) |
\((v_1w_1,\ldots,v_nw_n)\) |
outer_product(v,w) |
outer product (\(vw^T\)) |
A*x |
matrix-vector product |
A*B |
matrix product |
Division¶
First, it is possile to use division between two interval/doubles using C++ operator overloading, as for the previous operators.
You can either write x/y
(division) or x/=y
(division and assignment).
However, as a set-membership operator, and because the division of reals is non-continuous, dividing two intevals may result in a union of two intervals. E.g.,
This variant of the interval division is called “generalized division” in the litterature. The previous division operator / simply gives (in Ibex) the hull of this union. So, in C++, [2,3] / [-1,2] gives \((-\infty,\infty)\). If you want to handle the possible outcome of two intervals, you have to call the “div2” function.
Given four intervals [x],[y], [out1] and [out2]:
div2(x,y,out1,out2)
will store the union representing [x]/[y] in [out1] and [out2]. If the result is a single interval, [out2] is set to the empty interval. If the result is the empty set, both [out1] and [out2] are set to the empty interval.
Note: Contrary to the “cset” theory, the result is empty if y=[0,0] (whatever [x] is).
For convenience, there is also a function in the Interval
class that simultaneously performs divsion and intersection:
bool div2_inter(const Interval& x, const Interval& y, Interval& out2)
This function sets *this
to the intersection of itself with the division of two others, [x] and [y].
In return, *this
and [out2] contains the lower and upper part respectively of the division.
If the result of the generalized division and intersection is a single interval, [out2] is set to the empty interval.
The function returns true if the intersection is non empty.
Example:
Interval intv(-10,10);
Interval out2;
bool result=intv.div2_inter(Interval(2,3), Interval(-1,2), out2);
output << "the intersection is " << (result? "not":"") << " empty" << endl;
output << "left part=" << intv << " right part=" << out2 << endl;
the intersection is not empty
left part=[-10, -2] right part=[1, 10]
Non-linear elementary functions¶
The following operations are allowed for an interval [x].
Power and roots¶
C++ code |
Meaning |
sqr(x) |
\([x]^2\) |
sqrt(x) |
\(\sqrt{[x]}\) |
pow(x,n) |
\([x]^n\) |
pow(x,y) |
\([x]^{[y]} = e^{[y]\log([x])}\) |
root(x,n) |
\(\sqrt[n]{[x]}\) |
Exponenial, logarithm¶
exp(x) |
log(x) |
Trigonometric and hyperbolic functions¶
cos(x) |
sin(x) |
tan(x) |
acos(x) |
asin(x) |
atan(x) |
cosh(x) |
sinh(x) |
tanh(x) |
acosh(x) |
asinh(x) |
atanh(x) |
atan2(y,x) |
Max, min and miscellaneous¶
The sign function below is the interval extension of
C++ code |
Meaning |
---|---|
abs(x) |
interval extension of the absolute value. Not to be confused with magnitude; e.g. with [x]=[-2,1], abs([x])=[0,2] and the magnitude is 2. |
max(x,y) |
interval extension of the maximum. Not to be confused with \(\max ([x]\cup [y])\); e.g., max([0,3],[1,2])=[1,3]. |
min(x,y) |
interval extension of the minimum. Not to be confused with \(\min ([x]\cup [y])\); e.g., min([0,3],[1,2])=[0,2]. |
sign(x) |
(see above) |
Backward arithmetic¶
The “backward” or “relationnal” arithmetic consists in contraction operators for elementary constraints like
More precisely, given a function \(f:\mathbb{R}\to\mathbb{R}\) (\(\sin, \cos, \exp\, \ldots\)) the backward operator of f is an interval function \([f]_{bwd}:\mathbb{IR}^2\to\mathbb{IR}\) that satisfies:
Similarly, given a binary function \(f:\mathbb{R}^2\to\mathbb{R}\) (\(+, -, \times, \max, \ldots\)) the backward operator is a function \([f]_{bwd}:\mathbb{IR}^2\to\mathbb{IR}\) that satisfies:
In practice, backward operators in Ibex do not return an interval, they directly contracts the input interval [x] (in case of unary functions), that is:
or the two intervals [x1], [x2] (in case of binary functions):
So the bwd_* functions below all return void
.
Note: One important feature in Ibex is the ability to contract with respect to any constraints and, in particular, with constraints under the form
where f is an arbirary function. So, as a user, there is probably little interset for you to call the low-level routines presented here. Moreover, these routines only contracts the “pre-image argument” [x]; they don’t contract the “image argument” [y]. This is in contrast with high-level contractors like Forward-Backward that do both contractions (on [x] and [y]).
C++ code |
Relation |
---|---|
bwd_add(y,x1,x2) |
\(y=x_1+x_2\) |
bwd_sub(y,x1,x2) |
\(y=x_1-x_2\) |
bwd_mul(y,x1,x2) |
\(y=x_1\times x_2\) |
bwd_div(y,x1,x2) |
\(y=x_1/x_2\) |
bwd_sqr(y,x) |
\(y=x^2\) |
bwd_sqrt(y,x) |
\(y=\sqrt{x}\) |
bwd_pow(y,n,x) |
\(y=x^n\) |
bwd_pow(y,x1,x2) |
\(y=x_1^{x_2}\) |
bwd_root(y,n,x) |
\(y=\sqrt[n]{x}\) |
bwd_exp(y,x) |
\(y=\exp{x}\) |
bwd_log(y,x) |
\(y=\ln{x}\) |
bwd_cos(y,x) |
\(y=\cos{x}\) |
bwd_sin(y,x) |
\(y=\sin{x}\) |
bwd_tan(y,x) |
\(y=\tan{x}\) |
bwd_acos(y,x) |
\(y=\arccos{x}\) |
bwd_asin(y,x) |
\(y=\arcsin{x}\) |
bwd_atan(y,x) |
\(y=\arctan{x}\) |
bwd_cosh(y,x) |
\(y=\cosh{x}\) |
bwd_sinh(y,x) |
\(y=\sinh{x}\) |
bwd_tanh(y,x) |
\(y=\tanh{x}\) |
bwd_acosh(y,x) |
\(y=\operatorname{arcosh}(x)\) |
bwd_asinh(y,x) |
\(y=\operatorname{arsinh}(x)\) |
bwd_atanh(y,x) |
\(y=\operatorname{artanh}(x)\) |
bwd_atan2(y,x1,x2) |
\(y=atan2(x_1,x_2)\) |
bwd_abs(y,x) |
\(y=|x|\) |
bwd_sign(y,x) |
\(y=sign(x)\) (see def. above) |
bwd_integer(y,x) |
\(y=x \wedge x\in\mathbb{N}\) |
bwd_min(y,x1,x2) |
\(y=\min(x_1,x_2)\) |
bwd_max(y,x1,x2) |
\(y=\max(x_1,x_2)\) |
Inner arithmetic¶
The inner arithmetic [Chabert & Beldiceanu 2010] [Araya et al 2014] consists in two types of operators. Given a function \(f:\mathbb{R}^n\to\mathbb{R}\):
the forward inner operator is an interval function \(]f[:\mathbb{IR}^n\to\mathbb{IR}\) that satisfies the formula:
\[]f[([x]) \subseteq \{f(\alpha), \ \alpha\in [x] \}\]the backward inner operator is an interval function \(]f[_{bwd}:\mathbb{IR}^{n+1}\to\mathbb{IR}\) that satisfies the formula:
\[]f[_{bwd}([x],[y]) \subseteq \{ x\in [x] \ | \ \exists y\in [y], \quad y=f(x) \}.\]
Notice that the inclusion symbol has just been reversed, as compared to the classical interval (forward) and backward arithmetic.
Forward operators¶
Note: The inner operators are not implemented for all elementary functions yet.
C++ code |
Function |
---|---|
iadd(x1,x2) |
x1+x2 |
isub(x1,x2) |
x1-x2 |
imul(x1,x2) |
x1*x2 |
idiv(x1,x2) |
x1/x2 |
isqr(x) |
x^2 |
iminus(x) |
-x |
ilog(x) |
ln(x) |
iexp(x) |
exp(x) |
iacos(x) |
cos(x) |
iasin(x) |
sin(x) |
iatan(x) |
tan(x) |
Backward operators¶
Let us denote
\[\mathcal{S}:=\{ x\in [x] \ | \ \exists y\in [y], \quad y=f(x) \}.\]
The backward operator also takes an optional argument xin
that represents an “inner” box, that is a box which already satisfies
\[[x]_{in} \subseteq \mathcal{S}.\]
If this argument is set, the operator guarantees that the contracted box [x] will enclose [xin]. The operator works in this case as an inflator, the inner box [xin] is indeed “inflated” to a larger inner box [x]. More precisely, we have:
\[[x]_{in} \ \subseteq \ ]f[_{bwd}([x],[y],[x]_{in}) \subseteq ([x] \cap \mathcal{S}).\]
Note: The inner operators are not implemented for all elementary functions yet.
C++ code |
Relation |
|
---|---|---|
ibwd_add(y,x1,x2,xin1,xin2) |
y=x1+x2 |
|
ibwd_sub(y,x1,x2,xin1,xin2) |
y=x1-x2 |
|
ibwd_mul(y,x1,x2,xin1,xin2) |
y=x1*x2 |
|
ibwd_div(y,x1,x2,xin1,xin2) |
y=x1/x2 |
|
ibwd_sqr(y,x,xin) |
y=x^2 |
|
ibwd_sqrt(y,x,xin) |
\(y=\sqrt{x}\) |
|
ibwd_minus(y,x,xin) |
y=-x |
|
ibwd_abs(y,x,xin) |
\(y=|x|\) |
|
ibwd_pow(y,x,p,xin) |
y=x^p |
|
ibwd_log(y,x,xin) |
y=ln(x) |
|
ibwd_exp(y,x,xin) |
y=exp(x) |
|
ibwd_cos(y,x,xin) |
y=cos(x) |
|
ibwd_sin(y,x,xin) |
y=sin(x) |
|
ibwd_tan(y,x,xin) |
y=tan(x) |