Skip to content

Commit

Permalink
Make decorated interval the default (#590)
Browse files Browse the repository at this point in the history
* Make decorated interval the default

* Remove warning

* Fix decoration

* Fix tests

* Fix tests

* Fix NaI

* Minor cleanup

* Add docstring for `_unsafe_bareinterval`

* Remove decoration `bad` in favour of a new field

* Minor cleanup

* Remove `signbit`

* Fix `pow` and cleanup

* Fix tests

* Special display for unguaranteed interval

* Cleanup

* Update docs

* Fix doctest

* Do not error for missing docstring

* Rename `guarantee` to `isguaranteed`

* Delete empty file decorations.md

* Fix missing field infos in docstring

* Update `isguaranteed` docstring

* Update docs

* Ensures `NaN` is converted to a NaI

* Update docs

* Minor cleanup

* Fix typo

* Fix error for boolean functions from Base

* Add docstrings

* Fix bug

* Fix `StackOverflow`

* Fix `StackOverFlow`

* Set correct cuurent version
  • Loading branch information
OlivierHnt authored Dec 1, 2023
1 parent c07cb3f commit 742a26f
Show file tree
Hide file tree
Showing 78 changed files with 12,514 additions and 12,154 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "IntervalArithmetic"
uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
repo = "https://github.com/JuliaIntervals/IntervalArithmetic.jl.git"
version = "0.21.1"
version = "0.21.2"

[deps]
CRlibm = "96374032-68de-5a5b-8d9e-752f78720389"
Expand Down
4 changes: 2 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ makedocs(;
"Manual" => [
"Constructing intervals" => "manual/construction.md",
"Usage" => "manual/usage.md",
"Decorations" => "manual/decorations.md",
"API" => "manual/api.md"
]
]
],
warnonly = true
)

deploydocs(;
Expand Down
15 changes: 0 additions & 15 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,3 @@ If you use the IntervalArithmetic library in your publication, research, teachin

- [MPFI.jl](https://github.com/andrioni/MPFI.jl), a Julia wrapper around the [MPFI C library](http://perso.ens-lyon.fr/nathalie.revol/software.html), a multiple-precision interval arithmetic library based on MPFR
- [Intervals.jl](https://github.com/invenia/Intervals.jl), an alternative implementation of basic interval functions by Invenia Technical Computing



## History

This project began in 2014 during a masters' course in the postgraduate programs of Mathematics and Physics at the Universidad Nacional Autónoma de México. It was initially written in Python, then reinitiated in 2015 and rewritten in Julia. We thank the participants of the courses for their contribution, energy and ideas.


## Support

Financial support is acknowledged from DGAPA-UNAM PAPIME grants PE-105911 and PE-107114, and DGAPA-UNAM PAPIIT grant IN-117214.

Luis Benet acknowledges support from *Cátedra Marcos Moshinsky* (2013).

David P. Sanders acknowledges a sabbatical fellowship from CONACYT and thanks Alan Edelman and the Julia group at MIT for hosting his sabbatical visit.
208 changes: 175 additions & 33 deletions docs/src/manual/construction.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,188 @@
# Constructing intervals

Constructing an interval is the most basic operation in the library. The [`interval`](@ref) constructor is the standard way to create an interval. It accepts one or two values, and an optional bound type.
The library provides two interval types. The first one is [`BareInterval`](@ref), corresponding to a basic implementation of intervals, stored by their infimum and supremum. The second type is [`Interval`](@ref) and builds on top of bare intervals, with the additional fields `decoration` and `isguaranteed`. See the sections below.

```@repl construction
using IntervalArithmetic
setformat(:full) # print the interval in full
interval(0.1) # interval(Float64, 0.1)
interval(0.1, 0.2) # interval(Float64, 0.1, 0.2)
interval(3.1f0) # interval(Float32, 3.1f0)
interval(π) # interval(Float64, π)
interval(BigFloat, π)
interval(Inf) # not valid since infinity is not part of an interval
interval(3, 2) # not valid since the lower bound is strictly greater than the upper bound
setdisplay(:full) # print the interval in full
bareinterval(1, π) # `bareinterval(Float64, 1, π)`
interval(1, π) # `interval(Float64, 1, π)`, interval decorated with `com` (common)
```

The submodule `IntervalArithmetic.Symbols` exports the infix operator `..` as an alias for `interval`.
Therefore, we **strongly recommend the use of [`Interval`](@ref) over [`BareInterval`](@ref)** to better track the effect of functions according to the IEEE Standard 1788-2015 specifications. For instance, taking the square root of an interval discards the negative part of the interval, without any notice for bare intervals:

```@repl construction
using IntervalArithmetic.Symbols
0.1..0.2 # interval(0.1, 0.2)
sqrt(bareinterval(-1, 1)) # `sqrt(bareinterval(0, 1))`
sqrt(interval(-1, 1)) # interval decorated with `trv` (trivial)
```



### Decorations

A *decoration* is a label that indicates the status of a given interval. Decorated intervals provide valuable information on the result of evaluating a function on an initial interval.

Upon the application of a function ``f`` on an interval ``x``, the resulting interval ``f(x)`` has either one of the following decorations:

- `com` (common): ``x`` is a closed, bounded, non-empty subset of the domain of ``f``, ``f`` is continuous on the interval ``x``, and ``f(x)`` is bounded.

- `dac` (defined and continuous): ``x`` is a non-empty subset of the domain of ``f``, and ``f`` is continuous on ``x``.

- `def` (defined): ``x`` is a non-empty subset of the domain of ``f``; in other words, ``f`` is defined at each point of ``x``.

- `trv` (trivial): ``f(x)`` carries no meaningful information.

- `ill` (ill-formed): ``f(x)`` is Not an Interval (NaI).

Each decoration is paired with an integer as follows: `ill = 0`, `trv = 1`, `def = 2`, `dac = 3` and `com = 4`. Then, decorations degrade according to the propagation order `com > dac > def > trv > ill`.

One can specify a decoration when constructing intervals. Otherwise, the interval is initialised with a decoration according to the underlying bare interval:

- `com`: non-empty and bounded.

- `dac`: unbounded.

- `trv`: empty.

- `ill`: NaI.


#### Examples

##### Common

```@repl construction
x = interval(0.5, 3)
sqrt(x)
```

Both input `x` and output `sqrt(x)` are common intervals since they are closed, bounded, non-empty and that ``\sqrt`` is continuous over ``[1/2, 3]``.

Observe that these decorations, together with the fact that any element of the interval `sqrt(x)` is also in the interval `x`, imply that the [Schauder Fixed-Point Theorem](https://en.wikipedia.org/wiki/Schauder_fixed-point_theorem) is satisfied. More precisely, this computation proves the existence of a fixed-point of ``\sqrt`` in ``[1/2, 3]`` (in this simple example, ``\sqrt(1) = 1``).

##### Defined and continuous

```@repl construction
x = interval(3, Inf)
sqrt(x)
```

Both the intervals are unbounded, hence the maximum possible decoration is `dac`.

Note that overflows can also produce the decoration `dac`:

```@repl construction
x = interval(floatmax(Float64))
x + interval(1)
```

The [`±`](@ref) (`\pm<tab>`) infix operator creates the interval from the midpoint and the radius.
##### Defined and continuous

```@repl construction
0 ± 1
x = interval(-3, 4)
sign(x)
```

The various string formats are as follows:
- No string parameter or `Empty` string ("[Empty]") returns an empty interval.
- `entire` ("[entire]") and "[,]" string returns entireinterval
- "[nai]" returns `Nai{Type}`
- "[m]" returns `Interval(m,m)`
- "[l, r]" returns `Interval(l, r)`
- "m?r" returns `Interval(m-r, m+r)`
- "m?ren" returns `Interval((m-r)en, (m+r)en)`
- "m?ru" or "m?rd" returns `Interval(m, m+r)` or `Interval(m-r, m)` respectively
- "m?" returns `Interval(m + 5 precision units, m - 5 precision units)`
- "m??" returns `Interval(-Inf, +Inf)`
- "m??u" or "m??d" returns `Interval(m, +Inf)` or `Interval(-Inf, m) respectively`

!!! warning
The ``\sign`` function is discontinuous at ``0``, but is defined everywhere on the input interval, so the decoration of the result is `def`.

##### Trivial

```@repl construction
x = interval(-3.5, 4)
sqrt(x)
```

The negative part of `x` is discarded before evaluating the ``\sqrt`` function since its domain is ``[0, \infty)``. The process of discarding parts of an interval that are not in the domain of a function is called *loose evaluation*. This event has been recorded by degrading the decoration of the resulting interval to `trv`, indicating that nothing is known about the relationship between `x` and `sqrt(x)`.

In this case, we know why the decoration was reduced to `trv`. Generally, if this were just a single step in a longer calculation, a resulting decoration `trv` shows only that something like this occured at some step.

For instance,

```@repl construction
f = asin ∘ sqrt
x = interval(-3, 3)
f(x)
y = interval(0, 3)
f(y)
```

In both cases, `asin(sqrt(X))` gives a result with the decoration `trv`; to find out where things went wrong, the function must be analyzed.

```@repl construction
sqrt(x) # `f(x)` has the decoration is `trv` since `x` contains negative values
sqrt(y) # the decoration is `com`
asin(sqrt(y)) # `f(x)` has the decoration is `trv` since `sqrt(y)` contains values stricly greater than `1`
```

This shows that loose evaluation occurred in different parts of `f` for `x` and `y`.

!!! danger
The decoration `trv` is an indicator of information loss. Often this also reveals that something unexpected occured. Therefore, any interval marked by this decoration may not be trusted and the code may need to be revised.

##### Ill-formed

```@repl construction
interval(2, 1)
interval(NaN)
```

These are all examples of ill-formed intervals, resulting in the decoration `ill`.

!!! danger
The decoration `ill` is an indicator that an error has occured. Therefore, any interval marked by this decoration cannot be trusted and the code needs to be debugged.



### Guarantee

A *guarantee* is yet another label, independent of decorations, and not described by the IEEE Standard 1788-2015 specifications. Its purpose is to accomodate for Julia's extensive conversion and promotion system, while retaining reliability in computations. Specifically, an interval `x` constructed via [`interval`](@ref) satisfies `isguaranteed(x) == true`. However, if a call to `convert(::Type{<:Interval}, ::Real)` occurs, then the resulting interval `x` satisfies `isguaranteed(x) == false`.

In contrast, a [`BareInterval`](@ref) can only be constructed via [`bareinterval`](@ref), it is not a subtype of `Real`, and there are no allowed conversion with `Number`. Thus, this interval type is always guaranteed.

!!! danger
A user interested in validated numerics should **always** have a resulting interval for which [`isguaranteed`](@ref) is `true`.



## More constructors

The submodule `IntervalArithmetic.Symbols` exports the infix operator `..` and `±` as an alias for `interval`; this submodule must be explicitly imported.

```@repl construction
using IntervalArithmetic.Symbols
0.1 .. 0.2 # interval(0.1, 0.2; format = :standard)
0.1 ± 0.2 # interval(0.1, 0.2; format = :midpoint)
```

Moreover, one can parse strings into intervals. The various string formats are the following:

- `"[m]"` is equivalent to `interval(m, m)`.

- `"[l, r]"` is equivalent to `interval(l, r)`.

- `"m?r"` is equivalent to `interval(m-r, m+r)`.

- `"m?ren"` is equivalent to `interval((m-r)*1en, (m+r)*1en)`.

- `"m?rd"` is equivalent to `interval(m-r, m)`.

- `"m?ru"` is equivalent to `interval(m, m+r)`.

- `"m?"` is equivalent to `interval(m + 5 precision units, m - 5 precision units)`.

- `"m??"` is equivalent to `interval(-Inf, +Inf)`.

- `"m??d"` is equivalent to `interval(-Inf, m)`.

- `"m??u"` is equivalent to `interval(m, +Inf)`.

- `"[Entire]"`, `"[entire]"` and `"[,]"` are equivalent to `entireinterval()`.

- `"[Empty]"`, `"[empty]"` and `"[]"` are equivalent to `emptyinterval()`.

- `"[nai]"` and any other unsupported string formats are equivalent to `nai()`.

To add a specific decoration, add `"_com"`, `"_dac"`, `"_dec"`, `"_trv"` and `"_ill"` at the end of the string.

!!! danger
Most real numbers cannot be exactly represented by floating-points. In such cases, the literal expression is rounded at parse time. To construct an interval enclosing the true real number, one must rely on the string constructor mentioned above.

For instance, consider
Expand All @@ -49,15 +191,15 @@ The various string formats are as follows:
x = 0.1
```

This appears to store the real number ``1/10`` in a variable `x` of type `Float64`. Yet,
This appears to store the real number ``1/10`` in a variable `x` of type `Float64`. Yet,

```@repl construction
x > 1//10
```

Hence, the floating-point `0.1` is (slightly) greater than the real number ``1/10`` since ``1/10`` *cannot be represented exactly in binary floating-point arithmetic, at any precision*. The true value must be approximated by a floating-point number with fixed precision -- this procedure is called rounding.
Hence, the floating-point `0.1` is (slightly) greater than the real number ``1/10`` since ``1/10`` **cannot be represented exactly in binary floating-point arithmetic, at any precision**. The true value must be approximated by a floating-point number with fixed precision -- this procedure is called rounding.

In particular, this implies that `interval(0.1)` *does not* contain the real number ``1/10``. A valid interval containing the real number ``1/10`` can be constructed by
In particular, this implies that `interval(0.1)` **does not** contain the real number ``1/10``. A valid interval containing the real number ``1/10`` can be constructed by

```@repl construction
I"0.1"
Expand Down
Loading

0 comments on commit 742a26f

Please sign in to comment.