ConstraintSolver.jl icon indicating copy to clipboard operation
ConstraintSolver.jl copied to clipboard

Support of nonlinear constraints?

Open hakank opened this issue 3 years ago • 4 comments

Are there plan to support general nonlinear constraints (in the future)?

Coming from constraint programming systems such as MiniZinc, Picat, Gecode, OR-tools, etc, I'm quite used to - and spoiled with - nonlinear constraints. But what I can see, there is no support at all in ConstraintSolver.jl for nonlinear constraints which is - I assume - because JuMP don't handle general nonlinear constraint what I know. And it seems that ConstraintSolver.jl don' support quadratic constraints either.

Here's a simple (and contrived) example which yield the following error: MOI.ScalarQuadraticFunction{Float64}-in-'MOI.GreaterThan{Float64}' constraints are not supported and cannot be bridged into supported constrained variables and constraints. See details below: ....

# ....
    @variable(model, 1 <= x[1:n] <= n, Int)
    @variable(model, b1[1:n], Bin)

    for i in 1:n
        @constraint(model, b1[i] := {x[i] <= i})
        @constraint(model, b1[i]*x[i] >= 1)

    end

# ...

And the following constraint yield Cannot multiply a quadratic expression by a variable

   # ...
     @constraint(model, b1[i]*x[i]*x[i] >= 1)
   # ...

I know that many/most nonlinear constraint can be reformulated using a lot of binary variables instead, but I would really prefer to use "traditional" nonlinear constraints, especially when trying to translate (decompose) global constraints.

Here's a more realistic example: modelling the cumulative global constraint (adapted from MiniZinc's cumulative function)

function cumulative(model, start, duration, resource, b)

    tasks = [i for i in 1:length(start) if resource[i] > 0 && duration[i] > 0]
    num_tasks = length(tasks)

    # upper and lower limits of start times
    times_min_a = round.(Int,[JuMP.lower_bound(start[i]) for i in tasks])
    times_min = minimum(times_min_a)
    times_max_a = round.(Int,[JuMP.upper_bound(start[i]) for i in tasks])
    times_max = minimum(times_max_a)

    for t in times_min:times_max + 1
        bs = @variable(model, [1:num_tasks], Bin)
        bt = @variable(model, [1:num_tasks], Bin)
        for i in tasks
            # This don't work: Throws: `no method matching isless(::VariableRef, ::Int64)`
            # Hence the use of indicators.
            # @constraint(model,sum([(start[i] <= t) * (t <= start[i] + duration[i])*resource[i] for i in tasks])  <= b)

            # Using indicators instead:
            @constraint(model, bs[i] := {start[i] <= t})
            @constraint(model, bt[i] := {t <= start[i]})
            # This throws MOI.ScalarQuadraticFunction ... error
            @constraint(model,sum([bs[i] * (bt[i] + duration[i])*resource[i] for i in tasks]) <= b)
        end
  end
end

hakank avatar Dec 11 '20 09:12 hakank

It's currently not planned to do this even though JuMP supports non linear constraints. The problem for the constraint solver is that I don't know how to prune such constraints.

Wikunia avatar Dec 11 '20 09:12 Wikunia

OK. Is it the same with quadratic constraints?

hakank avatar Dec 11 '20 09:12 hakank

Yes whereas there it might be easier to do something I think. I was think I can support them if I have something like: monotonically increasing in all variables but that is quite restrictive. I'll have a look in the literature if I can find something on how to prune constraints like this.

Wikunia avatar Dec 11 '20 10:12 Wikunia

That's great. That would help for at least some of these non-linear constraints.

hakank avatar Dec 11 '20 16:12 hakank