Skip to content

Conversation

@mlubin
Copy link
Member

@mlubin mlubin commented Jan 10, 2017

In 0.6, we can essentially no longer run eval to load solver packages at runtime when we feel like it. Here's a new design for the default solver mechanism that works.

Module-level constants like MathProgBase.defaultLPsolver become methods MathProgBase.defaultLPsolver() which return solver objects. On 0.6 and later, the methods can only look at loaded packages. It's now the user's responsibility to load a capable solver package before solving a model. As a convenience, we provide MathProgBase.loaddefaultsolvers() which can be put at the top of a script to make everything magically work. On 0.5, I've implemented it to print a helpful warning in the use case that will become an error on 0.6.

This will become MathProgBase 0.6 since it's a breaking change.

CC @blegat @joehuchette @tkelman @jrevels @odow @joaquimg

Closes #148

@odow
Copy link
Member

odow commented Jan 10, 2017

I would even be in favour of making the solver a required (rather than optional) argument.

The "you don't have to worry about choosing a solver (if you don't want to)" approach has always seemed a bit too magical for me. When I first started using JuMP it led to some confusion where I used to change solvers by (un)commenting different using Solver lines. It made sense that you would want to be "using Gurobi" but then I didn't know how to set options etc..

@mlubin
Copy link
Member Author

mlubin commented Jan 10, 2017

I'd be open to considering that. What we still need though is a way to print a useful message if the user doesn't provide a solver giving them a suggestion for which solvers could work for their class of problem.

@odow
Copy link
Member

odow commented Jan 10, 2017

function buildlp(c::InputVector, A::AbstractMatrix, rowlb::InputVector, rowub::InputVector, lb::InputVector, ub::InputVector, solver::AbstractMathProgSolver)
    # as above
end

function buildlp(c::InputVector, A::AbstractMatrix, rowlb::InputVector, rowub::InputVector, lb::InputVector, ub::InputVector)
    error("""
        You haven't specified a linear solver.

        You can use any solver listed on the solvers table of http://www.juliaopt.org/ that has a tick in the Linear/Quadratic column. 

        A (free) example is Cbc.jl. You can install it (if you haven't already) by going
            julia> Pkg.add("Cbc")
    """)
end

@blegat
Copy link
Member

blegat commented Jan 10, 2017

Can we do something like

function loadfirst(solverlist)
     for (pkgname, _) in solverlist
         try
             eval(Expr(:import, pkgname))
             # stop when we've found a working solver in this category
             continue
         end
     end
 end
loaddefaultLPsolver() = loadfirst(LPsolvers)

to make it possible to load only the solver needed ?
Would then the following work ?

function defaultLPsolver_autoload()
    loaddefaultLPsolver()
    defaultLPsolver()
end

@mlubin
Copy link
Member Author

mlubin commented Jan 10, 2017

Would then the following work ?

No, it would return a solver "from the future". Its implementation of loadproblem! for example wouldn't necessarily be visible to the caller. See the new docs: http://docs.julialang.org/en/latest/manual/methods.html#Redefining-Methods-1

@mlubin
Copy link
Member Author

mlubin commented Jan 10, 2017

@odow, that gets trickier for JuMP because of the multiple problem classes. It would be nice if this were more programmatic (jump-dev/JuMP.jl#83).

@blegat
Copy link
Member

blegat commented Jan 10, 2017

@mlubin so the function loaddefaultsolvers() you have added should be used in the past ? In the documentation they say that it needs to be called from a previous REPL command so

julia> loaddefaultsolvers()
julia> defaultLPsolver()

would work but

julia> loaddefaultsolvers(); defaultLPsolver()

would not.
So why do you say that loaddefaultsolvers() "can be put at the top of a script" ? It will be in the same runtime environment than defaultLPsolver() then won't it ?

@mlubin
Copy link
Member Author

mlubin commented Jan 10, 2017

@blegat, it's safe to put loaddefaultsolvers() at the top of a script where you import other packages, for example. What you can't do is

function foo()
    loaddefaultsolvers()
    linprog(...)
end

@odow
Copy link
Member

odow commented Jan 10, 2017

Can't the error messages just be left to JuMP? Then you can do something with ProblemTraits.

I don't think MathProgBase is the place to be doing magical loading of solvers or telling users what they should install. Make the solver a required argument and pass the buck up to JuMP and Convex (who can probably give more informative error messages aimed at their different users anyway).

Or you could do something like

# In MathProgBase
function recommendsolver(solvertype, solvers)
    suggestions = join(["$(solvername)(), from the package $(pkgname).jl" for (pkgname,solvername) in solvers], '\n')
    error("No ",$solvertype, " solver chosen. Try using one of the following solvers:\n\n", suggestions, "\n\nIf the package is not installed, you will need to install it and restart Julia.")
end
recommendLPsolvers() = recommendsolver("Linear", MathProgBase.LPsolvers)
# ... and so on ...

# in JuMP
function recommendsolver(::JuMP.UnsetSolver, traits::ProblemTraits)
    if traits.nlp
        MathProgBase.recommendNLPsolvers()
    elseif traits.int || traits.sos
       MathProgBase.recommendMIPsolvers()
    elseif traits.sdp
        MathProgBase.recommendSDPsolvers()
    elseif traits.conic
        MathProgBase.recommendConicsolvers()
    elseif traits.qp || traits.qc
        MathProgBase.recommendQPsolvers()
    else
        MathProgBase.recommendLPsolvers()
    end
end
recommendsolver(solver, traits::ProblemTraits) = nothing

@mlubin
Copy link
Member Author

mlubin commented Jan 10, 2017

@odow, another use case is for packages that embed optimization. Their users may not know much about which solvers to choose, and it would nice to have a way to just call out to a MIP solver if one is needed. Then again you could argue that those packages should be responsible for dealing with the whole situation in a way that makes sense in their specific context.

CC @ccoffrin

@odow
Copy link
Member

odow commented Jan 10, 2017

If a package is embedding optimisation that requires a MIP solver and doesn't expect the user to know what one is, it should have a dependency on Cbc (for example). It might choose to allow the user to specify a different one if they so desired.

It should be the choice of the package developer to make the solver appear magically, rather than at a MathProgBase level.

@blegat
Copy link
Member

blegat commented Jan 10, 2017

@odow I am not sure that packages embedding optimizations should have a dependency on a specific solver. I think that one role of MBP is to make other package independent of any solver to reduce their maintainance cost. If one day the solver everybody used to use is not so fast anymore and a new one is preferred as a default, it would be better if this changes only needs to be done in MPB. Then if one package wants to sort them differently it can do something else. Also, since the default mechanism is not easy to implement, it is better if it is implemented only once.

Requiring the user to give the solver everytime seems reasonable but yet it would be better if there was some automatic choosing by default.

@mlubin
Copy link
Member Author

mlubin commented Jan 10, 2017

@odow, I'm hesitant to remove the ability to call solve on a JuMP model without specifying a solver because it's just too convenient to be able to show off and experiment with in the REPL. What we could do is print a message inside of JuMP saying something like:

Using ClpSolver(...) by default to solve this model.
To specify another solver or specific options, use the "solver" keyword to "Model()" or the "setsolver" method.

@mlubin
Copy link
Member Author

mlubin commented Jan 18, 2017

So the plan as of now would be to remove the default solver functionality from MathProgBase and move it into JuMP, with an explicit warning printed whenever a default solver is used. Any objections?

@joehuchette @chkwon @davidanthoff

@mlubin
Copy link
Member Author

mlubin commented Jan 26, 2017

Discussion continues in #151.

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