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

Versatile model type #1261

Closed
blegat opened this issue Mar 3, 2021 · 4 comments · Fixed by #1521
Closed

Versatile model type #1261

blegat opened this issue Mar 3, 2021 · 4 comments · Fixed by #1521
Labels
Milestone

Comments

@blegat
Copy link
Member

blegat commented Mar 3, 2021

Currently, MOI only provides one implementation of model: MOIU.AbstractModel that are created with MOIU.@model with the common choice of MOIU.Model.
This represents models in the following way:
We have a struct containing:

  • The objective which can be SingleVariable, ScalarAffineFunction or ScalarQuadraticFunction
  • A datastructure for SingleVariable constraints
  • One field per supported function type containing
    • One field per supported set type containing
      • a datastructure for storing each constraint of that type.
        This is useful to use as the cache used by JuMP to store the model as it gives good performance for all MOI operations. However, when copying this model to a solver, some work should be done by the solver to transfrom it into its canonical form.
        If we look at it more closely, the form required by the solvers are a combination of the following:
  • An objective type
  • A datastructure for SingleVariable constraints
  • One or more separate datastructures for subsets of constraint types. For example, some conic solvers have one datastructure for VectorAffineFunction-in-Zeros and one for VectorAffineFunction-in-K in other cones K.

LP solvers such as Clp, GLPK, HiGHS need all the constraints mixed up in a single matrix stored either row-wise or column-wise.

If we refactor AbstractModel, it could cover all these use case, simply by being parametrized by:

  • The type of objective
  • The type of the datastructure for SingleVariable constraints
  • The type of the datastructure for the other constraints.

This third type is either

Then, each solver would be able to significantly simplify it's copy_to function by doing, e.g. LP solvers could do

const MODEL = MOI.Utilities.GenericModel{MOI.ScalarAffineFunction{Float64}, ContinuousVariables, MatrixOfConstraints{..., SparseMatrixCSC{..., MOI.ZeroBasedIndexing}}
function MOI.copy_to(dest::Optimizer, src::MODEL)
    # No need to shift `rowval` by `-1` as it's already zero-base indexing.
    Solver_C_function(src.constraints.matrix.rowvals, ...)
    return identity_index_map(src)
end
function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
    model = MODEL()
    index_map = MOI.copy_to(model, src)
    MOI.copy_to(dest, model)
    return index_map
end

In order to drastically increase the chance a MODEL is copied to the solver, we could add an MOI attribute and replace Utilities.Model{with_bridge_type}() in

Utilities.UniversalFallback(Utilities.Model{with_bridge_type}())

by MOI.get(optimizer, MOI.PreferedModelType())().

The result of all this is two-fold:

  1. It would drastically simplify copy_to for solver wrappers and ensure they are all efficiently optimized
  2. It would spare the copy of the data that is done in this copy_to as the cache of the CachingOptimizer will already contain the data in the right form.
@odow odow added the Submodule: Utilities About the Utilities submodule label Mar 3, 2021
@odow
Copy link
Member

odow commented Mar 3, 2021

I would be in favor of a new MOI.Utilities.Model. We can leave the current one in-place so solvers can gracefully transition. I think we now understand the design-space a lot better than when it was first written.

@blegat
Copy link
Member Author

blegat commented Mar 4, 2021

We would still have const Model{T} = GenericModel{...} so it shouldn't break any code using @model or Model except if the solver is accessing the fields of Model directly but that is not considered part of the API. We have already modified the fields of Model before without considering it as a breaking change. Solvers should not access these fields anyway.
The purpose is to have one flexible implementation so that it's easier and more consistent to maintain. Having to keep two of them would be a burden.

@blegat
Copy link
Member Author

blegat commented Mar 4, 2021

In the line of #1252 (comment), in case the solver wants the matrix to be column-wise, we could have a fallback implementation of add_constraint that stores things temporarily in one vector per column and stack them at the end or something like that. That would allow the model to be used to store the bridged cache.
In copy_to for LazyBridgeOptimizer, we could check the constraint types that are neither bridged or added by bridged and copy these in a copy_to-like fashion so that even if some part is bridged, we could still use the efficient column-wise copy_to in case the relevant constraints are not bridged nor added by bridges.

@odow
Copy link
Member

odow commented Aug 9, 2021

@blegat: this, #1470 and #1502 are blocking for MOI 0.10. Can I ask that we either set a timeline ASAP for getting these implemented and merged.

Or, more preferably, decide to delay these until MOI 1.x? A GenericModel2 type could be introduced as a non-breaking feature, or even developed in an external package first. The MOI 0.10 release keeps slipping as we add more features, and there is still a lot of work to do once we release 0.10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

2 participants