Skip to content

added addindconstr! interface for indicator constraints #155

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

Closed
wants to merge 5 commits into from

Conversation

bbrunaud
Copy link

@bbrunaud bbrunaud commented Feb 8, 2017

Gurobi (since version 7), CPLEX and SCIP support indicator constraints. Which are something like

y = 1  ->   ax <= b

added addindconstr! interface to allow the implementation by CPLEX.jl, Gurobi.jl, SCIP.jl and also the implementation in JuMP. An example of usage with CPLEX would be

function addindconstr!(m::CplexMathProgModel, ind, comp, varidx, coef, lb, ub)

Ref.
http://www.ibm.com/support/knowledgecenter/SSSA5P_12.5.1/ilog.odms.cplex.help/refcallablelibrary/html/functions/CPXaddindconstr.html

@mlubin
Copy link
Member

mlubin commented Feb 8, 2017

Could you add corresponding documentation at doc/lpqcqp.rst?

@bbrunaud
Copy link
Author

bbrunaud commented Feb 9, 2017

In both Gurobi and CPLEX MathProgInterfaces a constraint is defined by (expr, lb, ub). If lb = ub it's an equality constraint. The alternative is to define (expr, sense, rhs), which actually is how the APIs are implemented. For some reason, the MathProgInterfaces use the (expr, lb, ub) and then transform into the other format. For example this is the function from GurobiSolverInterface.jl.

function addconstr!(m::GurobiMathProgModel, varidx, coef, lb, ub)
    if m.last_op_type == :Var
        updatemodel!(m)
        m.last_op_type = :Con
    end
    if lb == -Inf
        # <= constraint
        add_constr!(m.inner, varidx, coef, '<', ub)
    elseif ub == +Inf
        # >= constraint
        add_constr!(m.inner, varidx, coef, '>', lb)
    elseif lb == ub
        # == constraint
        add_constr!(m.inner, varidx, coef, '=', lb)
    else
        # Range constraint
        error("Adding range constraints not supported yet.")
    end
end

I also think it would be simpler to pass (expr, sense, rhs) but I thought there was some kind of convention in MathProgBase

replacing ``ind`` by ``binvar``, and ``comp`` to ``binval``, where ``binval`` is the actual value of the indicator variable. Simpler to understand and aligned with the definition of indicator constraints in Gurobi
@mlubin
Copy link
Member

mlubin commented Feb 9, 2017

The convention in MathProgBase is to preferably have exactly one way to accomplish a task. Given that some solvers like Clp naturally take two-sided constraints in (lb, expr, ub) form which is more general than (expr, sense, rhs), we decided to use the former form for general linear constraints. If there are no solvers we care about that support indicator variables for "two-sided" constraints, then the MathProgBase API should use the (expr, sense, rhs) form. Note that for lazy constraints we also use the sense form.

@bbrunaud
Copy link
Author

Your call... If we go with (expr,sense,rhs) we would also require to modify Cplex.jl add_indicator_constraint function. With the (expr,lb,ub) way this was my attempt for JuMP indicator.jl

type IndicatorConstraint <: AbstractConstraint
    binvar::Variable
    binvalue::Int32
    linconstraint::LinearConstraint
end
Base.copy(ic::IndicatorConstraint, new_model::Model) =
    IndicatorConstraint(copy(ic.binvar, new_model),
                    copy(ic.binval), copy(ic.linconstraint)) 


function addindicatorconstraint!(m::Model, c::IndicatorConstraint)
    push!(m.indconstr,c)
    if m.internalModelLoaded
        if method_exists(MathProgBase.addindconstr!, (typeof(m.internalModel),Int32, Int32, Vector{Int},Vector{Float64},Float64,Float64))
            assert_isfinite(c.linconstraint.terms)
            indices, coeffs = merge_duplicates(Cint, c.linconstraint.terms, m.indexedVector, m)
            MathProgBase.addindconstr!(m.internalModel, c.binvar.col, c.binvalue, indices,coeffs,c.linconstraint.lb,c.linconstraint.ub)
        else
            Base.warn_once("Solver does not appear to support adding constraints to an existing model. JuMP's internal model will be discarded.")
            m.internalModelLoaded = false
        end
    end
    return LinConstrRef(m,length(m.linconstr))
end

I was trying to take advantage of the LinearContraint object. I think I was able to figure out everything but how to construct the @indicatorconstraint macro... so, stick to (expr,sense,rhs) ??

regards

Braulio

@mlubin
Copy link
Member

mlubin commented Feb 14, 2017

JuMP doesn't have trouble putting constraints into (expr,sense,rhs) form: https://github.com/JuliaOpt/JuMP.jl/blob/9bef25fe7b64492fc0343f5bd3737d02a92cef24/src/callbacks.jl#L258. Let's go with that. I'll save discussion on the JuMP implementation for a JuMP issue/PR.

doc/lpqcqp.rst Outdated

