|
| 1 | +# Philosophy |
| 2 | + |
| 3 | +The goal of the `Interval` type is to be directly used to replace floating point |
| 4 | +number in arbitrary julia code, such that in any calculation, |
| 5 | +the resulting intervals are guaranteed to bound the true image of the starting |
| 6 | +intervals. |
| 7 | + |
| 8 | +So, essentially, we would like `Interval` to act as numbers and |
| 9 | +the julia ecosystem has evolved to use `Real` as the default supertype |
| 10 | +for numerical types that are not complex. |
| 11 | +Therefore, to ensure the widest compatiblity, |
| 12 | +our `Interval` type must be a subtype of `Real`. |
| 13 | + |
| 14 | +Then, for any function `f(x::Real)`, |
| 15 | +we want the following to hold for all real `x` in the interval `X` |
| 16 | +(note that it holds for **all** real numbers in `X`, |
| 17 | +even those that can not be represented as floating point numbers): |
| 18 | +```math |
| 19 | +f(x) \in f(X), \qquad \forall x \in X. |
| 20 | +``` |
| 21 | + |
| 22 | +At first glance, this is reasonable: |
| 23 | +all arithmetic operations are well-defined for both real numbers and intervals, |
| 24 | +therefore we can use multiple dispatch to define the interval behavior of |
| 25 | +operations such has `+`, `/`, `sin` or `log`. |
| 26 | +Then a code written for `Real`s can be used as is with `Interval`s. |
| 27 | + |
| 28 | +However, being a `Real` means way more than just being compatible with |
| 29 | +arithmetic operations. |
| 30 | +`Real`s are also expected to |
| 31 | + |
| 32 | +1. Be compatible with any other `Number` through promotion. |
| 33 | +2. Support comparison operations, such as `==` or `<`. |
| 34 | +3. Act as a container of a single element, |
| 35 | + e.g. `collect(x)` returns a 0-dimensional array containing `x`. |
| 36 | + |
| 37 | +Each of those points lead to specific design choice for `IntervalArithmetic.jl`, |
| 38 | +choices that we detail below. |
| 39 | + |
| 40 | + |
| 41 | +## Compatibility with other `Number`s |
| 42 | + |
| 43 | +In julia it is expected that `1 + 2.2` silently promoted the integer `1` |
| 44 | +to a `Float64` to be able to perform the addition. |
| 45 | +Following this logic, it means that `0.1 + interval(2.2, 2.3)` should |
| 46 | +silently promote `0.1` to an interval. |
| 47 | + |
| 48 | +However, in this case we can not guarantee that `0.1` is known exactly, |
| 49 | +because we do not know how it was produced in the first place. |
| 50 | +Following the julia convention is thus in contradiction with providing |
| 51 | +guaranteed result. |
| 52 | + |
| 53 | +In this case, we choose to be mostly silent, |
| 54 | +the information that a non-interval of unknown origin is recorded in the `NG` flag, |
| 55 | +but the calculation is not interrupted, and no warning is printed. |
| 56 | + |
| 57 | +For convenience, we provide the [`ExactReal`](@ref) and [`@exact`](@ref) macro |
| 58 | +to allow to explicitly mark a number as being exact, |
| 59 | +and not produce the `NG` flag when mixed with intervals. |
| 60 | + |
| 61 | + |
| 62 | +## Comparison operators |
| 63 | + |
| 64 | +We can extend our above definition of the desired behavior for two real numbers |
| 65 | +`x` and `y`, and their respective intervals `X` and `Y`. |
| 66 | +With this, we want to have, for any function`f`, |
| 67 | +for all `x` in `X` and all `y` in `Y`, |
| 68 | +``math |
| 69 | +f(x, y) \in f(X, Y), \qquad \forall x \in X, y \in Y. |
| 70 | +`` |
| 71 | + |
| 72 | +With this in mind, an operation such as `==` can easily be defined for intervals |
| 73 | + |
| 74 | +1. If the intervals are disjoints (`X ∩ Y === ∅`), then `X == Y` is `[false]`. |
| 75 | +2. If the intervals both contain a single element, |
| 76 | + and that element is the same for both, |
| 77 | + `X == Y` is `[true]`. |
| 78 | +3. Otherwise, we can not conclude anything, and `X == Y` must be `[false, true]`. |
| 79 | + |
| 80 | +Not that we use intervals in all case, because, according to our definition, |
| 81 | +the true result must be contained in the returned interval. |
| 82 | +However, this is not convenient, as any `if` statement would error when used |
| 83 | +with an interval. |
| 84 | +Instead, we have opted to return respectively `false` and `true` |
| 85 | +for cases 1 and 2, and to immediately error otherwise. |
| 86 | + |
| 87 | +In this way, we can return a more informative error, |
| 88 | +but we only do it when the result is ambiguous. |
| 89 | + |
| 90 | +This has a clear cost, however, in that some expected behaviors do not hold. |
| 91 | +For example, an `Interval` is not equal to itself. |
| 92 | + |
| 93 | +```julia> X = interval(1, 2) |
| 94 | +[1.0, 2.0]_com |
| 95 | +
|
| 96 | +julia> X == X |
| 97 | +ERROR: ArgumentError: `==` is purposely not supported when the intervals are overlapping. See instead `isequal_interval` |
| 98 | +Stacktrace: |
| 99 | + [1] ==(x::Interval{Float64}, y::Interval{Float64}) |
| 100 | + @ IntervalArithmetic C:\Users\Kolaru\.julia\packages\IntervalArithmetic\XjBhk\src\intervals\real_interface.jl:86 |
| 101 | + [2] top-level scope |
| 102 | + @ REPL[6]:1. |
| 103 | +``` |
| 104 | + |
| 105 | + |
| 106 | +## Intervals as sets |
| 107 | + |
| 108 | +We have taken the perspective to always let `Interval`s act as if they were numbers. |
| 109 | + |
| 110 | +But they are also sets of numbers, |
| 111 | +and it would be nice to use all set operations defined in julia on them. |
| 112 | + |
| 113 | +However, `Real` are also sets. For example, the following is valid |
| 114 | + |
| 115 | +```julia |
| 116 | +julia> 3 in 3 |
| 117 | +true |
| 118 | +``` |
| 119 | + |
| 120 | +Then what should `3 in interval(2, 6)` do? |
| 121 | + |
| 122 | +For interval as a set, it is clearly `true`. |
| 123 | +But for intervals as a subtype of `Real` this is equivalent to |
| 124 | +```julia |
| 125 | +3 == interval(2, 6) |
| 126 | +``` |
| 127 | +which must either be false (they are not the same things), |
| 128 | +or error as the result can not be established. |
| 129 | + |
| 130 | +To be safe, we decided to go one step further and disable |
| 131 | +**all** set operations from julia `Base` on intervals. |
| 132 | +These operations can instead be performed with the specific `*_interval` function, |
| 133 | +for example `in_interval` as a replacement for `in`, |
| 134 | +except for `setdiff`. |
| 135 | +We can not meaningfully define the set difference of two intervals, |
| 136 | +because our intervals are always closed, |
| 137 | +while the result of `setdiff` can be open. |
| 138 | + |
| 139 | + |
| 140 | +# Summary |
| 141 | + |
| 142 | +| | Functions | Behavior | Note | |
| 143 | +| :---- | :---- | :---- | :---- | |
| 144 | +| Arithmetic operations | `+`, `-`, `*`, `/`, `^` | Interval extension | Produce the `NG` flag when mixed with non-interval | |
| 145 | +| Other numeric function | `sin`, `exp`, `sqrt`, etc. | Interval extension | | |
| 146 | +| Boolean operations | `==`, `<`, `<=`, `iszero`, `isnan`, `isinteger`, `isfinite` | Error if the result can not be guaranteed to be either `true` or `false` | See [`isequal_interval`](@ref) to test equality of intervals, and [`isbounded`](@ref) to test the finiteness of the elements | |
| 147 | +| Set operations | `in`, `issubset`, `isdisjoint`, `issetequal`, `isempty`, `union`, `intersect` | Always error | Use the `*_interval` function instead (e.g. [`in_interval`](@ref)) |
| 148 | +| Exceptions | `≈`, `setdiff` | Always error | No meaningful interval extension | |
0 commit comments