Allow stacking of bridge optimier#3013
Draft
blegat wants to merge 3 commits into
Draft
Conversation
Foundation working (Variable/map.jl + bridge_optimizer.jl):
- Variable.Map has outer_to_inner / inner_to_outer IndexMaps, next_outer_variable counter, per-(F,S) constraint counters
- activate_variable_mapping!(map, model) / activate_constraint_mapping!(map, model, F, S) — lazy triggers, called from
add_constrained_variable(s) when a variable bridge is created
- _record_inner_variable!(b, inner_vi) — translates inner→outer when mapping active, identity otherwise
- MOI.add_variable / MOI.add_variables / MOI.add_constrained_variable(s) updated to translate
Verified manually:
- Basic single-layer flow: bridged variables (xs = [-1, -2, -3]) still use slot/negative scheme internally. Non-bridged passthrough
variables get outer indices via IndexMap when active.
- Two stacked SingleBridgeOptimizers succeed at add_constrained_variables — the outer pulls inner-allocated indices (positive) through
_record_inner_variable! while its own bridges keep negative outer indices.
Test status on test/Bridges/General/test_bridge_optimizer.jl:
- test_CallbackVariablePrimal errors — MOI.get(b, attr, vi) doesn't translate vi outer→inner yet
- test_nesting_SingleBridgeOptimizer still 3 fails — the cnn CI{VOV, Nonneg} collision case, same as before. Will be fixed by the
constraint-mapping plumbing.
- Rest pass.
Big remaining work (rough order):
1. Translate vi in MOI.is_valid, MOI.delete, MOI.get, MOI.set, MOI.modify, MOI.supports and the callback / primal / dual attribute getters
2. Translate functions containing variable indices via MOI.Utilities.map_indices when crossing the boundary (ConstraintFunction,
ObjectiveFunction, VariablePrimal, change types, etc.)
3. Constraint mapping per (F, S) — activate on first VI/VOV force-bridge, translate at boundaries
4. Remove negative-index conventions
Core namespace machinery (src/Bridges/bridge_optimizer.jl):
- outer_to_inner(b, idx) / inner_to_outer(b, idx) — the canonical "if variable bridges are used → use the index map, else identity"
helpers, for both VariableIndex and ConstraintIndex{F,S}. CI{VariableIndex,S} translation is derived from the variable mapping (since
ci.value == vi.value by MOI convention), so it needs no stored entries.
- _OuterToInner / _InnerToOuter / _TotalInnerToOuter functor structs (<: Function so they work with map_indices). The "total" variant
leaves unrecorded indices unchanged — those are bridge-created inner variables later removed by unbridged_function.
- _to_inner_value / _from_inner_value — whole-value translation at the b.model boundary, applied via map_indices.
- _unbridged_result_from_inner / _unbridged_result_from_bridge — result processing that respects where a value came from:
recursive_model(b) === b (Lazy, outer namespace) vs b.model (SBO, inner namespace).
Key insight encoded in bridged_variable_function: for LazyBridgeOptimizer the substituted expressions stay outer (translation happens at
the model boundary); for SingleBridgeOptimizer they're already inner, so passthrough variables translate during substitution and the
recursion is skipped.
Plumbed sites: add_constraint(s), add_constrained_variable(s), delete (vi/ci/vectors, with map cleanup), is_valid,
variable/constraint/model attribute get/set, ObjectiveFunction get, modify (with _to_inner_change and _modify_substituted_change to avoid
double-translating decomposed modifications), VariableName/ConstraintName get/set, name→index lookups (including reverse-lookup of names
delegated to bridge-created variables), ListOfVariableIndices.
Activation triggers: variable mapping activates on first variable bridge; per-(F,S) constraint mapping activates on first force-bridged
VOV; add_bridged_constraint avoids identity-copied indices via _is_available_constraint_index.
One Utilities change: substitute_variables(map, x::VariableIndex) now allows index-to-index renaming, erroring only when bridged into a
real function.
Test updates (3 places where tests reached around the bridge layer to the mock with outer indices — they now use the inner index,
consistent with how they already handled y).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Here, we simply use an IndexMap to map inner and outer variables. This IndexMap is only created when variable bridges are used.
So if no variable bridges are used, this shouldn't affect performance or anything.
If variable bridges are used, it's already slow because we need to substitute variables in expressions so the IndexMap shouldn't be a bottleneck.
This doesn't require adding the reservation indices API like #3010 so it should be much better.
Closes #3010