The easiest way to extend JuMP is by writing new methods for [parse|build|add][variable|constraint]. The JuMPExtension approach might be overkill for some extensions but it may be needed, it is the approach used by StructJuMP.
In your case, it seems you could multiply the constant by t
in the addconstraint
call. To define a new method for the addconstraint
call you need new types. Here you AbstractConstraint
will be classical ones so you need a new type for the model.
You could define
struct LinearFractionalModel <: JuMP.AbstractModel
model::JuMP.Model
t::JuMP.VariableRef
end
then define all methods defined in JuMPExtension
for LinearFractionalModel
. However here instead of creating MyVariableRef
ans MyConstraintRef
you just redirect all calls to the inner model
field except addconstraint
for which you modify the function by modifying the constant term by t
before redirecting it to the inner model
field.
Let me know if that’s clear or if you have any question
Note: this should be documented here, your post help us see what are the use cases and how the doc could be structured
EDIT: An alternative would be to do this at the MOI level by writing an MOI layer. You would write a
struct LinearFractionalModel <: MOI.ModelLike
model::MOI.ModelLike
t::MOI.VariableIndex
end
then you redirect all calls to the inner model
except MOI.addconstraint!
in which you modify the constraint and
MOI.set(model::LinearFractionalModel, ::ObjectiveFunction{MOI.ScalarNonlinearFunction}, f::ScalarNonlinearFunction)
in which you error if the nonlinear function is a fraction and get the numerator and denominator then pass an
MOI.set(model.model, ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}, new_obj)
You would then need to modify the results, e.g. MOI.VariablePrimal
, to make the transformation transparent, e.g. by dividing y
by t
to get x
because the variable primal values returned by the inner model
field would be the values of y
and not x
.
The user will then be able to do @NLobjective model (x+1) / (x - 2)
.
However, that approach is currently not feasible as the nonlinear interface does not currently permits you to look into the expression graph (you need it to grab the numerator, denominator, …) but that will be the case when the nonlinear interface will be rewritten with Cassette.