Thanks for the hint, besides all the digging into MOI, I somehow missed that JuMP has the add_bridge
too…
MWE below, short preface: I’ve got a quadratic constraint, that I want to reformulate in a way that HiGHS can handle it; the MWE just deletes the constraint.
The bridge looks like (based on your answer here):
struct MyCustomBridge{T} <: MOI.Bridges.Constraint.AbstractBridge end
function MOI.Bridges.Constraint.concrete_bridge_type(
::Type{<:MyCustomBridge},
F::Type{<:MOI.ScalarQuadraticFunction},
S::Type{<:MOI.AbstractScalarSet},
)
return MyCustomBridge{Float64}
end
function MOI.supports_constraint(
::Type{<:MyCustomBridge},
::Type{<:MOI.ScalarQuadraticFunction},
::Type{<:MOI.AbstractScalarSet},
)
return true
end
function MOI.Bridges.added_constrained_variable_types(::Type{<:MyCustomBridge})
return Tuple{DataType}[]
end
function MOI.Bridges.added_constraint_types(::Type{<:MyCustomBridge})
return []
end
function MOI.Bridges.Constraint.bridge_constraint(
::Type{<:MyCustomBridge},
m::MOI.ModelLike,
f::MOI.ScalarQuadraticFunction,
s::MOI.AbstractScalarSet,
)
@info "Bridging now"
return MyCustomBridge{Float64}()
end
The model building looks like:
model = JuMP.Model(HiGHS.Optimizer)
JuMP.add_bridge(model, MyCustomBridge)
x = JuMP.@variable(model, [1:3], lower_bound=0)
c = JuMP.@constraint(model, x[1] + x[2]^2 - 3*x[3] >= 0)
JuMP.@objective(model, Min, x[1] + 0)
JuMP.optimize!(model) # calling the bridge here
JuMP.fix(x[2], 5; force=true) # fixing this variable would make the constraint viable for HiGHS
JuMP.optimize!(model) # not calling the bride here - constraint is still deleted
JuMP.set_normalized_rhs(c, JuMP.normalized_rhs(c))
JuMP.optimize!(model) # calling the bridge here
The important part is JuMP.fix(x[2], 5; force=true)
, where the user may change the constraint (by fixing the variable that complicates the constraint into a quadratic one) - essentially reducing the constraint to an affine one. Now the next optimize!(...)
does not call the bridge - as far as I understand because it actually does not “know of the change” (since fix()
just creates an additional MOI Equal).
If however I force an “update” of the respective constraint, e.g. using JuMP.set_normalized_rhs(c, JuMP.normalized_rhs(c))
, then the following optimize!(...)
will invoke the bridge - allowing me to react to the change that the user made.
While it makes sense (at least to me ), that this happens, I’m looking for a “less hacky” way to trigger that update.