jump-dev / CPLEX.jl

A Julia interface to the CPLEX solver
https://www.ibm.com/products/ilog-cplex-optimization-studio
MIT License
134 stars 63 forks source link

Installing CPLEX.jl without cplex, as a placeholder / "empty wrapper" #406

Closed cfe316 closed 1 year ago

cfe316 commented 2 years ago

I'm helping to develop a package (https://github.com/genXProject/genX) that can use CPLEX (through JuMP), and can also use other solvers. Not all users will have access to commercial solvers (and neither does our test server).

The Gurobi.jl package optionally looks for an environment variable GUROBI_JL_SKIP_LIB_CHECK which allows it to be installed (but not used of course) on systems without Gurobi.

Would it be possible to have a similar interface for CPLEX.jl? Otherwise, is there a recommended workaround? We're admittedly somewhat new to julia package management.

odow commented 2 years ago

There's no work-around, but I guess there's precedence with Gurobi so we could add CPLEX_JL_SKIP_LIB_CHECK.

I'm usually pretty skeptical of packages which try to install CPLEX/Gurobi if the user doesn't have a license.

The correct work-around is to setup your project so that the user can pass in the solver they want to use, and to use an open-source solver like HiGHS as the default.

odow commented 2 years ago

Related:

I don't think configuring solvers like this is a good idea: https://github.com/GenXProject/GenX/blob/main/src/configure_solver/configure_cplex.jl

It means that your project installs a whole bunch of solvers that aren't actually used: https://github.com/GenXProject/GenX/blob/66cc78a4133a947af8a001d6fb923aeb193e83cc/Project.toml#L9-L10

Just provide one good default (HiGHS), and document how you can use something else.

cc @sambuddhac

davide-f commented 2 years ago

I feel that this feature is though very interesting to enable features that are not common with other solvers. In a repo, I am working on a more efficient procedure that exploits benders decomposition.

Currently, I am trying to use as a workaround a try-catch approach to enable CPLEX only when it is installed in the environment, without adding it as a dependency in the toml.

cfe316 commented 2 years ago

Davide, does your approach work? Would you be able to share the code?

davide-f commented 2 years ago

Davide, does your approach work? Would you be able to share the code?

I'll release the complete package soon. However, the principle is as follows:

I tested this in an environment where CPLEX was installed, yet the and it actually works; however a warning by Julia is usually prompted to notify that the package does not have CPLEX as a dependency. That is annoying but it works

sambuddhac commented 2 years ago

Thanks @cfe316 , @cfe316 , and @odow .... looking forward to the release and meanwhile, trying this approach out for GenX

davide-f commented 2 years ago

The code exactly is the follows;

try

    """
        Add notations for CPLEX backend
    """
    function add_notations!(model, ::Type{CPLEX.Optimizer})

        variable_classification = get_annotations(model)

        num_variables = sum(length(it) for it in values(variable_classification))
        if num_variables != JuMP.num_variables(model)
            @warn "Annotation for $num_variables out of the total $(JuMP.num_variables(model)) variables"
        end
        indices, annotations = CPLEX.CPXINT[], CPLEX.CPXLONG[]
        for (key, value) in variable_classification
            indices_value = map(x->CPLEX.CPXINT(x.index.value-1), value)
            append!(indices, indices_value)
            append!(annotations, fill(CPLEX.CPXLONG(CPLEX.CPX_BENDERS_MASTERVALUE + key), length(indices_value)))
        end
        cplex = JuMP.backend(model)
        index_p = Ref{CPLEX.CPXINT}()
        CPLEX.CPXnewlongannotation(
            cplex.env,
            cplex.lp,
            CPLEX.CPX_BENDERS_ANNOTATION,
            CPLEX.CPX_BENDERS_MASTERVALUE,
        )
        CPLEX.CPXgetlongannotationindex(
            cplex.env,
            cplex.lp,
            CPLEX.CPX_BENDERS_ANNOTATION,
            index_p,
        )
        CPLEX.CPXsetlongannotations(
            cplex.env,
            cplex.lp,
            index_p[],
            CPLEX.CPX_ANNOTATIONOBJ_COL,
            length(indices),
            indices,
            annotations,
        )
        return
    end
catch e
    @warn "Notation by CPLEX are not enabled"
end

You just need to adapt the get_annotations(model) function to use the code

odow commented 1 year ago

Julia 1.9 has an upcoming "weakdeps" feature that should support functionality like this. It'll allow you to write code that gets loaded only if two packages are installed.

Closing because we won't be enabling CPLEX.jl to be installed without a working version of CPLEX.