Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System of Linear Equations #67

Merged
merged 22 commits into from
Jul 17, 2018
Merged

Conversation

eeshan9815
Copy link
Contributor

No description provided.

@codecov-io
Copy link

codecov-io commented May 14, 2018

Codecov Report

Merging #67 into master will increase coverage by 6.37%.
The diff coverage is 85.13%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #67      +/-   ##
==========================================
+ Coverage   56.14%   62.52%   +6.37%     
==========================================
  Files          10       10              
  Lines         431      467      +36     
==========================================
+ Hits          242      292      +50     
+ Misses        189      175      -14
Impacted Files Coverage Δ
src/IntervalRootFinding.jl 4.65% <ø> (+2.61%) ⬆️
src/linear_eq.jl 85.13% <85.13%> (ø)
src/contractors.jl 90% <0%> (-0.48%) ⬇️
src/newton.jl 0% <0%> (ø) ⬆️
src/krawczyk.jl 0% <0%> (ø) ⬆️
src/bisect.jl
src/roots.jl 88.13% <0%> (+2.19%) ⬆️
src/complex.jl 83.33% <0%> (+8.33%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 25ebe4c...7746380. Read the comment docs.

@dpsanders
Copy link
Member

Thanks! I don't see the benchmarks though.

@eeshan9815
Copy link
Contributor Author

I manually benchmarked, and shared the results on #intervals in slack

@dpsanders
Copy link
Member

Please include at least the benchmarking script (using BenchmarkTools) in a new directory perf.

@eeshan9815
Copy link
Contributor Author

eeshan9815 commented May 15, 2018

I have included the script and results for n = 1:25
cc @dpsanders

@dpsanders
Copy link
Member

That's the kind of thing I had in mind, thanks. You could instead collect the data using @belapsed (I think) and output a table using Dataframes or something like that.

@eeshan9815
Copy link
Contributor Author

Okay, I'll try it out!
So which version of the function should I retain? I don't think MArray improved the performance a lot.

@dpsanders
Copy link
Member

Try à version using IntervalBox and setindex.

@dpsanders
Copy link
Member

IntervalBox is immutable like an SVector. So modifying it actually requires making a new copy and changing the relevant entry using setindex (without !)

src/linear_eq.jl Outdated
function gauss_seidel_interval_static1{T, N}(A::SMatrix{N, N, Interval{T}}, b::SVector{N, Interval{T}}; precondition=true, maxiter=100)

n = size(A, 1)
x = @MVector fill(-1e16..1e16, n)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SVector?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized only x has to be modified, so I tried a version where A and b are SVectors, and that showed improvement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try a version where x is an SVector and you use setindex to "modify" it (i.e. replace x with a new SVector).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
I think this version gives the best benchmarks.

@dpsanders
Copy link
Member

This needs tests.

@eeshan9815
Copy link
Contributor Author

@dpsanders Travis and AppVeyor both use the latest tagged version and don't have extended_div, and hence are failing.

@lbenet
Copy link
Member

lbenet commented May 17, 2018

I tried to tag a new version but it was causing failures in ValidatedNumerics. I did some upgrades, but I think we should plan carefully how to make patches-versions, so they don't break everything. See this comment.

@dpsanders
Copy link
Member

Actually the matrix version could be pretty efficient when using SMatrix and SVector everywhere. Have you tried that?

@eeshan9815 eeshan9815 force-pushed the linear-eq branch 2 times, most recently from db737c7 to e3f58c1 Compare May 17, 2018 23:17
@eeshan9815
Copy link
Contributor Author

Done.
I think this version gives the best benchmarks.

src/linear_eq.jl Outdated
"""
function gauss_seidel_interval_static2!{T, N}(x::SVector{N, Interval{T}}, A::SMatrix{N, N, Interval{T}}, b::SVector{N, Interval{T}}; precondition=true, maxiter=100)

precondition && ((M, r) = preconditioner_static2(A, b))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens is precondition is false? Where are M and r defined then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@dpsanders
Copy link
Member

So it seems just using normal arrays is fine?

@eeshan9815
Copy link
Contributor Author

So it seems just using normal arrays is fine?

More or less, yes. Should I remove the other implementations and update the PR?

t2 = @belapsed gauss_seidel_interval_static($mA, $mb)
t3 = @belapsed gauss_seidel_interval_static1($sA, $sb)
t4 = @belapsed gauss_seidel_interval_static2($sA, $sb)
t5 = @belapsed gauss_seidel_contractor($A, $b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try this one with the static vectors too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gauss_seidel_contractor with static vectors? Okay

t3 = @belapsed gauss_seidel_interval_static1($sA, $sb)
t4 = @belapsed gauss_seidel_interval_static2($sA, $sb)
t5 = @belapsed gauss_seidel_contractor($A, $b)
df[Symbol("n = $n")] = [t1, t2, t3, t4, t5]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need Symbol here? Can't you just use a string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

julia> df["test"] = [1, 2, 3, 4]
ERROR: MethodError: no method matching setindex!(::DataFrames.DataFrame, ::Array{Int64,1}, ::String)

The DataFrames package takes a symbol as the index.

t3 = @belapsed gauss_seidel_interval_static1($sA, $sb)
t4 = @belapsed gauss_seidel_interval_static2($sA, $sb)
t5 = @belapsed gauss_seidel_contractor($A, $b)
df[Symbol("n = $n")] = [t1, t2, t3, t4, t5]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you in fact just make n another column?

│ 17 │ n = 17 │ 0.00122326 │ 0.00125759 │ 0.0398127 │ 0.00134961 │ 0.00125675 │
│ 18 │ n = 18 │ 0.00167176 │ 0.00159463 │ 0.043795 │ 0.00181409 │ 0.00162584 │
│ 19 │ n = 19 │ 0.0018632 │ 0.00185617 │ 0.0549107 │ 0.00208529 │ 0.00196587 │
│ 20 │ n = 20 │ 0.0020604 │ 0.00210595 │ 0.0635598 │ 0.00232704 │ 0.00212374 │
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually n seems to be redundant since it's just the same as the row.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The row numbers need not be sequential due to the stack operations, That's why I kept the column with the value of n

src/linear_eq.jl Outdated

n = size(A, 1)

for iter in 1:maxiter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add @inbounds to the outer for loop in each of these functions.

@dpsanders
Copy link
Member

You should squash down the implementations as I suggested; I think you should leave all of them for now.

Could you please add the example from the Jaulin book in the examples folder?

Actually you should keep a separate repo, e.g. in your GitHub account or in JuliaIntervals, with a Jupyter notebook with usage examples and explanation.

src/linear_eq.jl Outdated
Keyword `precondition` to turn preconditioning off.
Eldon Hansen and G. William Walster : Global Optimization Using Interval Analysis - Chapter 5 - Page 115
"""
function gauss_seidel_interval_static1!{T, N}(x::MVector{N, Interval{T}}, A::SMatrix{N, N, Interval{T}}, b::SVector{N, Interval{T}}; precondition=true, maxiter=100)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several versions of the function whose code is identical.
Just make a single version and do much looser type annotations as AbstractArray etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Or no type annotations at all)

