-
Notifications
You must be signed in to change notification settings - Fork 40
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
Debugging UNKNOWN #519
Comments
My first proposed solution is very much brute force. It works for the MWE above though, and perhaps could be a starting point for something more sophisticated. It is based on the assumption that a program returns unknown because the problem is "underconstrained" from the perspective of the solver, and that all it needs is for a certain variable to be fixed. This code could help to identify which variable(s) are at the root of the problem. def test_unknown(m):
for key in m.varkeys:
if key not in m.constants and key not in m.substitutions:
m.substitutions.update({key: 1})
print "Solving with fixed", key
try:
m.solve(verbosity=0)
print "Problem solves"
except ValueError:
try:
m.localsolve(verbosity=0)
except RuntimeWarning:
print "Runtime Warning"
del m.substitutions[key] For the MWE above: In [6]: from test_unknown import test_unknown
In [7]: test_unknown(m)
Solving with fixed y
Problem solves
Solving with fixed x
Problem solves |
@whoburg, is this related to / generalizable with your result on one class of UNKNOWN / singular solves? |
Hmm. @pgkirsch, I'm really glad you started this issue, but I'm a bit skeptical on the MWE and proposed solution, for two reasons:
|
Yes, you're right, we have seen this before (specifically in issue #403). I reduced the example in issue #380 to another MWE: from gpkit import Variable, Model
D = Variable('D')
F = Variable('F')
mi = Variable('m_i')
mb = Variable('m_b', 0.4)
V = Variable('V', 300)
m = Model(F,
[F >= D + V,
V >= mi + mb,
])
sol = m.solve() For this problem, the issue isn't that the feasible set is a single point. Glad it looks scary, it consistently ruins my day. Edit by @whoburg: note that cvxopt solves the above problem -- only MOSEK returns UNKNOWN. |
We could lowercase the error messages? :p :p :p The solver for example two does say "D was not lower-bounded" etc. More seriously we could remind the user what was unbounded and suggest that fixing those might fix the "UNKNOWN" |
I actually am surprised we don't remind the user of the unbounded vars on solve failures already. Should I just push that up? |
@whoburg you are also correct about point 2, that the proposed heuristic doesn't work for the case with one feasible point if the value is not exactly right. |
@bqpd For this example you do provide a warning message saying those two variables are unbounded, and again this might not be the greatest MWE, because the fix is quite obvious (and made clear by the message). |
Well, it's not entirely obvious what we would guess as bounds. And sometimes unbounded things are a model goof. But yeah, that's what we'd do for this example. |
A better MWE: from gpkit import Variable, Model
Ap = Variable('A_p')
D = Variable('D')
F = Variable('F')
mi = Variable('m_i')
mf = Variable('m_f')
T = Variable('T')
Fs = Variable('Fs', 0.9)
mb = Variable('m_b', 0.4)
rf = Variable('r_f', 0.01)
V = Variable('V', 300)
m = Model(F,
[F >= D + T,
D == rf*V**2*Ap,
T == mf*V,
mf >= mi + mb,
mf == rf*V,
Fs <= mi,
])
sol = m.solve() The problem here is that GPkit can't tell that Ap is not lower bounded because it is in an equality constraint. If you add a constraint, |
Simplified further: from gpkit import Variable, Model
Ap = Variable('A_p')
D = Variable('D')
F = Variable('F')
mi = Variable('m_i')
Fs = Variable('Fs', 0.9)
mb = Variable('m_b', 0.4)
V = Variable('V', 300)
m = Model(F,
[F >= D + V**2,
D == V**2*Ap,
V >= mi + mb,
Fs <= mi,
])
sol = m.solve() edit by @whoburg: cvxopt solves this too -- mosek returns unknown. |
Yeah. I've been thinking for a while that we could figure this kind of thing out automatically. Any ideas for how to do that? |
Not exactly that, but my crappy proposed heuristic above at least works for this new MWE, regardless of choice of substitution value. |
Otherwise, maybe something like for every equality constraint, you could check with only one of the constituent inequality constraints at a time and see if you learn something? |
And check 2^(number of equality constraints) times? 👻 😨 |
Everything else on the right side has a lower bound, everything on the left side has an upper bound... |
...so |
let's divide each EQConstraint out to |
If |
Now, if |
Given a monomial |
@bqpd, I've been trying to follow your comments above but getting caught up on a few details -- let's discuss in person. Alternatively, and even better, I suggest writing up a short pdf to explain your point. |
This is an important ticket -- the UNKNOWN issue is a big problem. I'd like us to start a working latex document to categorize the types of failures that result in UNKNOWN. Here's a start. Unbounded Variable(s)Typical MWE: a lower-unbounded variable inside a posynomial constraint MWE: from gpkit import Variable, Model
x = Variable('x')
t = Variable('t')
m = Model(t, [t >= 1 + x])
sol = m.solve() On this MWE, cvxopt returns unknown and mosek has an issue #361 error. Note that there are a number of strange tweaks that can cause cvxopt to actually return solutions (with very small values for the unbounded variable). Examples are listed in a comment on issue #460. Mosek, on the other hand, continues raising an issue #361 error. Note this appears to be the failure mode associated with @pgkirsch's MWE above adapted from #380. Nearly dual-infeasible modelsTypical MWE: specific, precise exponents are required on a constraint to achieve dual feasibility MWE: from gpkit import Variable, Model
import numpy as np
CL = Variable("CL")
CD = Variable("CD")
V = Variable("V", "m/s", "cruise speed")
W = Variable("W", 200, "lbf", "weight")
rho = Variable(r"\rho", 1.2, "kg/m^3", "air density")
S = Variable("S", 190, "ft^2", "wing area")
m = Model(V*(W/(CL/CD)),
[0.5*rho*CL*S*V**2 == W,
CL**1.5/CD <= 20])
sol = m.solve("cvxopt") With a little math (to eliminate Note that when the In my opinion there is a big opportunity with this class of problems. In particular, I think we might be able to use some linear algebra tricks to identify problems that are nearly dual infeasible and provide other useful information to modelers. The is still an idea being formed; I'll elaborate in a separate ticket. |
Instead of being clever with math we've recently been just using resolves. |
@bqpd what do you mean by "resolves"? |
re-solving the model with boundedconstraintsets / realaxed models |
Do boundedconstraintsets and relaxed models help with the nearly-dual-infeasible case? p.s. what do you mean by relaxed models? |
Relaxed models are my new name for primal-feasibility models. It may or may not stick. |
I'm wondering if there is a lazy approach to handling the nearly dual infeasible case, for example relaxing the convergence criterion of the solver. |
😨 |
Ha that went over about as well as I was expecting it to...... |
This issue is partly covered by the new |
before we close this based on the existence of |
1st example: does not solve with cvxopt debug :-/ Should be tried again with MOSEK |
@1ozturkbe this is useful |
related: JuliaOpt/MathProgBase.jl#164 |
I have a model that solves perfectly well for a given set of inputs, however, when slightly higher performance is asked of the model (higher speed, heavier payload) it returns unknown, and the solver iterations look completely different (reaches max iterations after plateauing pretty early and making no convergence progress. When I say a slightly higher performance, we're talking about less than 1% increases. Plotting a sweep of this variable, this point typically occurs as the objective starts growing exponentially, so it's understandable why the solver may have issues with it. It's possible that the model is genuinely becoming infeasible, but I am skeptical that this is the case. When I "diff" the solution of the original model with the solver output at the end of max iterations with the higher performance model there are substantial differences, most noticeably in variables I wouldn't expect to care. From what I recall, these solvers typically move through infeasible regions as they optimize the dual function (is that right?) but is there anything meaningful to be gained from seeing how the solution evolves iteration-to-iteration? And does GPkit have an interface that allows this kind of solution stepping? Coming at it from a different angle, are there exposed solver parameters that control how a solver responds to perceived near-dual-infeasibility? Finally, does anyone from the modeling side of things (@mayork, @1ozturkbe, @mjburton11) have any good debugging tips they have discovered for this specific type of unknown problem? |
Eesh. cvxopt doesn't seem to be giving very credible results around this feasibility threshold. As I increment one of the "performance" variables (e.g. speed) in very small steps (0.04% increase) the result changes dramatically (50% increase in objective value). Based on conversation with @bqpd on #1143 it seems that the problem is becoming infeasible. This raises two questions for me: (1) is the dramatically different threshold solution truly globally optimal? (2) why isn't the solver returning primal infeasible instead of unknown once that threshold is crossed? |
This is most likely specific to my particular model, but I have actually had some success by increasing the resolution of discretization used. i.e. More elements in key vector variables take this model from returning unknown to returning a feasible solution. |
@pgkirsch do you mind if I close this thread? I like that it's been a place to bring new weird UNKNOWNS, but it's gotten a bit unwieldy... |
Sure go for it.
On Nov 9, 2017, at 22:07, bqpd <notifications@github.com<mailto:notifications@github.com>> wrote:
.debug now debugs all of the above, if you also check the output and note that certain variables are getting quite small...
@pgkirsch<https://github.com/pgkirsch> do you mind if I close this thread? I like that it's been a place to bring new weird UNKNOWNS, but it's gotten a bit unwieldy...
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<#519 (comment)>, or mute the thread<https://github.com/notifications/unsubscribe-auth/AITKc6IolrWShSjIc7cPdNSei9NSA9e-ks5s0-f6gaJpZM4HaHod>.
|
Excitingly, we can now catch all of @pgkirsch's examples before even solving, by implementing an algorithm similar to the one I describe above... |
Catching @whoburg's marginally feasible examples will probably require the SVD analysis he discussed! |
Cool! If anyone wants to work on that let me know, I'd be excited to discuss the math. |
It would be a huge help if we could figure out a way of debugging programs that return UNKNOWN. Specifically, it would be useful to know what needs to be constrained for the problem to be solved.
A MWE for a problem that returns unknown:
The text was updated successfully, but these errors were encountered: