Skip to content

Commit 6f65bae

Browse files
authored
Compatibility with IntervalArithmetic v0.22 (#203)
* Compatibility with IntervalArithmetic v0.22 * Decorated intervals must be used (which is the default Interval in the IntervalArithmetic v0.22) * The signature of the roots function changed to roots(f::Function, X::Union{Interval, AbstractVector} ; kwargs...), with the following consequences - Manual derivatives and contractors must now always be passed as keyword arguments. This greatly simplify the internal logic of the roots function. - No more IntervalBox. Multidimensional problem are specified by returning a vector of intervals, and giving a vector of intervals as initial search region. * Standard vectors of intervals are supported. They are significantly (3x) time slower for a simple 2D problem than SVector, but more convenient. * Features of the packages that were undocumented are now unsupported (e.g. Slopes and quadratic equations). If you were using them, please open an issue.
1 parent 80f4ca5 commit 6f65bae

31 files changed

+927
-1260
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
fail-fast: false
1414
matrix:
1515
version:
16-
- '1'
16+
- '1.10'
1717
- 'nightly'
1818
os:
1919
- ubuntu-latest

NEWS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Updates to `IntervalRootFinding.jl`
22

3+
## v0.6
4+
5+
Compatibility with IntervalArithmetic v0.22, which fundamentally changes the package.
6+
- Decorated intervals must be used (which is the default `Interval` in the latest releases of IntervalArithmetic)
7+
- The signature of the `roots` function changed to `roots(f::Function, X::Union{Interval, AbstractVector} ; kwargs...)`, with the following consequences
8+
- Manual derivatives and contractors must now always be passed as keyword arguments. This greatly simplify the internal logic of the `roots` function.
9+
- No more `IntervalBox`. Multidimensional problem are specified by returning a vector of intervals, and giving a vector of intervals as initial search region.
10+
- Normal vectors of intervals are supported. They are significantly (3x) time slower for a simple 2D problem than SVector, but more convenient.
11+
- Features of the packages outside the `roots` function are unsupported for the time being (e.g. `Slopes` and quadratic equations). If you need them, please open an issue.
12+
- `roots` works with `@exact`.
13+
14+
315
## v0.2
416

517
The package has been completely rewritten for v0.2.

Project.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
name = "IntervalRootFinding"
22
uuid = "d2bf35a9-74e0-55ec-b149-d360ff49b807"
3-
version = "0.5.11"
3+
version = "0.6"
44

55
[deps]
66
BranchAndPrune = "d3bc4f2e-91e6-11e9-365e-cd067da536ce"
77
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
88
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
99
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
10-
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
1110
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1211
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1312

1413
[compat]
1514
BranchAndPrune = "0.2.1"
1615
ForwardDiff = "0.10"
17-
IntervalArithmetic = "0.15, 0.16, 0.17, 0.18, 0.19, 0.20"
18-
Polynomials = "0.5, 0.6, 0.7, 0.8, 1, 2, 3"
16+
IntervalArithmetic = "0.22.13"
1917
Reexport = "1"
20-
StaticArrays = "0.11, 0.12, 1.0"
18+
StaticArrays = "1"
2119
julia = "1"
2220

2321
[extras]

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ The basic function is `roots`. Given a standard Julia function and an interval,
3030
```julia
3131
julia> using IntervalArithmetic, IntervalRootFinding
3232

33+
julia> using IntervalArithmetic.Symbols # to use `..`
34+
3335
julia> f(x) = sin(x) - 0.1*x^2 + 1
3436
f (generic function with 1 method)
3537

benchmark/benchmarks.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ sizes = (2, 5, 10)
4949

5050
for n in sizes
5151
s = S["n = $n"] = BenchmarkGroup()
52-
A = Interval.(randn(n, n))
53-
b = Interval.(randn(n))
52+
A = interval.(randn(n, n))
53+
b = interval.(randn(n))
5454

5555
s["Gauss seidel"] = @benchmarkable gauss_seidel_interval($A, $b)
5656
s["Gauss seidel contractor"] = @benchmarkable gauss_seidel_contractor($A, $b)
@@ -59,7 +59,7 @@ end
5959

6060

6161
S = SUITE["Dietmar-Ratz"] = BenchmarkGroup()
62-
X = Interval(0.75, 1.75)
62+
X = interval(0.75, 1.75)
6363

6464
for (k, dr) in enumerate(dr_functions)
6565
s = S["Dietmar-Ratz $k"] = BenchmarkGroup()

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ makedocs(
1010
pages = Any[
1111
"Home" => "index.md",
1212
"`roots` interface" => "roots.md",
13+
"Decorations and guarantees" => "decorations.md",
1314
"Internals" => "internals.md",
1415
"Bibliography" => "biblio.md",
1516
"API" => "api.md"

docs/src/decorations.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Decorations and guarantees
2+
3+
When you define a simple function, you will notice that the roots
4+
usually come out with the flag `com` and `NG`.
5+
6+
```jl
7+
julia> roots(x -> x^2 - 0.1, interval(-5, 5))
8+
2-element Vector{Root{Interval{Float64}}}:
9+
Root([-0.316228, -0.316227]_com_NG, :unique)
10+
Root([0.316227, 0.316228]_com_NG, :unique)
11+
```
12+
13+
In this case, `com` is the *decoration* of the interval,
14+
and `NG` its *guarantee*.
15+
See the documentation of `IntervalArithmetic.jl` for a detailed description,
16+
here we will go on what they mean in the context of finding roots
17+
of a function.
18+
19+
## Decorations
20+
21+
The decoration tells us whether all operations that affected the interval
22+
were well-behaved.
23+
The two crucial ones for us are `def` (the operations were not continuous)
24+
and `trv` (something horribly wrong happened).
25+
26+
In both cases, it means that the root can not be trusted,
27+
as the hypothesis of the theory we use are not fulfilled.
28+
29+
## Guarantee
30+
31+
The `NG` flags means that non-interval have been mixed with intervals.
32+
Since we can not track the source of the non-interval numbers,
33+
we can not guarantee that they are correct.
34+
For example, `0.1` is famously not parsed as `0.1`,
35+
as `0.1` can not be represented exactly as a binary number
36+
(just like `1/3` can not be represented exactly as a decimal number).
37+
38+
```jl
39+
julia> big(0.1)
40+
0.1000000000000000055511151231257827021181583404541015625
41+
```
42+
43+
If you want the number that you are inputting to be trusted **as is**,
44+
you can use the `@exact` macro from `IntervalArithmetic.jl`,
45+
and the `NG` flag will be avoided in most cases.
46+
47+
```jl
48+
julia> @exact f(x) = x^2 - 0.1
49+
f (generic function with 1 method)
50+
51+
julia> roots(f, interval(-5, 5))
52+
2-element Vector{Root{Interval{Float64}}}:
53+
Root([-0.316228, -0.316227]_com, :unique)
54+
Root([0.316227, 0.316228]_com, :unique)
55+
```
56+
57+
The `NG` flag can still appear if other computations,
58+
from a library using non-interval "magic" numbers for example,
59+
thus indicating that some non-trusted numbers have been used in the computation.
60+
61+
Moreover, the macro work in such a way that you can still use the defined
62+
function with numbers and get floating point results.
63+
64+
```jl
65+
julia> f(0.2)
66+
-0.06
67+
```
68+
## Trust
69+
70+
We are doing our best to really give validated and guaranteed results.
71+
However, in the case that you may make your house explode based on a result
72+
returned by this package,
73+
we would like to remind you that you should not trust the package beyond
74+
the promises of the license:
75+
76+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED
77+
78+
All bug reports and pull requests to fix them are however more than welcome.

docs/src/index.md

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ To do so, it uses methods from interval analysis, using interval arithmetic from
1010

1111
## Basic 1D example
1212

13-
To begin, we need a standard Julia function and an interval in which to search roots of that function. Intervals use the `Interval` type provided by the `IntervalArithmetic.jl` package and are generally constructed using the `..` syntax, `a..b` representing the closed interval $[a, b]$.
13+
To begin, we need a standard Julia function and an interval in which to search roots of that function. Intervals use the `Interval` type provided by the `IntervalArithmetic.jl` package
14+
Intervals are generally constructed using the `..` syntax (from the `IntervalArithmetic.Symbols` submodule),
15+
`a..b` representing the closed interval $[a, b]$.
1416

1517
When provided with this information, the `roots` function will return a vector of all roots of the function in the given interval.
1618

1719
Example:
1820

1921
```jl
20-
julia> using IntervalArithmetic, IntervalRootFinding
22+
julia> using IntervalArithmetic, IntervalArithmetic.Symbols, IntervalRootFinding
2123

2224
julia> rts = roots(x -> x^2 - 2x, 0..10)
23-
2-element Array{Root{Interval{Float64}},1}:
24-
Root([1.99999, 2.00001], :unique)
25-
Root([0, 4.4724e-16], :unknown)
25+
2-element Vector{Root{Interval{Float64}}}:
26+
Root([0.0, 3.73849e-08]_com_NG, :unknown)
27+
Root([1.999999, 2.00001]_com_NG, :unique)
2628
```
2729

2830
The roots are returned as `Root` objects, containing an interval and the status of that interval, represented as a `Symbol`. There are two possible types of root status, as shown in the example:
@@ -42,68 +44,89 @@ julia> g(x) = (x^2 - 2)^2 * (x^2 - 3)
4244
g (generic function with 1 method)
4345

4446
julia> roots(g, -10..10)
45-
4-element Array{IntervalRootFinding.Root{IntervalArithmetic.Interval{Float64}},1}:
46-
Root([1.73205, 1.73206], :unique)
47-
Root([1.41418, 1.4148], :unknown)
48-
Root([-1.4148, -1.41418], :unknown)
49-
Root([-1.73206, -1.73205], :unique)
47+
4-element Vector{Root{Interval{Float64}}}:
48+
Root([-1.73206, -1.73205]_com_NG, :unique)
49+
Root([-1.41422, -1.41421]_com, :unknown)
50+
Root([1.41421, 1.41422]_com, :unknown)
51+
Root([1.73205, 1.73206]_com_NG, :unique)
5052
```
5153

5254
Here we see that the two double roots are reported as being possible roots without guarantee and the simple roots have been proved to be unique.
5355

5456

5557
## Basic multi-dimensional example
5658

57-
For dimensions $n > 1$, the function passed to `roots` must currently return an `SVector` from the `StaticArrays.jl` package.
59+
For dimensions $n > 1$, the function passed to `roots` must take an array as
60+
argument and return an array.
61+
The initial search region is an array of interval.
5862

5963
Here we give a 3D example:
6064

6165
```jl
6266
julia> function g( (x1, x2, x3) )
63-
return SVector(x1^2 + x2^2 + x3^2 - 1,
64-
x1^2 + x3^2 - 0.25,
65-
x1^2 + x2^2 - 4x3
66-
)
67+
return [
68+
x1^2 + x2^2 + x3^2 - 1,
69+
x1^2 + x3^2 - 0.25,
70+
x1^2 + x2^2 - 4x3
71+
]
6772
end
6873
g (generic function with 1 method)
6974

7075
julia> X = -5..5
71-
[-5, 5]
72-
73-
julia> rts = roots(g, X × X × X)
74-
4-element Array{Root{IntervalBox{3,Float64}},1}:
75-
Root([0.440762, 0.440763] × [0.866025, 0.866026] × [0.236067, 0.236068], :unique)
76-
Root([0.440762, 0.440763] × [-0.866026, -0.866025] × [0.236067, 0.236068], :unique)
77-
Root([-0.440763, -0.440762] × [0.866025, 0.866026] × [0.236067, 0.236068], :unique)
78-
Root([-0.440763, -0.440762] × [-0.866026, -0.866025] × [0.236067, 0.236068], :unique)
76+
[-5.0, 5.0]_com
77+
78+
julia> rts = roots(g, [X, X, X])
79+
4-element Vector{Root{Vector{Interval{Float64}}}}:
80+
Root(Interval{Float64}[[-0.440763, -0.440762]_com_NG, [-0.866026, -0.866025]_com_NG, [0.236067, 0.236069]_com_NG], :unique)
81+
Root(Interval{Float64}[[-0.440763, -0.440762]_com_NG, [0.866025, 0.866026]_com_NG, [0.236067, 0.236069]_com_NG], :unique)
82+
Root(Interval{Float64}[[0.440762, 0.440763]_com_NG, [-0.866026, -0.866025]_com_NG, [0.236067, 0.236069]_com_NG], :unique)
83+
Root(Interval{Float64}[[0.440762, 0.440763]_com_NG, [0.866025, 0.866026]_com_NG, [0.236067, 0.236069]_com_NG], :unique)
7984
```
8085

81-
Thus the system admits four unique roots in the box $[-5, 5]^3$. We have used the unicode character `×` (typed as `\times<tab>`) to compose several intervals into a multidimensional box.
86+
Thus, the system admits four unique roots in the box $[-5, 5]^3$.
87+
88+
Moreover, the package is compatible with `StaticArrays.jl`.
89+
Usage of static arrays is recommended to increase performance.
90+
```jl
91+
julia> using StaticArrays
92+
93+
julia> h((x, y)) = SVector(x^2 - 4, y^2 - 16)
94+
h (generic function with 1 method)
95+
96+
julia> roots(h, SVector(X, X))
97+
4-element Vector{Root{SVector{2, Interval{Float64}}}}:
98+
Root(Interval{Float64}[[-2.00001, -1.999999]_com_NG, [-4.00001, -3.999999]_com_NG], :unique)
99+
Root(Interval{Float64}[[-2.00001, -1.999999]_com_NG, [3.999999, 4.00001]_com_NG], :unique)
100+
Root(Interval{Float64}[[1.999999, 2.00001]_com_NG, [-4.00001, -3.999999]_com_NG], :unique)
101+
Root(Interval{Float64}[[1.999999, 2.00001]_com_NG, [3.999999, 4.00001]_com_NG], :unique)
102+
```
82103

83104
## Stationary points
84105

85106
Stationary points of a function $f:\mathbb{R}^n \to \mathbb{R}$ may be found as zeros of the gradient of $f$.
86-
The package exports the `` operator to calculate gradients using `ForwardDiff.jl`:
107+
The gradient can be computed using `ForwardDiff.jl`:
87108

88109
```jl
110+
julia> using ForwardDiff: gradient
111+
89112
julia> f( (x, y) ) = sin(x) * sin(y)
90113
f (generic function with 1 method)
91114

92-
julia> ∇f = (f) # gradient operator from the package
115+
julia> ∇f(x) = gradient(f, x) # gradient operator from the package
93116
(::#53) (generic function with 1 method)
94117

95118
julia> rts = roots(∇f, IntervalBox(-5..6, 2), Newton, 1e-5)
96-
25-element Array{IntervalRootFinding.Root{IntervalArithmetic.IntervalBox{2,Float64}},1}:
97-
Root([4.71238, 4.71239] × [4.71238, 4.71239], :unique)
98-
Root([4.71238, 4.71239] × [1.57079, 1.5708], :unique)
119+
25-element Vector{Root{Vector{Interval{Float64}}}}:
120+
Root(Interval{Float64}[[-4.7124, -4.71238]_com, [-4.7124, -4.71238]_com], :unique)
121+
Root(Interval{Float64}[[-3.1416, -3.14159]_com, [-3.1416, -3.14159]_com], :unique)
99122
100-
[output snipped for brevity]
123+
[output skipped for brevity]
101124
```
102125
103126
Now let's find the midpoints and plot them:
104127
105128
```jl
106-
midpoints = mid.(interval.(rts))
129+
midpoints = [mid.(root_region(rt)) for rt in rts]
107130

108131
xs = first.(midpoints)
109132
ys = last.(midpoints)
@@ -114,6 +137,4 @@ surface(-5:0.1:6, -6:0.1:6, (x,y) -> f([x, y]))
114137
scatter!(xs, ys, f.(midpoints))
115138
```
116139
117-
The result is the following:
118-
119140
![stationary points](stationary_points.png)

0 commit comments

Comments
 (0)