Advice about refactoring LinearFractional.jl for JuMP 0.19 and Julia 0.7

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 :slight_smile:

Note: this should be documented here, your post help us see what are the use cases and how the doc could be structured :slight_smile:

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.

1 Like