Skip to content

Commit 5b7a8ab

Browse files
eeshan9815dpsanders
authored andcommitted
System of Linear Equations (#67)
* Add Gauss-Seidel method * Use StaticArrays and add benchmarking * Add benchmark script * Add another version with StaticArrays * contractor approach * Add tests * replace deepcopy with copy * Add test dependency * another method with a static vector x * bug fix * Display benchmark data in a table * add tabular results * make results more readable * generalized the various implementation * Add Gauss Elimination with preconditioning * Add benchmarking data * Add tests and fix typos * Add examples and bug fix * Import and redefine \ for intervals * Add tests for \ * Fix declaration in wrong file * Add more tests and bug fixes
1 parent 9f212fb commit 5b7a8ab

File tree

8 files changed

+500
-2
lines changed

8 files changed

+500
-2
lines changed

examples/linear_eq.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Examples from Luc Jaulin, Michel Kieffer, Olivier Didrit and Eric Walter - Applied Interval Analysis
2+
3+
using IntervalArithmetic, IntervalRootFinding, StaticArrays
4+
5+
A = [4..5 -1..1 1.5..2.5; -0.5..0.5 -7.. -5 1..2; -1.5.. -0.5 -0.7.. -0.5 2..3]
6+
sA = SMatrix{3}{3}(A)
7+
mA = MMatrix{3}{3}(A)
8+
9+
b = [3..4, 0..2, 3..4]
10+
sb = SVector{3}(b)
11+
mb = MVector{3}(b)
12+
13+
p = fill(-1e16..1e16, 3)
14+
15+
rts = gauss_seidel_interval!(p, A, b, precondition=true) # Gauss-Seidel Method; precondition=true by default
16+
rts = gauss_seidel_interval!(p, sA, sb, precondition=true) # Gauss-Seidel Method; precondition=true by default
17+
rts = gauss_seidel_interval!(p, mA, mb, precondition=true) # Gauss-Seidel Method; precondition=true by default
18+
19+
rts = gauss_seidel_interval(A, b, precondition=true) # Gauss-Seidel Method; precondition=true by default
20+
rts = gauss_seidel_interval(sA, sb, precondition=true) # Gauss-Seidel Method; precondition=true by default
21+
rts = gauss_seidel_interval(mA, mb, precondition=true) # Gauss-Seidel Method; precondition=true by default
22+
23+
rts = gauss_seidel_contractor!(p, A, b, precondition=true) # Gauss-Seidel Method (Vectorized); precondition=true by default
24+
rts = gauss_seidel_contractor!(p, sA, sb, precondition=true) # Gauss-Seidel Method (Vectorized); precondition=true by default
25+
rts = gauss_seidel_contractor!(p, mA, mb, precondition=true) # Gauss-Seidel Method (Vectorized); precondition=true by default
26+
27+
rts = gauss_seidel_contractor(A, b, precondition=true) # Gauss-Seidel Method (Vectorized); precondition=true by default
28+
rts = gauss_seidel_contractor(sA, sb, precondition=true) # Gauss-Seidel Method (Vectorized); precondition=true by default
29+
rts = gauss_seidel_contractor(mA, mb, precondition=true) # Gauss-Seidel Method (Vectorized); precondition=true by default
30+
31+
rts = gauss_elimination_interval!(p, A, b, precondition=true) # Gaussian Elimination; precondition=true by default
32+
rts = gauss_elimination_interval(A, b, precondition=true) # Gaussian Elimination; precondition=true by default

perf/linear_eq.jl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using BenchmarkTools, Compat, DataFrames, IntervalRootFinding, IntervalArithmetic, StaticArrays
2+
3+
function randVec(n::Int)
4+
a = randn(n)
5+
A = Interval.(a)
6+
mA = MVector{n}(A)
7+
sA = SVector{n}(A)
8+
return A, mA, sA
9+
end
10+
11+
function randMat(n::Int)
12+
a = randn(n, n)
13+
A = Interval.(a)
14+
mA = MMatrix{n, n}(A)
15+
sA = SMatrix{n, n}(A)
16+
return A, mA, sA
17+
end
18+
19+
function benchmark(max=10)
20+
df = DataFrame()
21+
df[:Method] = ["Array", "MArray", "SArray", "Contractor", "ContractorMArray", "ContractorSArray"]
22+
for n in 1:max
23+
A, mA, sA = randMat(n)
24+
b, mb, sb = randVec(n)
25+
t1 = @belapsed gauss_seidel_interval($A, $b)
26+
t2 = @belapsed gauss_seidel_interval($mA, $mb)
27+
t3 = @belapsed gauss_seidel_interval($sA, $sb)
28+
t4 = @belapsed gauss_seidel_contractor($A, $b)
29+
t5 = @belapsed gauss_seidel_contractor($mA, $mb)
30+
t6 = @belapsed gauss_seidel_contractor($sA, $sb)
31+
df[Symbol("$n")] = [t1, t2, t3, t4, t5, t6]
32+
end
33+
a = []
34+
for i in 1:max
35+
push!(a, Symbol("$i"))
36+
end
37+
df1 = stack(df, a)
38+
dfnew = unstack(df1, :variable, :Method, :value)
39+
dfnew = rename(dfnew, :variable => :n)
40+
println(dfnew)
41+
dfnew
42+
end
43+
44+
function benchmark_elimination(max=10)
45+
df = DataFrame()
46+
df[:Method] = ["Gauss Elimination", "Base.\\"]
47+
for n in 1:max
48+
A, mA, sA = randMat(n)
49+
b, mb, sb = randVec(n)
50+
t1 = @belapsed gauss_elimination_interval($A, $b)
51+
t2 = @belapsed gauss_elimination_interval1($A, $b)
52+
df[Symbol("$n")] = [t1, t2]
53+
end
54+
a = []
55+
for i in 1:max
56+
push!(a, Symbol("$i"))
57+
end
58+
df1 = stack(df, a)
59+
dfnew = unstack(df1, :variable, :Method, :value)
60+
dfnew = rename(dfnew, :variable => :n)
61+
println(dfnew)
62+
dfnew
63+
end

perf/linear_eq_results.txt

Lines changed: 149 additions & 0 deletions
Large diffs are not rendered by default.

perf/linear_eq_tabular.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Gauss Seidel
2+
3+
│ Row │ variable │ Array │ Contractor │ MArray │ SArray1 │ SArray2 │
4+
├─────┼──────────┼─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤
5+
│ 1 │ n = 1 │ 1.1426e-5 │ 2.4173e-5 │ 1.5803e-5 │ 1.014e-5 │ 9.512e-6 │
6+
│ 2 │ n = 2 │ 2.4413e-5 │ 4.0504e-5 │ 3.9829e-5 │ 2.4083e-5 │ 2.4083e-5 │
7+
│ 3 │ n = 3 │ 4.5194e-5 │ 6.4343e-5 │ 9.1236e-5 │ 4.5533e-5 │ 5.1221e-5 │
8+
│ 4 │ n = 4 │ 7.2639e-5 │ 9.3671e-5 │ 0.00016058 │ 7.0972e-5 │ 6.9937e-5 │
9+
│ 5 │ n = 5 │ 0.000103281 │ 0.000127489 │ 0.000262814 │ 0.000104823 │ 0.000102729 │
10+
│ 6 │ n = 6 │ 0.000141315 │ 0.000169992 │ 0.000416266 │ 0.000144021 │ 0.000139086 │
11+
│ 7 │ n = 7 │ 0.000195001 │ 0.000217059 │ 0.000680848 │ 0.000198164 │ 0.000191145 │
12+
│ 8 │ n = 8 │ 0.000247193 │ 0.00027535 │ 0.00122559 │ 0.000251089 │ 0.000241972 │
13+
│ 9 │ n = 9 │ 0.000306262 │ 0.000338028 │ 0.0020223 │ 0.000310961 │ 0.000299153 │
14+
│ 10 │ n = 10 │ 0.00037781 │ 0.000414073 │ 0.00294134 │ 0.000386235 │ 0.000367335 │
15+
│ 11 │ n = 11 │ 0.000465017 │ 0.000489114 │ 0.00585246 │ 0.000473425 │ 0.000447176 │
16+
│ 12 │ n = 12 │ 0.00055403 │ 0.000587152 │ 0.0110894 │ 0.000573395 │ 0.00054274 │
17+
│ 13 │ n = 13 │ 0.000659994 │ 0.000692402 │ 0.0160662 │ 0.000686098 │ 0.000643391 │
18+
│ 14 │ n = 14 │ 0.000777718 │ 0.000793626 │ 0.0184595 │ 0.000800443 │ 0.000767576 │
19+
│ 15 │ n = 15 │ 0.000910174 │ 0.000952207 │ 0.0239422 │ 0.000972855 │ 0.000899402 │
20+
│ 16 │ n = 16 │ 0.00107785 │ 0.00111443 │ 0.0303295 │ 0.00113467 │ 0.0010867 │
21+
│ 17 │ n = 17 │ 0.00122326 │ 0.00125759 │ 0.0398127 │ 0.00134961 │ 0.00125675 │
22+
│ 18 │ n = 18 │ 0.00167176 │ 0.00159463 │ 0.043795 │ 0.00181409 │ 0.00162584 │
23+
│ 19 │ n = 19 │ 0.0018632 │ 0.00185617 │ 0.0549107 │ 0.00208529 │ 0.00196587 │
24+
│ 20 │ n = 20 │ 0.0020604 │ 0.00210595 │ 0.0635598 │ 0.00232704 │ 0.00212374 │
25+
26+
10×7 DataFrames.DataFrame
27+
│ Row │ n │ Array │ Contractor │ ContractorMArray │ ContractorSArray │ MArray │ SArray │
28+
├─────┼────┼─────────────┼─────────────┼──────────────────┼──────────────────┼─────────────┼─────────────┤
29+
│ 1 │ 1 │ 1.0188e-5 │ 2.5276e-5 │ 0.000781355 │ 0.000764627 │ 1.6959e-5 │ 1.1216e-5 │
30+
│ 2 │ 10 │ 0.000377853 │ 0.000407817 │ 0.00122723 │ 0.00123259 │ 0.0029247 │ 0.000393896 │
31+
│ 3 │ 2 │ 2.609e-5 │ 4.3597e-5 │ 0.000802467 │ 0.000796962 │ 4.1039e-5 │ 2.6654e-5 │
32+
│ 4 │ 3 │ 4.7362e-5 │ 6.317e-5 │ 0.000832029 │ 0.000821289 │ 9.1695e-5 │ 4.7007e-5 │
33+
│ 5 │ 4 │ 7.0378e-5 │ 9.1502e-5 │ 0.00085429 │ 0.000844048 │ 0.000163604 │ 7.1166e-5 │
34+
│ 6 │ 5 │ 0.000104813 │ 0.000129112 │ 0.000898626 │ 0.000889969 │ 0.000268756 │ 0.000112476 │
35+
│ 7 │ 6 │ 0.000142316 │ 0.000168891 │ 0.000941422 │ 0.000922727 │ 0.000430442 │ 0.000146906 │
36+
│ 8 │ 7 │ 0.000192073 │ 0.000218861 │ 0.00108794 │ 0.000997821 │ 0.00073304 │ 0.000198962 │
37+
│ 9 │ 8 │ 0.000242832 │ 0.000273341 │ 0.00107159 │ 0.00106769 │ 0.00126407 │ 0.000250956 │
38+
│ 10 │ 9 │ 0.000308256 │ 0.000333722 │ 0.00116203 │ 0.00116428 │ 0.00196451 │ 0.000314499 │
39+
40+
Gauss Elimination
41+
42+
5×3 DataFrames.DataFrame
43+
│ Row │ n │ Base.\\ │ Gauss Elimination │
44+
├─────┼───┼────────────┼───────────────────┤
45+
│ 1 │ 1 │ 1.84e-6 │ 8.127e-6 │
46+
│ 2 │ 2 │ 3.1615e-6 │ 1.0029e-5 │
47+
│ 3 │ 3 │ 4.53986e-6 │ 1.1211e-5 │
48+
│ 4 │ 4 │ 6.8655e-6 │ 1.4342e-5 │
49+
│ 5 │ 5 │ 1.0773e-5 │ 1.7725e-5 │

src/IntervalRootFinding.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ using ForwardDiff
99
using StaticArrays
1010

1111

12-
import Base: , show, big
12+
import Base: , show, big, \
1313

1414
import Polynomials: roots
1515

@@ -18,7 +18,10 @@ export
1818
derivative, jacobian, # reexport derivative from ForwardDiff
1919
Root, is_unique,
2020
roots, find_roots,
21-
bisect, newton1d, quadratic_roots
21+
bisect, newton1d, quadratic_roots,
22+
gauss_seidel_interval, gauss_seidel_interval!,
23+
gauss_seidel_contractor, gauss_seidel_contractor!,
24+
gauss_elimination_interval, gauss_elimination_interval!
2225

2326
export isunique, root_status
2427

@@ -43,6 +46,7 @@ include("contractors.jl")
4346
include("roots.jl")
4447
include("newton1d.jl")
4548
include("quadratic.jl")
49+
include("linear_eq.jl")
4650

4751

4852
gradient(f) = X -> ForwardDiff.gradient(f, X[:])

src/linear_eq.jl

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
Preconditions the matrix A and b with the inverse of mid(A)
3+
"""
4+
function preconditioner(A::AbstractMatrix, b::AbstractArray)
5+
6+
Aᶜ = mid.(A)
7+
B = inv(Aᶜ)
8+
9+
return B*A, B*b
10+
11+
end
12+
13+
function gauss_seidel_interval(A::AbstractMatrix, b::AbstractArray; precondition=true, maxiter=100)
14+
15+
n = size(A, 1)
16+
x = similar(b)
17+
x .= -1e16..1e16
18+
gauss_seidel_interval!(x, A, b, precondition=precondition, maxiter=maxiter)
19+
return x
20+
end
21+
"""
22+
Iteratively solves the system of interval linear
23+
equations and returns the solution set. Uses the
24+
Gauss-Seidel method (Hansen-Sengupta version) to solve the system.
25+
Keyword `precondition` to turn preconditioning off.
26+
Eldon Hansen and G. William Walster : Global Optimization Using Interval Analysis - Chapter 5 - Page 115
27+
"""
28+
function gauss_seidel_interval!(x::AbstractArray, A::AbstractMatrix, b::AbstractArray; precondition=true, maxiter=100)
29+
30+
precondition && ((A, b) = preconditioner(A, b))
31+
32+
n = size(A, 1)
33+
34+
@inbounds for iter in 1:maxiter
35+
= copy(x)
36+
for i in 1:n
37+
Y = b[i]
38+
for j in 1:n
39+
(i == j) || (Y -= A[i, j] * x[j])
40+
end
41+
Z = extended_div(Y, A[i, i])
42+
x[i] = hull((x[i] Z[1]), x[i] Z[2])
43+
end
44+
if all(x .== x¹)
45+
break
46+
end
47+
end
48+
x
49+
end
50+
51+
function gauss_seidel_contractor(A::AbstractMatrix, b::AbstractArray; precondition=true, maxiter=100)
52+
53+
n = size(A, 1)
54+
x = similar(b)
55+
x .= -1e16..1e16
56+
x = gauss_seidel_contractor!(x, A, b, precondition=precondition, maxiter=maxiter)
57+
return x
58+
end
59+
60+
function gauss_seidel_contractor!(x::AbstractArray, A::AbstractMatrix, b::AbstractArray; precondition=true, maxiter=100)
61+
62+
precondition && ((A, b) = preconditioner(A, b))
63+
64+
n = size(A, 1)
65+
66+
diagA = Diagonal(A)
67+
extdiagA = copy(A)
68+
for i in 1:n
69+
if (typeof(b) <: SArray)
70+
extdiagA = setindex(extdiagA, Interval(0), i, i)
71+
else
72+
extdiagA[i, i] = Interval(0)
73+
end
74+
end
75+
inv_diagA = inv(diagA)
76+
77+
for iter in 1:maxiter
78+
= copy(x)
79+
x = x .∩ (inv_diagA * (b - extdiagA * x))
80+
if all(x .== x¹)
81+
break
82+
end
83+
end
84+
x
85+
end
86+
87+
function gauss_elimination_interval(A::AbstractMatrix, b::AbstractArray; precondition=true)
88+
89+
n = size(A, 1)
90+
x = similar(b)
91+
x .= -1e16..1e16
92+
x = gauss_elimination_interval!(x, A, b, precondition=precondition)
93+
94+
return x
95+
end
96+
"""
97+
Solves the system of linear equations using Gaussian Elimination,
98+
with (or without) preconditioning. (kwarg - `precondition`)
99+
Luc Jaulin, Michel Kieffer, Olivier Didrit and Eric Walter - Applied Interval Analysis - Page 72
100+
"""
101+
function gauss_elimination_interval!(x::AbstractArray, a::AbstractMatrix, b::AbstractArray; precondition=true)
102+
103+
if precondition
104+
((a, b) = preconditioner(a, b))
105+
else
106+
a = copy(a)
107+
b = copy(b)
108+
end
109+
n = size(a, 1)
110+
111+
p = zeros(x)
112+
113+
for i in 1:(n-1)
114+
if 0 a[i, i] # diagonal matrix is not invertible
115+
p .= entireinterval(b[1])
116+
return p .∩ x
117+
end
118+
119+
for j in (i+1):n
120+
α = a[j, i] / a[i, i]
121+
b[j] -= α * b[i]
122+
123+
for k in (i+1):n
124+
a[j, k] -= α * a[i, k]
125+
end
126+
end
127+
end
128+
129+
for i in n:-1:1
130+
sum = 0
131+
for j in (i+1):n
132+
sum += a[i, j] * p[j]
133+
end
134+
p[i] = (b[i] - sum) / a[i, i]
135+
end
136+
137+
p .∩ x
138+
end
139+
140+
function gauss_elimination_interval1(A::AbstractMatrix, b::AbstractArray; precondition=true)
141+
142+
n = size(A, 1)
143+
x = fill(-1e16..1e16, n)
144+
145+
x = gauss_elimination_interval1!(x, A, b, precondition=precondition)
146+
147+
return x
148+
end
149+
"""
150+
Using `Base.\``
151+
"""
152+
function gauss_elimination_interval1!(x::AbstractArray, a::AbstractMatrix, b::AbstractArray; precondition=true)
153+
154+
precondition && ((a, b) = preconditioner(a, b))
155+
156+
a \ b
157+
end
158+
159+
\(A::StaticMatrix{Interval{T}}, b::StaticArray{Interval{T}}; kwargs...) where T = gauss_elimination_interval(A, b, kwargs...)
160+
\(A::Matrix{Interval{T}}, b::Array{Interval{T}}; kwargs...) where T = gauss_elimination_interval(A, b, kwargs...)

test/linear_eq.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using IntervalArithmetic, StaticArrays, IntervalRootFinding
2+
3+
function randVec(n::Int)
4+
a = randn(n)
5+
A = Interval.(a)
6+
mA = MVector{n}(A)
7+
sA = SVector{n}(A)
8+
return A, mA, sA
9+
end
10+
11+
function randMat(n::Int)
12+
a = randn(n, n)
13+
A = Interval.(a)
14+
mA = MMatrix{n, n}(A)
15+
sA = SMatrix{n, n}(A)
16+
return A, mA, sA
17+
end
18+
19+
@testset "Linear Equations" begin
20+
21+
A = [[2..3 0..1;1..2 2..3], ]
22+
b = [[0..120, 60..240], ]
23+
x = [[-120..90, -60..240], ]
24+
25+
for i in 1:10
26+
rand_a = randMat(i)[1]
27+
rand_b = randVec(i)[1]
28+
rand_c = rand_a * rand_b
29+
push!(A, rand_a)
30+
push!(b, rand_c)
31+
push!(x, rand_b)
32+
end
33+
for i in 1:length(A)
34+
for precondition in (false, true)
35+
for f in (gauss_seidel_interval, gauss_seidel_contractor, gauss_elimination_interval, \)
36+
@test all(x[i] .⊆ f(A[i], b[i]))
37+
end
38+
end
39+
end
40+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ include("roots.jl")
77
include("newton1d.jl")
88
include("quadratic.jl")
99
include("test_smiley.jl")
10+
include("linear_eq.jl")

0 commit comments

Comments
 (0)