# Identifying the systems available linear solvers (With JuMP & Ipopt)

**URL:** https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675
**Category:** Optimization (Mathematical)
**Tags:** jump
**Created:** [November 29, 2019, 9:47pm UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675 "2019-11-29T21:47:09Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![Libbum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/libbum/32/3935_2.png) [@Libbum](https://discourse.julialang.org/u/Libbum)
#### Post date: [November 29, 2019, 9:47pm UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675/1 "2019-11-29T21:47:10Z")

</div>

Hi all,

I’d like to be able to verify / list available linear solvers on any given machine before running a model. My stack is `JuMP` and `Ipopt`, which means I can do something like this:

```julia
import Ipopt; 
import JuMP; 
optimizer = JuMP.with_optimizer(Ipopt.Optimizer, linear_solver="ma97"); 
model = JuMP.Model(optimizer)

```

Here, `optimizer` is of type `JuMP.OptimizerFactory`, and seems to be lazily evaluated since at this point in time `model` has no issues even if one types garbage into the `linear_solver` string.  
From my understanding, JuMP sends this factory info to Ipopt.jl, which uses MOI to then do a bunch of `ccall`s to run everything once `JuMP.optimize!(model)` is called, and not before.

By default Ipopt.jl packages the MUMPS linear solver, although there are more efficient ones out there: HSL MA97 as an example (as set in `optimizer` above).

In my specific case, I want to be able to prioritize certain linear solvers over others, depending on solvers available on a users system. By default, Ipopt will use MA27 if it’s compiled with HSL, but I’m needing MA97 if available and would fall back to MUMPS if not.

The only ways I can find to check if MA97 is extant at the moment is to run `JuMP.optimize!(model)` and wait for it to fail via the MOI `ccall` attempt via [`addOption`](https://github.com/JuliaOpt/Ipopt.jl/blob/085b0177de3042c4a9b965e6f70cd61c2b5c22a5/src/Ipopt.jl#L247-L259) at the JuMP level.

Which ultimately could be simplified to invoking something at the Ipopt.jl level by building some dummy `IpoptProblem` and capture the error:

```julia
prob = Ipopt.createProblem(1,[1.],[1.],1,[1.],[1.],1,1,sum,sum,sum,sum);
try 
    Ipopt.addOption(prob, "linear_solver", "ma97")
    println("HSL installed")
catch 
    println("Use MUMPS")
end

```

Is there a cleaner way to do this at all that I’ve just overlooked? This of course gets pretty messy if I want a list of installed solvers rather than just one check and a fallback.

* * *

**Update:** as a followup - I’ve realized this solution isn’t even enough. The `addOption` call will pass so long as it is possible to be accepted in Ipopt’s settings here. For example, I don’t have “pardiso” installed, but I won’t receive an error at this point if I try to test with it. Thus I need to actually call `Ipopt.solveProblem(prob)` as well and capture on that output instead.

---

<div class="post-metadata">

### Author: ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)
#### Post date: [November 30, 2019, 6:23pm UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675/2 "2019-11-30T18:23:58Z")

</div>

I’m not sure there is a good solution. Trying to solve the dummy model via the low-level interface is probably the easiest way forward.

---

<div class="post-metadata">

### Author: ![Libbum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/libbum/32/3935_2.png) [@Libbum](https://discourse.julialang.org/u/Libbum)
#### Post date: [December 1, 2019, 12:03pm UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675/3 "2019-12-01T12:03:11Z")

</div>

Thanks @odow. Just wanted to make sure I wasn’t doing something overtly complicated for no reason.

For completeness, here’s my final solution to this issue.

```nohighlight
function linearSolver(solver_name::String = "ma97")
    prob = Ipopt.createProblem(1,[1.],[1.],1,[1.],[1.],1,1,sum,sum,sum,sum);
    Ipopt.addOption(prob, "sb", "yes");
    Ipopt.addOption(prob, "print_level", 0);
    # Initially, we must check that coinhsl is installed at all if we want a HSL solver.
    # If not, Ipopt will default to try and find any dynamically linked libhsl. If it
    # cannot find one it will hard panic and we can't capture that failure.
    if occursin("ma", solver_name)
        Ipopt.addOption(prob, "linear_solver", "ma27");
        try
            # No HSL was found on the system, we return with fallback.
            return runLinearSolverCheck(prob, solver_name)
        catch
            #Continue, we have access to at least some version of HSL
        end
    end
    try
        # Outer try will fail if solver string is not in the list of
        # possible Ipopt solvers.
        # For now that's ma27, ma57, ma77, ma86, ma97, pardiso, wsmp, mumps, custom
        Ipopt.addOption(prob, "linear_solver", solver_name);
        try
            # Inner try attempts to run the dummy program and will crash because
            # the dummy is malformed.
            # No error code will be returned if the solver is extant, and an Invalid_Option
            # if the library is not found.
            runLinearSolverCheck(prob, solver_name)
        catch
            # We can use the requested solver.
            solver_name
        end
    catch
        "mumps"
    end
end

function runLinearSolverCheck(prob::IpoptProblem, solver_name::String)
    result_code = Ipopt.solveProblem(prob);
    if Ipopt.ApplicationReturnStatus[result_code] == :Invalid_Option
        @info "Unable to set linear_solver = $(solver_name), defaulting to MUMPS."
        return "mumps"
    else
        error("Attempts to identify linear solvers on system returned unexpected results.");
    end
end

```

Linear solvers installed on my system: ma\*, mumps. Missing pardiso, wsmp.

```nohighlight
julia> linearSolver()
"ma97"

julia> linearSolver("ma27")
"ma27"

julia> linearSolver("pardiso")
┌ Warning: Unable to set linear_solver = pardiso, defaulting to MUMPS.
└ @ LS ~/testing/src/ls.jl:56
"mumps"

julia> linearSolver("mumps")
"mumps"

```

---

<div class="post-metadata">

### Author: ![miles.lubin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/miles.lubin/32/279_2.png) [@miles.lubin](https://discourse.julialang.org/u/miles.lubin)
#### Post date: [December 1, 2019, 2:50pm UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675/4 "2019-12-01T14:50:53Z")

</div>

This is a good question for the [Ipopt mailing list](https://list.coin-or.org/mailman/listinfo/ipopt). If Ipopt has an API for this, we can use it. If not, then your solution is the best that one can do.

---

<div class="post-metadata">

### Author: ![pjssilva](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pjssilva/32/4238_2.png) [@pjssilva](https://discourse.julialang.org/u/pjssilva)
#### Post date: [May 23, 2020, 9:48pm UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675/5 "2020-05-23T21:48:37Z")

</div>

Libbum, can I use your code? How it is licensed? I need to do something like this in a code I am writing.

---

<div class="post-metadata">

### Author: ![Libbum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/libbum/32/3935_2.png) [@Libbum](https://discourse.julialang.org/u/Libbum)
#### Post date: [May 24, 2020, 11:12am UTC](https://discourse.julialang.org/t/identifying-the-systems-available-linear-solvers-with-jump-ipopt/31675/6 "2020-05-24T11:12:57Z")

</div>

Yes, certainly! You can find everything I currently use in [`DICE.jl`](https://github.com/Libbum/DICE.jl/blob/59b8c3c4419fab362a0fd29b1d4fe6052e2f5d1f/src/DICE.jl#L41-L93), which is MIT licensed.