@eeshan9815
Copy link
Contributor Author

@dpsanders Is this accurate?

@eeshan9815
Copy link
Contributor Author

Tests fail as it uses extended_div which is not in the latest tagged version of IntervalArithmetic.
Tests pass locally if the current master is used.

@dpsanders
Copy link
Member

Ah great, thanks

@eeshan9815
Copy link
Contributor Author

I have redefined \ for Intervals. Currently, it uses the gauss_elimination_interval method with precondition=true.
This is due to the advantages of preconditioning such as reducing dependence mentioned in various books such as Global Optimisation using Interval Analysis by Eldon Hansen.

Copy link
Member

@dpsanders dpsanders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Could you please add Tests for \ and comparisons to Base.\

@eeshan9815
Copy link
Contributor Author

Since \ is same as gauss_elimination_interval, for which tests exist already, is there a need to define tests for \ separately?
Also, comparisions exist in the perf/linear_eq_tabular.txt file.

@eeshan9815 eeshan9815 changed the title [WIP] System of Linear Equations System of Linear Equations Jun 30, 2018
@dpsanders
Copy link
Member

Right, but the tests are also for future-proofing, eg if we change the implementation of \ again. So you can just add a couple of tests to make sure the output of \ and gaussian elim are the same.

@dpsanders
Copy link
Member

@eeshan9815 Did you actually check that the definition of \ in the package is being called? It doesn't seem to be. You need to import Base: \.

@dpsanders
Copy link
Member

On the example in the testset "Linear Equations", the new definition seems to be worse than using Base's \.

@eeshan9815
Copy link
Contributor Author

I have imported Base.\ in IntervalRootFinding.jland it seems to be using gauss_elimination_interval instead of Base.\
Also, preconditioning widens the solution set by a little bit. It's a trade-off mentioned in the Hansen book as well.

@dpsanders
Copy link
Member

Hmm, I see, sorry. I don't know why it didn't work for me, then.

What's the advantage of preconditioning, then? I thought it was supposed to narrow, not widen, the result.

@dpsanders
Copy link
Member

After checking out your PR, I get

julia> 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]
3×3 Array{IntervalArithmetic.Interval{Float64},2}:
 [4, 5]            [-1, 1]                    [1.5, 2.5]
    [-0.5, 0.5]   [-7, -5]                [1, 2]
    [-1.5, -0.5]       [-0.700001, -0.5]  [2, 3]

julia> b = [3..4, 0..2, 3..4]
3-element Array{IntervalArithmetic.Interval{Float64},1}:
 [3, 4]
 [0, 2]
 [3, 4]

julia> A \ b
3-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-1.81928, 1.16873]
 [-0.41407, 1.72523]
  [0.700232, 3.42076]

julia> @which A \ b
\(A::AbstractArray{T,2} where T, B::Union{AbstractArray{T,1}, AbstractArray{T,2}} where T) in Base.LinAlg at linalg/generic.jl:820

which shows that the \ from Base is being used. Not sure why.

@dpsanders
Copy link
Member

Ignore me, I seem not to have managed to checkout the latest version, apologies.