Adds a new indicator constraint to the model, of the form
.. math::
y = 1 \Rightarrow ax \leq b

``binvar`` is the indicator variable (binary), and ``binval`` is the value of the binary variable.
The rest of the arguments are the same as addconstr!
``binvar`` is the indicator variable (binary), and ``binval`` is the value of the binary variable. Coefficients for the linear constraint are specified in a sparse format: the ``coef`` vector contains the nonzero coefficients, and the ``varidx`` vector contains the indices of the corresponding variables. ``sense`` is the sense of the linear constraint ``('<','=','>')``, and ``rhs`` is the right hand side.
Copy link
Member

Choose a reason for hiding this comment

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

binvar is the indicator variable (binary)

Is the index of the indicator variable. Is it assumed to be marked binary already?

binval is the value of the binary variable

I don't know what this means.

Copy link
Author

Choose a reason for hiding this comment

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

I played a little bit with CPLEX to get your answers

  1. binvar must be the index of the variable
  2. CPLEX will not complain if the variable is not marked as binary beforehand or will it mark it if declared if used in an indicator constraint. However, using the variable as indicator will make it behave as binary, even if not marked. See the examples below
  3. an indicator constraint has the form binvar = binval => linearconstraint where binval is in {0,1}.

Let me know how do we proceed from here.


A few examples for 2, all of them constructed using JuMP and CPLEX:

Maximize
 obj: 0 x1 + x2
Subject To
 c1: x2 <= 10
 i1: x1 = 1 -> x2 <= 5
Bounds
      x1 = 1
Binaries
 x1 
End

works, obj= 5

Maximize
 obj: 0 x1 + x2
Subject To
 c1: x2 <= 10
 i1: x1 = 1 -> x2 <= 5
Bounds
      x1 = 1
End

CPLEX Error 1017: Not available for mixed-integer problems. Probably caused because an LP cannot have indicators.

Maximize
 obj: 0 x1 + x2 + 0 x3
Subject To
 c1: x2 <= 10
 i1: x1 = 1 -> x2 <= 5
Bounds
      x1 = 1
 0 <= x3 <= 1
Binaries
 x3 
End

works, obj=5. Column types [:Cont, :Cont, :Bin]

Maximize
 obj: 0 x1 + x2 + 0 x3
Subject To
 c1: x2 <= 10
 i1: x1 = 1 -> x2 <= 5
Bounds
      x1 = 0.5
 0 <= x3 <= 1
Binaries
 x3 
End

Infeasible. Even though x1 is not marked as binary it cannot take a value different than {0,1}

Copy link
Member

Choose a reason for hiding this comment

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

Could you clarify these points in the documentation?


``binvar`` is the index of the indicator variable (binary), and ``binval`` is the value of the binary variable, either ``0`` or ``1``. Coefficients for the linear constraint are specified in a sparse format: the ``coef`` vector contains the nonzero coefficients, and the ``varidx`` vector contains the indices of the corresponding variables. ``sense`` is the sense of the linear constraint ``('<','=','>')``, and ``rhs`` is the right hand side.

This builds the constraint ``binvar = binval => ax <= b``, where ``ax <= b`` is specified with ``(varidx,coef,sense,rhs``)
Copy link
Member

Choose a reason for hiding this comment

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

backtick quotes are not matching here

.. math::
y = 1 \Rightarrow ax \leq b

``binvar`` is the index of the indicator variable (binary), and ``binval`` is the value of the binary variable, either ``0`` or ``1``. Coefficients for the linear constraint are specified in a sparse format: the ``coef`` vector contains the nonzero coefficients, and the ``varidx`` vector contains the indices of the corresponding variables. ``sense`` is the sense of the linear constraint ``('<','=','>')``, and ``rhs`` is the right hand side.
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a mention of what the solver should do if binvar isn't already marked as a binary variable? E.g., the indicator constraint also enforces that binvar is binary.

@mlubin
Copy link
Member

mlubin commented Jun 9, 2017

@bbrunaud, there are just a couple more comments to address here. Do you plan on completing this PR and the corresponding implementation in JuMP and solver interfaces?

@mlubin mlubin mentioned this pull request Jun 10, 2017
@bbrunaud
Copy link
Author

Hi Miles

I have almost everything working on my local forks. I'm just missing the JuMP macro. However, based on the amount of work that it's taking to insert 1 line of code into MathProgBase, I'm not sure I have the time to commit to take care of every little detail. Is there a more efficient way to collaborate with the project?. Please, shoot me an email to brunaud at cmu.edu so we can discuss further.

BTW, congrats on your defense

Braulio

@mlubin
Copy link
Member

mlubin commented Jun 19, 2017

This will be made much simpler following #173. It's still a work in progress, but your comments are welcome there. We will also need help updating the solver interfaces for the new API.

BTW, congrats on your defense

Thanks!

@mlubin mlubin closed this Jun 19, 2017
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.

2 participants