Open blakecole opened 1 year ago
great ticket - thanks for the report. This one could be good for new contributors. And the provided MWE looks like a great unit test
Hey @blakecole thanks for putting this together!
A couple observations:
Tight
specifically. Replacing Tight
with ConstraintSet
(the parent class of Tight
) yields the same error.
- constraints += Tight([m_1 * d_1 >= m_2 * d_2 + m_3 * d_3])
+ constraints += ConstraintSet([m_1 * d_1 >= m_2 * d_2 + m_3 * d_3])
toggleTight
is that the non-tight-wrapped constraint is in a list. Putting the Tight
constraint set (or just the ConstraintSet
) in a list actually also solves the error too, though I'm not sure why yet..
- constraints += Tight([m_1 * d_1 >= m_2 * d_2 + m_3 * d_3])
+ constraints += [Tight([m_1 * d_1 >= m_2 * d_2 + m_3 * d_3])]
Regardless of the above, this definitely feels like a bug or at the very least confusing behaviour for the user.
I hope you don't mind, I pared down/tweaked the MWE a little:
from gpkit import Variable, Model
from gpkit.constraints.set import ConstraintSet
option = 1
m_1 = Variable("m_1", "kg", "mass 1")
m_2 = Variable("m_2", "kg", "mass 2")
m_3 = Variable("m_3", 0.6, "kg", "mass 3")
m_a = Variable("m_a", 0.138, "kg", "mass a")
constraints = [m_2 >= m_a]
if option == 1: # does not work
constraints += ConstraintSet([m_1 >= m_3])
elif option == 2: # works
constraints += [ConstraintSet([m_1 >= m_3])]
elif option == 3: # works
constraints += [m_1 >= m_3]
costfn = m_1 + m_2
m = Model(costfn, constraints)
tsol = m.solve()
Btw @whoburg and @bqpd, Blake is a former Hyperloop One colleague of mine, currently finishing a PhD at MIT doing some awesome work optimizing saildrone design using GP (he came across gpkit independently). The ecosystem of gpkit applications continues to grow!
Another observation: option 1 only breaks if m_3
is not used elsewhere (in the main constraints list).
@pgkirsch the pared down MWE looks great to me -- I prioritized speed over minimalism! Thanks for that near-term fix, good to know that putting the tight constraint in a list solves the problem.
So I guess maybe Tight is intended to bracket only the constraint expression itself, and then the entire thing goes in a list? I.e:
constraint += [Tight(inequality expression)]
I suppose that would sort of make sense, now that I think about it. That would enable one to build a single list of comma separated constraints, while specifying only some of them as Tight.
Ahh great observation by @pgkirsch. Yeah now looking back at the original MWE I think that fully explains it. Propose closing this (as a Python gotcha) unless anyone can think of a specific improvement to make or test to add.
Late to the party, but welcome @blakecole!
All ConstraintSets take as their first argument lists or dicts whose values are list-like things, and they're generally used recursively: ContraintSets of ConstraintSets etc.
...but since they inherit from lists, they unfortunately permit addition to them despite needing some data that gets destroyed in that addition (particularly fixed-variable values). Oops. They probably shouldn't let you do that; we can keep this issue open until they return NotImplemented on addition and multiplication.
helpfully, the given example would work if m_3 had been declared with
m_3 = Variable("m_3", 0.6, "kg", "mass 3", constant=True)
but @pgkirsch's solution is much preferred.
Good to know -- thanks all, for the insights! Please let me know when / if you'd like me to close out this ticket.
Big fan of @bqpd's suggestion of ConstraintSet
returning NotImplemented
on addition and multiplication.
@bqpd @pgkirsch
The documentation on Tight ConstraintSets states the following:
This seems to suggest that the use of tight constraints (via the Tight() function) will not alter or otherwise interrupt a GPKit solution; however, I have recently discovered that this is not always the case.
While attempting to solve a model, I noticed that GPKit would throw an error only when a particular constraint had been specified as Tight(). Without the use of the Tight() function, GPKit was able to find a solution to the model.
The nature of the error message was somewhat befuddling: it appeared that when the constraint was specified as Tight, the program was no longer able to interpret one or more constant variables. The error read:
Again, when the Tight() function is removed from the constraint, the model is able to find a solution without any issue.
I have attached a minimum working example for your review. The boolean variable toggleTight can be set to True to incite the issue.
Thanks in advance, -Blake tight_error.py.zip