Skip to content
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

Add variable constraint watcher bridges #1097

Closed
wants to merge 1 commit into from

Conversation

blegat
Copy link
Member

@blegat blegat commented May 31, 2020

The key feature of bridges is that they are applied to each constraint independently.
This allows to defined numerous different model transformations just by defining a few bridges.
However, there are a few exceptions, that we have ignored from now:

  1. Quadratic constraints x^2 + y^2 <= z^2 can be bridged to [z, x, y] in SOC if z >= 0. So the transformations depends on a bound on y.
  2. Complementarity constraints depends on the bounds on the variable.

So in the bridge, we would need to check what are the bounds on the variables (and assume the bound was set first), this is currently possible.
However, if the bounds on the variable are changed, the bridge should be notified as it may need to modify the constraints it added or it may throw an error.
In example 1) above, if the nonnegativity constraint is removed on z, the bridge should throw an error.
To allow this, this PR adds a mechanism for bridge to inform that they want to be notified whenever a SingleVariable constraint is added on a variable.

Requires #1095

@dourouc05
Copy link
Contributor

What about the case where the bridge should first raise an error (because of a missing bound, for instance), but can still bridge the constraint properly later on (once the bound is added)? Also, I think there should be two types of errors: those that can be recovered later on (adding bounds: the error should only be thrown when the model is completely built, just before solving it, if no corrective action was taken) and that are not recoverable (the function-set does not make sense: a power cone with a negative exponent).

@blegat
Copy link
Member Author

blegat commented Jul 29, 2021

For future reference: @dourouc05 just highlighted use cases for this in his JuMP-dev 2021 talk:
https://youtu.be/B0lO6HdlFAw?t=972

What about the case where the bridge should first raise an error (because of a missing bound, for instance), but can still bridge the constraint properly later on (once the bound is added)? Also, I think there should be two types of errors: those that can be recovered later on (adding bounds: the error should only be thrown when the model is completely built, just before solving it, if no corrective action was taken) and that are not recoverable (the function-set does not make sense: a power cone with a negative exponent).

Yes, we would need something like that. The bridge constructor could for instance return an error (instead of an instance of the bridge for the normal execution) and the bridge optimizer would understand that it should retry later if a variable bound has changed and if no variable bound changed, it should just throw the error.

@dourouc05
Copy link
Contributor

@odow Would it be possible to consider inclusion of this feature in MOI 0.10? It would really be useful for CP, but handling errors in this case will be tricky, and I'm not sure that dealing with these errors would be doable within MOI 1.x.

This is my expected workflow from the point of view of a bridge, when adding an unsupported constraint:

  1. Enter the bridge_constraint(bridge, model, func, set) function.
  2. Check if the required constraints are already present in the model. Mostly, that means: SingleVariable-in-{Integer, ZeroOne, LessThan, GreaterThan, Interval}. Return each and every of these variables as watched variables (in case the required constraint is deleted and the bridge no more applies).
  3. If so, build the bridge, end of the workflow.
  4. Otherwise, return an empty AbstractBridge subtype for now.
  5. MOI must have a way to decide that this empty bridge is an error: the user cannot solve the model as-is!
  6. Later on, the user adds the required constraint: notify_bridge(bridge, model, func, set) is called. The bridge performs the same checks as before (even maybe repeating some of them that passed previously).
  7. The same decisions as before are taken: either build the bridge or return a problematic AbstractBridge.

With the current design, you cannot @assert or error within the bridge, as everything would collapse. You should still do this when the pair function-set does not make any sense (scalar function in vector set, other dimension inconsistency, etc.)

What about having a new MOI.Bridges.is_bridge_fully_built? It would return true for most cases, except if the bridge could not be built to completion due to missing bound or similar. You would check that this condition is satisfied for each and every bridge before allowing the user to solve the model.


From a performance point of view, it's probably suboptimum to check every now and then if the constraints are present. You could think of registering the needs of the bridge: in the AbstractBridge subtype, you could store a list of "problems to be solved" and of "conditions already met" and expose them to MOI so that the bridge is called as little as possible. However, this kind of issue can really be dealt with later on.

However, it might be more problematic to go through the list of bridges to detect if everything was fine if there are too many bridges. Maybe have two types of bridges, those that can fail and those that cannot?

@odow
Copy link
Member

odow commented Aug 12, 2021

This should be implementable in MOI 1.x if we only add the ability for bridges to watch other bridgesconstraints. I don't see the need to rush it before MOI 0.10.

@odow odow added this to the MOI 1.x milestone Aug 12, 2021
@dourouc05
Copy link
Contributor

dourouc05 commented Aug 12, 2021

What do you mean by "watching other bridges"? That's not yet proposed :). However, having a way to deal with errors will be required for the CP bridges to work correctly. I'm totally fine if this is not included in MOI 1.0, but not if we have to wait for 2.0.

@odow
Copy link
Member

odow commented Aug 12, 2021

watching other bridges

I misspoke.

However, having a way to deal with errors will be required for the CP bridges to work correctly.

I'm not convinced that this is the right way to go. If you need multiple constraints together, we may be better off writing model transformers, as opposed to constraint-by-constraint transforms.

In any case, we should be able to make it so that any additions are additions, and bridges that work today remain working.

@dourouc05
Copy link
Contributor

I'm not convinced that this is the right way to go. If you need multiple constraints together, we may be better off writing model transformers, as opposed to constraint-by-constraint transforms.

In any case, we should be able to make it so that any additions are additions, and bridges that work today remain working.

The problem is that you sometimes need the input of the user: to get a useful upper bound on a variable, either the user gives it or you presolve the problem to get it. Similarly, if you use a continuous variable to activate an indicator constraint, it's highly likely that it's an error (or you post a constraint later on). It's really not about having bridges that work on several constraints at once (as a presolver could do).

@blegat
Copy link
Member Author

blegat commented Aug 13, 2021

I don't think that implementing this would be breaking so it could even be MOI v0.10.x or v1.0.x, ...
The bridges that don't need this feature won't have to change anything.
We just want bridges that need this feature to either be able to return different things than a bridge instance or to implement some MOI.Bridges.is_bridge_fully_built. In either design, this is not breaking.

@dourouc05
Copy link
Contributor

If you are confident that there will be no problems in landing this feature in MOI 1.x, then I'm sold!

@odow
Copy link
Member

odow commented Nov 18, 2021

Should we close this as stale, and in favor of #1665?

I'm wary of introducing something like this for a very small edge case, so I think we should release MOI 1.0 and then wait a while to see if it ever comes up as an issue.

@blegat
Copy link
Member Author

blegat commented Nov 19, 2021

I still think this PR is the right way to do this so I'd rather keep it open to not delete the branch accidentally and lose the work.

@dourouc05
Copy link
Contributor

@odow, this is already an issue for many of the bridges for constraint programming.

@odow
Copy link
Member

odow commented Nov 24, 2021

I guess I'm not convinced that this is a better approach than a holistic model transformer that can see every constraint. In many cases, it seems that's really what is asked for.

@blegat
Copy link
Member Author

blegat commented Nov 24, 2021

holistic model transformer that can see every constraint

How would you do write these transformations to avoid duplicate code between each model transformer ? In these cases, it's almost independent of other constraints so we can still implement these transformation quite simply with bridges and you only need to handle the complicated bookkeeping once in the bridge optimizer.

@odow odow added the Submodule: Bridges About the Bridges submodule label Jun 1, 2022
@blegat blegat force-pushed the bl/variable_constraint_watcher branch from b1bab26 to 1e1bad5 Compare June 14, 2022 07:09
@odow odow closed this in #1901 Jun 28, 2022
@odow odow deleted the bl/variable_constraint_watcher branch June 29, 2022 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Submodule: Bridges About the Bridges submodule
Development

Successfully merging this pull request may close these issues.

3 participants