JuliaNLSolvers / NLsolve.jl

Julia solvers for systems of nonlinear equations and mixed complementarity problems

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Prototype of a specialized algorithm for LCP

jlperla opened this issue · comments

@arnavs cc: @pkofod
As discussed, it would be great to check if a specialized algorithm for LCP problems has higher performance than the general nonlinear one.

Yuval has given permission to port over https://www.mathworks.com/matlabcentral/fileexchange/20952-lcp-mcp-solver-newton-based?s_tid=prof_contriblnk

I think we should, before trying to hook it into NLsolve:

  • Do a direct port over of the LCP function there, being careful with preallocating vectors/type stability/etc. but otherwise not do any enhancements
  • Verify that it gives the same solution as the matlab one
  • Compare the performance to PATH's LCP and the MCP in NLsolve. If the performance is sufficiently better than the current NLsolve, then we can take the next steps.

Doesn't Jump support this? It looks like a well studied class of problems for which there should be efficient solvers there.

@antoine-levitt I had trouble using the VariationalInequality and Complementarity packages in JuMP, mainly because of the vectorized map. If you'd like to take a crack at it, here's a minimal setup of the problem in PATHSolver.

using PATHSolver, LinearAlgebra
n = 50
B = Tridiagonal(rand(n-1), rand(n), rand(n-1))
q = rand(n)
f = z -> B*z + q
lb = -1000. * ones(n)
ub = 1000. * ones(n)
options(convergence_tolerance = 1e-12, output = :yes, time_limit = 600) # 10 minute budget
@time exit_code, sol_z, sol_f = solveLCP(f, lb, ub)

@antoine-levitt Actually, after some inspection, this is not bad at all in pure JuMP. So maybe the takeaway is that the two specialized packages above are ill-adapted for this.

One straightforward implementation is:

using JuMP, Ipopt, LinearAlgebra
# setup objects
n = 50 
tolerance = 1e-12
B = Tridiagonal(rand(n-1), rand(n), rand(n-1))
q = rand(n)
# define and solve model
m = Model(solver = IpoptSolver(tol = tolerance))
@variable(m, z[1:n])
@constraint(m, z .>= 0)
@constraint(m, B*z + q .>= 0)
@objective(m, Min, z' * B * z + z' * q)
solve(m)

The quadratic programming formulation might work, but there are specialized algorithms that could dominate it... I don't think people use the QP formulation for these in practice (see http://fmwww.bc.edu/ec-p/software/Miranda/chapt4.pdf for a few of the algorithms). Lemke and others are the standards, I believe.

That said, perhaps a commercial quality QP solver would win against a more specialized implementation done by us.

I’ll benchmark the commercial solvers.

Stupid question time: is it possible to AD through a JuMP.solve call?

Had an actual look at the code and paper. In principle all you have to do is to do the FB transform, and then pass it to curve_fit in LsqFit.

FYI, will maintain a table here with the results on the 1000-node diffusion problem.

Mosek (QP formulation) --- 4.1 e-6 seconds 
PATHSolver  --- 13.6 e-6 seconds 
OSQP (QP formulation) --- 164 e-6 seconds 
Ipopt --- 171 e-6 seconds 
NLsolve --- 589 e-6 seconds

Note that NLsolve scales particularly poorly in the jump from 300 to 1000 nodes (it's a whole new order of magnitude slower).

To Do:

  • Eliminate PSD error in Gurobi
  • Try other formulations (i.e., direct LCP).
  • Give Yuval's thing a shot.

Might be worth trying the exact matlab thing from Ben's code using Yuvals stuff with 1000 nodes to see how it goes

Sure, will give it a shot tomorrow. Should give us a sense of the value of porting.

Could you share the script?

@jlperla You asked for timing information on the solveMCP coming out of PATHSolve. I ran the code again (during lunch, on my laptop). Here's what we have:

solveLCP (PATHSolve): 3.955 ms
solveMCP (PATHSolve): 14.159 ms
NLSolve: 76.867 ms
JuMP/IPOPT: 99.152 ms
JuMP/OSQP: 45.953 ms

I think with the exception of OSQP, we're in roughly the same tolerance ballpark. Just in case it matters:

uarnavsood@mbp:~$ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.1.0 (2019-01-21)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin14.5.0)
  CPU: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, skylake)