@dpsanders
Copy link
Member

No, don't ignore me -- you defined \ in test/linear_eq.jl instead of in the source code!

end
end

\(A::StaticMatrix{Interval{T}}, b::StaticArray{Interval{T}}; kwargs...) where T = gauss_elimination_interval(A, b, kwargs...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be moved to the source code.

I think you can define a single method if you use AbstractMatrix and AbstractVector.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be moved to the source code.

I'm terribly sorry, I didn't realize.

I think you can define a single method if you use AbstractMatrix and AbstractVector

I tried AbstractArrays earlier, but it doesn't work since it matches its type with the \ for StaticArrays or Arrays instead.

What's the advantage of preconditioning, then? I thought it was supposed to narrow, not widen, the result

Preconditioning doesn't always give a better result. Specifically, for M-matrices, the non-preconditioned version computes the exact hull whereas preconditioning causes some widening.

@dpsanders
Copy link
Member

No problem, thanks!

@dpsanders
Copy link
Member

Running gauss_seidel seems to modify something it shouldn't? The result of A \ b shouldn't change in the following session:

julia> IntervalRootFinding.gauss_elimination_interval(A, b)
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-130.228, 167.728]
  [-60.0001, 267.273]

julia> IntervalRootFinding.gauss_elimination_interval(A, b, precondition=false)
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-120, 90]
 [-60, 240]

julia> IntervalRootFinding.gauss_seidel_interval(A, b, precondition=false)
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-5e+15, 5.00001e+15]
 [-1e+16, 1e+16]

julia> A
2×2 Array{IntervalArithmetic.Interval{Float64},2}:
 [2, 3]  [0, 1]
 [1, 2]  [1, 3]

julia> b
2-element Array{IntervalArithmetic.Interval{Float64},1}:
   [0, 120]
 [-60, 240]

julia> A \ b
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-1e+16, 1e+16]
 [-1e+16, 1e+16]

@dpsanders
Copy link
Member

I think this needs more tests. E.g. the gauss_seidel methods give huge pointless answers for the single test case.

Z = extended_div(Y, A[i, i])
x[i] = hull((x[i] ∩ Z[1]), x[i] ∩ Z[2])
end
if all(x .== x¹)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably use a tolerance instead.
Can you write this as all(==, x, x1)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing that gives this error -

julia> A = [2..3 0..1;1..2 2..3]
2×2 Array{IntervalArithmetic.Interval{Float64},2}:
 [2, 3]  [0, 1]
 [1, 2]  [2, 3]

julia> all(==, A, A)
ERROR: ArgumentError: reduced dimension(s) must be integers
Stacktrace:
 [1] reduced_indices(::Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}, ::Array{IntervalArithmetic.Interval{Float64},2}) at ./reducedim.jl:35
 [2] reducedim_initarray(::Array{IntervalArithmetic.Interval{Float64},2}, ::Array{IntervalArithmetic.Interval{Float64},2}, ::Bool, ::Type{Bool}) at ./reducedim.jl:73
 [3] mapreducedim at ./reducedim.jl:242 [inlined]
 [4] all(::Function, ::Array{IntervalArithmetic.Interval{Float64},2}, ::Array{IntervalArithmetic.Interval{Float64},2}) at ./reducedim.jl:583
 [5] macro expansion at /home/eeshan/.julia/v0.6/Atom/src/repl.jl:118 [inlined]
 [6] anonymous at ./<missing>:?

@dpsanders
Copy link
Member

@eeshan9815 There seems to be a real test failure here.

@eeshan9815
Copy link
Contributor Author

eeshan9815 commented Jul 17, 2018

The test failure appears to be in a test for 2D roots, giving error messages similar to the ones faced when the SVector was initially made a data member of IntervalBox.
It should work after merging #83

@eeshan9815
Copy link
Contributor Author

The incorrect modification issue has also been resolved.

julia> IntervalRootFinding.gauss_elimination_interval(A, b)
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-130.228, 167.728] 
  [-60.0001, 267.273]

julia> IntervalRootFinding.gauss_elimination_interval(A, b, precondition=false)
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-120, 90]
 [-60, 240]

julia> IntervalRootFinding.gauss_seidel_interval(A, b, precondition=false)
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-120.001, 90.0001] 
  [-60.0001, 240.001]

julia> A
2×2 Array{IntervalArithmetic.Interval{Float64},2}:
 [2, 3]  [0, 1]
 [1, 2]  [2, 3]

julia> b
2-element Array{IntervalArithmetic.Interval{Float64},1}:
  [0, 120]
 [60, 240]

julia> A \ b
2-element Array{IntervalArithmetic.Interval{Float64},1}:
 [-130.228, 167.728] 
  [-60.0001, 267.273]

@dpsanders
Copy link
Member

@eeshan9815 Tests are now passing. Is this ready?

@eeshan9815
Copy link
Contributor Author

Yes, I think so. I added 88 tests (mostly randomly generated) to make sure the methods are working.

@dpsanders dpsanders merged commit 5b7a8ab into JuliaIntervals:master Jul 17, 2018
@dpsanders
Copy link
Member

Thanks very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants