Giter Club home page Giter Club logo

mathoptinterface.jl's Introduction

MathOptInterface

Documentation Build Status
Build Status Codecov branch

An abstraction layer for mathematical optimization solvers. Replaces MathProgBase.

Citing MathOptInterface

If you find MathOptInterface useful in your work, we kindly request that you cite the following paper:

@article{legat2021mathoptinterface,
    title={{MathOptInterface}: a data structure for mathematical optimization problems},
    author={Legat, Beno{\^\i}t and Dowson, Oscar and Dias Garcia, Joaquim and Lubin, Miles},
    journal={INFORMS Journal on Computing},
    year={2021},
    volume={34},
    number={2},
    pages={672--689},
    doi={10.1287/ijoc.2021.1067},
    publisher={INFORMS}
}

A preprint of this paper is freely available.

mathoptinterface.jl's People

Contributors

blegat avatar chriscoey avatar dourouc05 avatar edvinablad avatar ericphanson avatar frapac avatar github-actions[bot] avatar guilhermebodin avatar guimarqu avatar henriquebecker91 avatar ilancoulon avatar issamt avatar jd-lara avatar joaquimg avatar lkapelevich avatar martinbiel avatar matbesancon avatar mlubin avatar mohamed82008 avatar ndinsmore avatar odow avatar peiffap avatar rafabench avatar rdeits avatar remi-garcia avatar rtwalker avatar shadiakiki1986 avatar tkoolen avatar wikunia avatar yashvardhan747 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mathoptinterface.jl's Issues

Where should be caching?

I agree that the current strategy of JuMP is useful but I don't like the idea of cache being only in JuMP... I understand and accept the argument of having MOI a pure API but It would be nice to have the MOIU instance shared by JuMP and the solver wrapper. Example: if a solver wrapper wants to answer the query about MOI.ConstraintSet it will need to store it's own copy or build the set on the fly which would be a waste since the set exists and is stored in JuMP.

The problem with sharing is that I don't know if it is possible with julia to deny the solvers from modifying the instance once it is shared.

One possible solution: given a valid MOIU instance there are queries that do not need a solver to be answered. so wrappers do not implement those methods and we move them to MOIU. Everything using MOIU instance goes to MOIU to keep it separated from what solvers should implement. Then I see methods in MOIU like

MOIU.addconstraint!(m::MOI.AbstractSolverInstance, func::F, set::S, <:MOIU.AbstractInstance{T}, updateform::Bool)
#MOIU update its instance and if updateform == false
#MOIU keeps track of what is not yet passed through vectors for each combination F,S as discussed
#here https://github.com/JuliaOpt/MathOptInterface.jl/issues/63#issuecomment-317526848 

MOIU.update_solver_instance() 
#calls MOI.addconstraints! and deletes the vectors but keeps the instance.

MOIU.getattribute(m::MOIU.AbstractInstance{T}, ::MOI.ConstraintSet}, ref)
#replacing what was before in MOI and implemented by the solvers 
#MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet}, ref)

As @blegat mentioned, this will not work in the following case:
if the solver wrapper wants to have a mode on the fly where every info required for a single constraint is added and deleted right afterwards (being only kept inside the solver).

Such solver wrappers will be denied to have an answer to the following query since now it s the responsability of MOIU instance.

MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet}, ref)

To solve this issue, solvers need to have access to the instance (bad thing). and all those methods will not be replaced but only get a default implementation that uses MOIU instance (when available):

So Instead of this

MOIU.getattribute(m::MOIU.AbstractInstance{T}, ::MOI.ConstraintSet, ref)
# replacing what was before in MOI and implemented by the solvers
# MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet, ref)

We can have this

MOIU.getattribute(m::MOIU.AbstractInstance{T}, ::MOI.ConstraintSet, ref)
# helps providing default implementation to
# MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet}, ref)
function MOI.getattribute(m::MOI.AbstractSolverInstance{T}, s::MOI.ConstraintSet, ref)
    if has_instance(m)
       MOIU.getattribute(instance(m), s, ref)
    else
      throw MethodError()
    end
end

With JuMP being now only one among other possible users of MOI / MOIU doing instead of this :

functions1 = Vector{FunctionSetA}()
sets1 = Vector{ConstraintSetA}()
# fill the vectors
functions2 = Vector{FunctionSetA}()
sets2 = Vector{ConstraintSetB}()
# fill the vectors
# ...continue for each F,S type combination
MOI.addconstraints!(m,functions1, sets1)
MOI.addconstraints!(m,functions2, sets2)
#...continue for each F,S type combination

the follwing:

MOIU.addconstraint!(m, f, s, false) # assuming instance(m) returns an MOIU instance
#...continue adding the other constraints
MOIU.update_solver_instance(m)

some exception handling

Define what should happen when you ask a solver to do something that it can't do. Most of this is covered by cansetattribute, cangetattribute, and supportsproblem, but not everything (like adding a certain constraint after solving: #8). We have the InvalidInstance termination status code, although you would normally test that only after calling optimize!.

We should probably define a standard exception that solvers should throw which can be intercepted by JuMP to infer that a solver won't allow a certain action. For other exceptions JuMP might decide to let them pass through to the user.

Change constraint "sense"

Many solvers allow us to change the constraint from equality to inequality. The first idea was not to support it f245a6a , one had to delete a constraint and add a new one, however some solver might be able to modify the sign and not be able to delete. Moreover modifying looks simpler.

The idea is to have something along the lines:

c_new = modifyconstraint(m::SolverInstance, ::MOI.ConstraintSet, 
    c::MOI.ConstraintReference{F, MOI.LessThan{T}}, 
    newset::MOI.EquaTo{T})::MOI.ConstraintReference{F, MOI.EqualTo{T}}

separate model and solver attributes

only 3 overlap, and they don't really make sense (per discussion with @mlubin). so separate these attributes.

also from discussion with @mlubin: define an interface for asking a solver whether a group of "types" of constraints is supported together. and also a way to ask the solver, given an existing model that has already been solved, what types of constraints can be added to it. EDIT moved to #8

there is confusion on my part on what a model is and what a solver is. I think perhaps the terminology could be revisited to make it clearer.

A File-format for MathOpt

Here's a potentially naive question (considering I know nothing about conic):

Is it possible to save any problem (based on the current standard form with linear-quadratic functions, and the current sets) in the CBF format?

Because I was reading through the specification (incl. the new one http://cblib.zib.de/doc/format2.pdf) and it wasn't obvious to me how to have quadratic objectives/quadratic terms in the constraints.

It seems like if this is to be the standard form that unifies linear/conic etc, it would be nice to have one simple, easy to parse file-format that everybody agrees upon (unlike LP and MPS where nobody agrees on a canonical format and each reader/writer has weird limitations).

solver-independent options

... haven't been ported to MOI yet. Are they Solver attributes? SolverInstance attributes? Both? Something else?

Questions implementing a wrapper

I'm going to use this issue as a collection place for a range of questions I have implementing the CPLEX solver wrapper:

On the purpose of low-level interface

  • should we expose the full (potentially ad-hoc, complicated) API, or a simplified one to satisfy MOI requirements. Don't remove existing functionality

On variable bounds

  • is addconstraint!(m, v::VectorOfVariables, set::LessThan{Float64}) a valid constraint? oO
  • should addconstraint!(m, v::VectorOfVariables, set::Vector{Union{LessThan{Float64}, GreaterThan{Float64}, EqualsTo{Float64}, Interval{Float64}}}) be a valid constraint so users can set bounds all at once? No. Overload the broadcast operator #63 (comment) #63 (comment)

Preparing for generic duals with: FunctionDual, SetDual (and ConeDual?), and ConstraintDual

I am still processing the details, but after talking to @mlubin I think a name change from ConstraintDual to ConicDual may be important to allow the correct generic dual convention in the future.

The current dual conventions are based on Conic duality, but to me a more generic dual convention includes:

  1. dual for a constraint: simpler constraint (usually linear inequality) that approximates/contains the constraint.
  2. optimal dual for a constraint: a dual constraint that can replace the original constraint and lead to the same optimal value (and maybe solution)
  3. optimal set of constraint duals: set of optimal duals for the constraints that give a simple proof of optimality: e.g. objective is cx and duals are linear inequalities the cone generated by their lhs contains c
  4. dual problem: structured optimization problem that finds an optimal set of constraint duals.

Point 1. also works for functions and sets, and probably the function and set duals can be combined into a dual for the associated constraint (e.g. if the function is affine and the set is a cone, we know how to get the constraint dual). Points 2. and 3. are the same up to the relative scaling between the duals required by Point 3. You also may be able to get the duals for 3. even if 4. does not exist. Probably a lot more to discuss about this (e.g. infeasibility and unboundedness) and so too early to resolve.

In the mean time I propose changing ConstraintDual to ConicDual so we can reserve FunctionDual, SetDual and ConstraintDual for the future. ConicDual would be a special case of SetDual whose feasibility/optimality is w/r to 4. In particular, this would allow us to later have a ConeDual in between SetDual and ConicDual whose feasibility/optimality could be w/r to 1.-3. (e.g. in a conic problem the ConeDuals in an optimal set from 3. would be any non-zero scalings of the optimal ConicDuals)

Return type of getattribute functions

Is a detailed documentation enough or should we have something like the following?

abstract type AbstractSolverInstanceAttribute{T} end
...

and

function getattribute(m, attr::AnyAttribute{T}, args...)::T

We can have shortcuts like NumberOfVariables <: AbstractSolverInstanceAttribute{Int}

separate packages for MOI wrappers?

Since we're about to write a bunch of MOI wrappers, now would be a good time to reconsider the current structure of the solver wrappers. I propose that we move all of the MOI wrappers into separate packages, like GurobiMOI MathOptInterfaceGurobi, CPLEXMOI MathOptInterfaceCPLEX, CbcMOI MathOptInterfaceCbc, etc. The reasons are:

  • We would like solver companies and developers to become more engaged with the Julia interfaces, and it will be easier to do so if we don't require them to maintain MOI wrappers as well.
  • There is potential for contention when non-JuliaOpt developers want to include something in a solver wrapper (e.g., jump-dev/CPLEX.jl#125). With MOI wrappers in a separate package, the logical separation of what should go where will be more clear.
  • It will be easier to write the new wrappers starting from fresh empty packages.
  • We'll finally have consistency with GLPK.

It's a bit uglier to say using MathOptInterfaceGurobi versus using Gurobi. JuMP can get around this by providing a @usingsolver macro that translates @usingsolver Gurobi, Mosek into using MathOptInterfaceGurobi, MathOptInterfaceMosek.

CC's for those not watching this repo: @ulfworsoe @tkelman @odow

out of date docs

There are references to VariableResult which is now called VariablePrimal.

Tests to be ported from MPB

List of tests to be ported from MPB

linproginterface.jl

  • 1 "Testing LP interface with $solver"
    • a "Basic interface"
    • b "addvar! interface"
    • c "setconstrLB! and setconstrUB!"
      now: "Modify constants in Nonnegative and Nonpositive"
      and: "Modify GreaterThan and LessThan sets as linear constraints"
    • d "Issue #40 from Gurobi.jl"
    • e "Change coeffs, del constr, del var"
  • 2 "Testing LP interface extra with $solver"
    • a "setconstrLB! and setcontrUB!"
    • b "setvarLB! and setvarUB!"

linprog.jl

  • 1 "Testing linprog and subfunctions with $solver"
    • a "LP1"
    • b "LP2"
    • c "LP3 infeasible"
    • d "LP4 unbounded"
    • e "LP4 unbounded with unique ray"

mixintprog.jl

  • 1 "Testing mixintprog with $solver"
    now: "Knapsack Model"

quadprog.jl

  • 1 "Testing quadprog with $solver"
    • a "QP1"
    • b "QP2"
  • 2 "Testing QP duals with $solver"
    • a "QP1"
    • b "QP2"
  • 3 "Testing SOCP interface with $solver"

conicinterface.jl

  • 1 "Testing linear problems through conic interface with $solver"
    • a "LIN1"
    • b "LIN1A"
    • c "LIN2"
    • d "LIN2A"
    • e "LIN3 infeasible"
    • f "LIN4 infeasible"
  • 2 "Testing SOC problems through conic interface with $solver"
    • a "SOC1"
    • b "SOC1A"
    • c "SOC2"
    • d "SOC2A"
    • e "SOC3 infeasible"
    • f "SOC4"
  • 3 "Testing SOCRotated problems through conic interface with $solver"
    • a "SOCRotated1"
    • b "SOCRotated1A"
    • c "SOCRotated2 infeasible"
  • 4 "Testing SOCINT problems through conic interface with $solver"
  • 5 "Testing EXP problems through conic interface with $solver"
    • a "EXP1"
    • b "EXP1A"
    • c "EXP2"
    • d "EXP3"
  • 6 "Testing SDP problems through conic interface with $solver"
    • a "SDP1"
    • a "SDP1" with PositiveSemidefinteConeScaled
    • b "SDP2"

Parameterize the solver and instance types with a number type

Should we parameterize the solver and interface types with number types? For instance replace CSDPSolver by CSDPSolver{Float64} and CSDPSolverInstance by CSDPSolverInstance{Float64}.

I think this is necessary, for instance, to make SemidefiniteOptInterface work with high precision solvers.

performance hints

@joaquimg proposed a performance hint to tell the MOI wrapper to load the problem into the solver. I expect that we may decide that we want other performance hints as we start implementing things, so opening this for general discussion.

an Instance type

It would be nice if we had a data type to store all the data for an instance in MOI format independent of a solver API. This will make it more natural to talk about performing transformations, and we could also revive loadproblem! which could be more efficient for throwing everything to a SolverInstance at once.

Should the data structure just be a dictionary with vectors of variablewise, affine, and quadratic constraints (by type)? Do we care about type stability?

rename sense attribute?

The attribute name Sense seems inconsistently short for MOI. I keep thinking it should be ObjectiveSense. Opinions?

Naming of set fields

There are a few inconsistencies in the naming of various fields within sets

  • dim instead of dimension: most of the time, long names are preferred (i.e. lower). However, dim is used in place of `dimension.
  • a is not descriptive in the PowerCone: should it be exponent or something similar?
  • l and u in Semicontinuous instead of lower and upper

Are we happy with these or do we want to change?

Interval sets

Why do we need Reals, Zeros, Nonnegatives, Nonpositives, GreaterThan, LessThan, EqualTo, and Interval?

struct Interval{T}
    lower::T
    upper::T
end
struct Intervals{T}
    lower::Vector{T}
    upper:Vector{T}
end
# or just riffing here...
struct VectorOfIntervals{T}
    intervals::Vector{Interval{T}}
end

# variable bounds
addconstraint!(m, ::SingleVariable, ::Interval)
addconstraint!(m, ::SingleVariable, ::Intervals) # error. Not valid

addconstraints!(m, ::VectorOfVariables, ::Interval) # every variable has the same bound
addconstraints!(m, ::VectorOfVariables, ::Intervals) # every variable has different bound

# constraints
addconstraints!(m, ::ScalarAffineFunction, ::Interval) 
addconstraints!(m, ::ScalarAffineFunction, ::Intervals) # error not valid

addconstraints!(m, ::VectorAffineFunction, ::Interval) # every constraint has same set
addconstraints!(m, ::VectorAffineFunction, ::Intervals) # every constraint has different set 

addconstraints!(m, ::ScalarQuadraticFunction, ::Interval) 
addconstraints!(m, ::ScalarQuadraticFunction, ::Intervals) # error not valid

addconstraints!(m, ::VectorQuadraticFunction, ::Interval) # every constraint has same set
addconstraints!(m, ::VectorQuadraticFunction, ::Intervals) # every constraint has different set 

You can have Nonpositives(dim) = Intervals(fill(-Inf, dim), fill(0.0, dim)) if you really want.

ScalarLinearFunction and redundancy with scalar constraints

We have the following table in the manual:

Linear constraints

Mathematical Constraint MOI Function MOI Set
a^Tx \le u ScalarAffineFunction LessThan
a^Tx \ge l ScalarAffineFunction GreaterThan
a^Tx = b ScalarAffineFunction EqualTo
l \le a^Tx \le u ScalarAffineFunction Interval
... ... ...

In the first four lines we use a^Tx in the first column and we say ScalarAffineFunction, should we say/use ScalarLinearFunction? This could avoid multiple representations of the same constraint. Since a^Tx - b = 0 is equivalent to a^Tx = b

better mechanism for choosing which tests to run

Right now we have a few different ways of deciding whether to run a particular test or subtest:
Example 1 Example 2.

I wanted to get rid of the duals keyword and replace it with the assumption that the solver wants us to test duals if the SupportsDuals attribute returns true, but that doesn't quite work since some solvers (ahem) do duals for linear and quadratic but not cones.

We should think up a nicer way to handle this.

More info about TerminationStatus/PrimalStatus etc

Should we have a minimally standardized way to return more info about the status?

Some output info of Xpress and CPLEX do not match any existing status (and probably should not match at all) but we could have a default way to return a string/symbol/error/warning with that extra info.

need way to query what constraints can be added together

from discussion with @mlubin, and following the fix to #6.

define an interface for asking a solver

  • whether a group of "types" of constraints is supported together (ie a valid instance can be constructed with all of them)
  • whether, given an existing instance that has already been solved, what types of constraints can be added to it

Benchmark against MPB

With (#70) CPLEX and Xpress working for LP we could start preparing benchmark (against MPB).

Should they be in tests or we need a bench folder?

Suggestions?

force Nonnegative, Nonpostive, and Zeros to be vector sets

In order to avoid some level of redundancy and extra work for the solver wrappers, I'm thinking that it would be a good idea to disallow (by convention) constraints of the form: ScalarAffineFunction in Nonnegative(1), Nonpositve(1), or Zeros(1). These are already covered by the one-dimensional sets GreaterThan, LessThan, and EqualTo.

Helper functions

Some functions like converting Matrices to MOI VectorAffineFunction format might be very useful for users, for instance this sketch:

function triplets(A::SparseMatrixCSC, cols::Vector{MOI.VariableReference}, rows::Vector{I})
    @assert A.m == length(rows)
    @assert A.n == length(cols)
    colval = colval(A.colptr, length(A.rowval))
    return rows[A.rowval], cols[colval], A.nzvals    
end
function colval(colptr::Vector{I}, n::Integer)
    colval = zeros(I, n)
    c = 1
    for i in 1:length(colptr)-1
        for j in colptr[i]:(colptr[i+1]-1)
            colval[c] = I(i)
            c+=1
        end
    end
    return colval
end

Would it be good to have a MOIExtras.jl?
Maybe solver APIs have similar functions that could also go into MOIExtras.jl ...

References as pointers

What if...

References were pointers instead of UInt´s?

With UInts we can have simple vector as mappings IF the solver does not support deletions. However, if the solver supports deletions we need some mapping. This mapping could be a vector, which would grow a lot with lots of deletions; could be a linked list which is fast to delete but not fast to access; could be a Dict which is easy to delete but not so fast to access.

Given that we usually do a lot more additions than deletions, may we should not be much worried of the time it takes to delete, but with the time it takes to add.

My proposal goes as follows, instead of giving the user a unique UInt everytime a variable/constraint is added we could give a pointer to an Int that directly matches the solver´s index, the SolverInstance would also hold a list of those pointers. In case of adding a constraint there would be no need for a lookup to get the solver´s corresponding index. In case of a deletion we can set that pointer to a zero value and fix all the other pointers since the SolverInstance has a list of them, then the user´s reference are fixed.

default value for Sense?

Currently we haven't defined what happens when the Sense attribute is never set. Should we define it to be minimization by default or make it required to provide a sense? I'm leaning towards the latter.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.