-
Notifications
You must be signed in to change notification settings - Fork 8
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
PolyJuMP makes a feasible model infeasible #111
Comments
So I'm guessing the problem is related to julia> using JuMP, PolyJuMP, Gurobi, Ipopt
# Solutions:
julia> c_opt = [0.91165750302348, 0.9436635072926446, 0.575952449978958, 0.624599296124994, 0.9896625334970969]
5-element Vector{Float64}:
0.91165750302348
0.9436635072926446
0.575952449978958
0.624599296124994
0.9896625334970969
julia> s_opt = [0.4109508452126525, 0.3309066106987665, 0.8174831957681062, 0.7809454009597355, -0.14341572365716249]
# model = Model(() -> PolyJuMP.QCQP.Optimizer(Gurobi.Optimizer()))
5-element Vector{Float64}:
0.4109508452126525
0.3309066106987665
0.8174831957681062
0.7809454009597355
-0.14341572365716249
julia> model = Model(Ipopt.Optimizer)
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: EMPTY_OPTIMIZER
Solver name: Ipopt
julia> @variable(model, -1 <= c[i = 1:5] <= 1, start = c_opt[i])
5-element Vector{VariableRef}:
c[1]
c[2]
c[3]
c[4]
c[5]
julia> @variable(model, -1 <= s[i = 1:5] <= 1, start = s_opt[i])
5-element Vector{VariableRef}:
s[1]
s[2]
s[3]
s[4]
s[5]
julia> @constraints(model, begin
c.^2 .+ s.^2 .== 1
(((c[1]*c[2] - 0.4608811486294164c[3]*s[4] - 0.5700328131603017c[5]*s[3] - 0.6801846504873382s[3]*s[5]) + (0.5700328131603017* ((c[3]*c[4]) * s[5]))) + (-0.6801846504873382* ((c[3]*c[4]) * c[5]))) == 0
(((-0.7382491921224805c[3]*s[4] - 0.17909626144720603c[5]*s[3] + 0.6503173528871414s[3]*s[5] + s[2]) + (0.17909626144720603* ((c[3]*c[4]) * s[5]))) + (0.6503173528871414* ((c[3]*c[4]) * c[5]))) == 0
(((0.5700328131603017c[3]*c[5] + 0.6801846504873382c[3]*s[5] - 0.4608811486294164s[3]*s[4] + s[1]) + (0.5700328131603017* ((c[4]*s[3]) * s[5]))) + (-0.6801846504873382* ((c[4]*c[5]) * s[3]))) == 0
(((0.8018647772886565c[3]*c[5] - 0.3382841731078751c[3]*s[5] + 0.49252075810927465s[3]*s[4] - c[1]) + (0.8018647772886565* ((c[4]*s[3]) * s[5]))) + (0.3382841731078751* ((c[4]*c[5]) * s[3]))) == 0
(((0.17909626144720603c[3]*c[5] - 0.6503173528871414c[3]*s[5] - 0.7382491921224805s[3]*s[4]) + (0.17909626144720603* ((c[4]*s[3]) * s[5]))) + (0.6503173528871414* ((c[4]*c[5]) * s[3]))) == 0
0.6503173528871414c[5]*s[4] + 0.17909626144720603s[4]*s[5] - c[2] + 0.7382491921224805c[4] == 0
end);
julia> optimize!(model)
This is Ipopt version 3.14.14, running with linear solver MUMPS 5.6.2.
Number of nonzeros in equality constraint Jacobian...: 51
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 85
Exception of type: TOO_FEW_DOF in file "Interfaces/IpIpoptApplication.cpp" at line 662:
Exception message: status != TOO_FEW_DEGREES_OF_FREEDOM evaluated false: Too few degrees of freedom (rethrown)!
EXIT: Problem has too few degrees of freedom. This seems like a bug in the formulation that we are passing to Gurobi. |
I would guess so. Hmm, for this smaller problem it is possible that the feasible set is zero- Here is the reduced bug, as promised. Removing the indicated parentheses makes the problem feasible. using JuMP, PolyJuMP, Gurobi
m = Model(() -> PolyJuMP.QCQP.Optimizer(Gurobi.Optimizer()))
@variable m -1 <= c[3:5] <= 1
@variable m -1 <= s[3:5] <= 1
@constraint m s[4] == 0.7809454009597355
@constraint m 0.6796850426502927 - 0.4608811486294164 * s[3] * s[4] - 0.6731532644471365 * c[4] * s[3] == - 0.5700328131603017 * c[4] * s[3] * s[5]
@constraint m (-0.28365359534933055 - 0.1948355982905866 * s[5] + 0.49252075810927465 * s[3] * s[4]) + 0.8018647772886565 * c[4] * s[3] * s[5] == 0
# ^ ^
# Remove parentheses to make feasible
optimize!(m) |
Removing the parentheses is a very suspicious sign! Is it fixed by #112?
|
I believe I tried it with the latest version directly from github main. I do not believe it is fixed. |
Hmm... It looks as if the model is substituting the wrong variables. If I model this: m = Model(() -> PolyJuMP.QCQP.Optimizer(Gurobi.Optimizer()))
@variable m -1 <= v[1:4] <= 1
@constraint(m, v[3] == 0.780)
@constraint(m, 0.67 - 0.673*v[1]*v[2] + (0.57*v[1]*v[2]*v[4]) - 0.46*v[2]*v[3] == 0)
@constraint(m, -0.283 - 0.194*v[4] + 0.801*v[1]*v[2]*v[4] + 0.492*v[2]*v[3] == 0)
optimize!(m) and inspect it with
to
Based on the first constraint, the variable I can't for the life of me figure out where the bug is. The code is a bit too complex for me. |
BTW, there is a |
Let me take a look. |
Here's a slightly simpler example: julia> using JuMP, Gurobi, PolyJuMP
julia> function main()
model = Model() do
grb = Gurobi.Optimizer()
MOI.set(grb, MOI.Silent(), true)
PolyJuMP.QCQP.Optimizer(grb)
end
@variable(model, v[i = 1:3] >= i)
@constraint(model, 12*v[1]*v[2] + (123*v[1]*v[2]*v[3]) == 1)
@constraint(model, 3*v[3] + 123*v[1]*v[2]*v[3] == 2)
optimize!(model)
print(unsafe_backend(model).model)
end
main (generic function with 1 method)
julia> main()
Feasibility
Subject to:
VariableIndex-in-GreaterThan{Float64}
v[1] >= 1.0
v[2] >= 2.0
v[3] >= 3.0
v[4] >= 3.0
ScalarQuadraticFunction{Float64}-in-EqualTo{Float64}
0.0 + 1.0 v[4] - 1.0 v[1]*v[3] == 0.0
0.0 + 12.0 v[4] + 123.0 v[2]*v[4] == 1.0
0.0 + 3.0 v[2] + 123.0 v[2]*v[4] == 2.0 The affine terms in the bottom two constraints are wrong. It should be 12 * v[1] * v[2] |
The right-hand-side seems un-ordered, that could cause issues. I'm not sure why it's done this way. It seems that you just want the dictionary both ways: from MP to JuMP and from JuMP to MP. If I just pass the
But I don't understand the code yet, so who knows... If it's just that, the functions could be simplified a bit. |
Want to make a PR? |
p.s., if you hadn't seen, I've been using one of your questions in talks: https://youtu.be/6q76umkG-34?si=KA-AzP8u_MbHF15u&t=215 😆 |
Yeah, sure, I'll open a PR. Ooh, I feel honored :D |
Hmm, this might take a while. It looks like there aren't all that many tests for the QCQP part. There are a few in a file called The error was in the Also, the typing, seesh... starting with one(T), explicitly multiplying by floats, then converting back to T? I'm sure there is a reason, but can't see it now. |
Actually, there is a comment
the issue is closed. |
While I'm at it, was this meant to check for divisibility, then use |
Yes, if we can avoid rational, it's indeed best. Otherwise there might be ways to handle rational polynomials but we can wait to have a use case before complicating the code for that |
When using PolyJuMP, the following model is reported as infeasible.
but the commented lines for
c
ands
contain the solution. If you un-comment them, the model becomes feasible. (You can convince yourself otherwise. The infeasibility of roughly 1e-16 is due to rounding)Sorry this is the most I could clean it up for today, I'll try harder tomorrow.
The text was updated successfully, but these errors were encountered: