Closed pulsipher closed 11 months ago
I think we should force this on the @disjunction call. It wasn't there before because we couldn't distinguish Nested from non nested disjunctions. Now that we can, we can add the appropriate Exactly constraint.
Ok, so you would prefer something like @disjunction(model, name_expr, lvars, exactly1 = true)
(i.e., the default is true)?
I think we should add it and not give the user the option of not adding it.
One thing to consider is that under the new formal definition of GDP it seems like we are decoupling the exactly one constraint from the disjunction, so there might be cases where an exactly one constraint is not given. So, would it not be better to give the user an option to not add it and then have reformulations error that rely on this assumption?
@bernalde, do you have any thoughts on this?
This recent paper always includes the exactly constraint as part of the extended GDP notation: https://arxiv.org/pdf/2303.04375.pdf.
Right and this paper also explicitly uses an exactly one constraint: https://www.sciencedirect.com/science/article/pii/S009813542300217X. The question is whether GDP requires there to always be an exactly one constraint. For instance, would it be possible to enforce exactly 2 out of 3 disjuncts?
Not unless there were some valid reformulation, which is currently not the case.
Conceptually, you need to select exactly one disjunct region (even if they are not proper (have some overlap)).
The "exactly one" is an explicit way of writing an exclusive or for more than 2 elements. In GDP, it was usually written this exclusive or which is not quite well defined. In disjunctive programming, you do not enforce the "only one" condition; it is a disjunction in its true case (at least one), i.e., sum_i y_i <= 1. A way of setting things straight is by using normal disjunctions and then enforcing the "exactly one" as part of the logical constraints. Practically, this would require the flexibility that Josh wants to put in the code of a flag deciding whether you want \sum_i y_i =1 or <=1. This is the way that it is implemented in Pyomo.GDP.
Thanks for the input @bernalde.
After talking with @hdavid16 offline, the game plan is as follows:
exactly1 = true
keyword argument to disjunction
(then @disjunction
inherits it)Disjunction
to store whether an exactly 1 constraint was added automaticallyexactly1= false
unless this is overridden via a keyword argument, thus, users have a way to manually add exactly 1 constraints if wanted, but will not accidently use reformulation techniques that depend on this assumption.This will enable users to specify things like Exactly(2)
or AtLeast(1)
that are valid for reformulations like big-M, but not Hull.
Since 99% of the time this is the case, automating it would be nice. I think we should make it opt-in (i.e., the default would be
false
).One other thing to consider is whether the reformulations are still valid if this constraint is not given.