From 1519ae15542f2b5d651185ee82f7e5a28390d5f1 Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Wed, 14 Jun 2017 22:28:27 -0400 Subject: [PATCH 01/10] docstrings for writers --- docs/src/refmodel.md | 10 ++++++++-- src/writers.jl | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/src/refmodel.md b/docs/src/refmodel.md index cfbe705d3d9..c84d30425f0 100644 --- a/docs/src/refmodel.md +++ b/docs/src/refmodel.md @@ -1,3 +1,7 @@ +```@meta +CurrentModule = JuMP +``` + Models ====== @@ -60,8 +64,10 @@ Methods **Output** -- `writeLP(m::Model, filename::AbstractString; genericnames=true)` - write the model to `filename` in the LP file format. Set `genericnames=false` for user-defined variable names. -- `writeMPS(m::Model, filename::AbstractString)` - write the model to `filename` in the MPS file format. +```@docs +writeLP +writeMPS +``` Solve Status ------------ diff --git a/src/writers.jl b/src/writers.jl index e06b5967dc2..4385a32e7cc 100644 --- a/src/writers.jl +++ b/src/writers.jl @@ -3,6 +3,11 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +""" + writeMPS(m::Model, fname::AbstractString) + +write the model to `fname` in the MPS file format. +""" function writeMPS(m::Model, fname::AbstractString) f = open(fname, "w") @@ -188,6 +193,11 @@ function varname_given(m::Model, col::Integer) name end +""" + writeLP(m::Model, fname::AbstractString; genericnames=true) + +write the model to `fname` in the LP file format. Set `genericnames=false` for user-defined variable names. +""" function writeLP(m::Model, fname::AbstractString; genericnames=true) varname = genericnames ? varname_generic : varname_given From 613094f207bdc7212382ffa8ed6876b35304cd3b Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Wed, 14 Jun 2017 22:38:05 -0400 Subject: [PATCH 02/10] moved model objective to docstrings --- docs/src/refmodel.md | 8 ++++++++ src/JuMP.jl | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/docs/src/refmodel.md b/docs/src/refmodel.md index c84d30425f0..06873c8af9e 100644 --- a/docs/src/refmodel.md +++ b/docs/src/refmodel.md @@ -56,6 +56,14 @@ Methods **Objective** +```@docs +getobjective +getobjectivesense +setobjectivesense +getobjectivevalue +getobjectivebound +``` + - `getobjective(m::Model)` - returns the objective function as a `QuadExpr`. - `getobjectivesense(m::Model)` - returns objective sense, either `:Min` or `:Max`. - `setobjectivesense(m::Model, newSense::Symbol)` - sets the objective sense (`newSense` is either `:Min` or `:Max`). diff --git a/src/JuMP.jl b/src/JuMP.jl index e01708e4a89..99a499b35cb 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -216,6 +216,11 @@ for f in MathProgBase.SolverInterface.methods_by_tag[:rewrap] eval(Expr(:export,f)) end +""" + getobjective(m::Model) + +returns the objective function as a `QuadExpr` +""" function getobjective(m::Model) traits = ProblemTraits(m) if traits.nlp @@ -224,9 +229,32 @@ function getobjective(m::Model) return m.obj end +""" + getobjectivebound(m::Model) + +returns the best known bound on the optimal objective value after a call to `solve` +""" getobjectivebound(m::Model) = m.objBound + +""" + getobjectivevalue(m::Model) + +returns objective value after a call to `solve` +""" getobjectivevalue(m::Model) = m.objVal + +""" + getobjectivesense(m::Model) + +returns objective sense, either `:Min` or `:Max` +""" getobjectivesense(m::Model) = m.objSense + +""" + setobjectivesense(m::Model, newSense::Symbol) + +sets the objective sense (`newSense` is either `:Min` or `:Max`) +""" function setobjectivesense(m::Model, newSense::Symbol) if (newSense != :Max && newSense != :Min) error("Model sense must be :Max or :Min") From 57ec7e6362be2dcc538bb2eccde59e91a9e4cdfa Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Wed, 14 Jun 2017 22:50:33 -0400 Subject: [PATCH 03/10] starting model general methods --- docs/src/refmodel.md | 20 +++++++++++++------ src/JuMP.jl | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/docs/src/refmodel.md b/docs/src/refmodel.md index 06873c8af9e..880313abbaa 100644 --- a/docs/src/refmodel.md +++ b/docs/src/refmodel.md @@ -32,14 +32,28 @@ Methods **General** +```@docs +MathProgBase.numvar +MathProgBase.numlinconstr +MathProgBase.numquadconstr +numsocconstr +numsosconstr +numsdconstr +numnlconstr +MathProgBase.numconstr +``` + - `MathProgBase.numvar(m::Model)` - returns the number of variables associated with the `Model m`. - `MathProgBase.numlinconstr(m::Model)` - returns the number of linear constraints associated with the `Model m`. - `MathProgBase.numquadconstr(m::Model)` - returns the number of quadratic constraints associated with the `Model m`. + - `JuMP.numsocconstr(m::Model)` - returns the number of second order cone constraints associated with the `Model m`. - `JuMP.numsosconstr(m::Model)` - returns the number of sos constraints associated with the `Model m`. - `JuMP.numsdconstr(m::Model)` - returns the number of semi-definite constraints associated with the `Model m`. - `JuMP.numnlconstr(m::Model)` - returns the number of nonlinear constraints associated with the `Model m`. + - `MathProgBase.numconstr(m::Model)` - returns the total number of constraints associated with the `Model m`. + - `getsolvetime(m::Model)` - returns the solve time reported by the solver if it is implemented. - `getnodecount(m::Model)` - returns the number of explored branch-and-bound nodes, if it is implemented. - `getobjbound(m::Model)` - returns the best known bound on the optimal objective value. This is used, for example, when a branch-and-bound method is stopped before finishing. @@ -64,12 +78,6 @@ getobjectivevalue getobjectivebound ``` -- `getobjective(m::Model)` - returns the objective function as a `QuadExpr`. -- `getobjectivesense(m::Model)` - returns objective sense, either `:Min` or `:Max`. -- `setobjectivesense(m::Model, newSense::Symbol)` - sets the objective sense (`newSense` is either `:Min` or `:Max`). -- `getobjectivevalue(m::Model)` - returns objective value after a call to `solve`. -- `getobjectivebound(m::Model)` - returns the best known bound on the optimal objective value after a call to `solve`. - **Output** ```@docs diff --git a/src/JuMP.jl b/src/JuMP.jl index 99a499b35cb..335912cc77b 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -187,14 +187,61 @@ function Model(;solver=UnsetSolver(), simplify_nonlinear_expressions::Bool=false end # Getters/setters + +""" + MathProgBase.numvar(m::Model) + +returns the number of variables associated with the `Model m`. +""" MathProgBase.numvar(m::Model) = m.numCols + +""" + MathProgBase.numlinconstr(m::Model) + +returns the number of linear constraints associated with the `Model m` +""" MathProgBase.numlinconstr(m::Model) = length(m.linconstr) + +""" + MathProgBase.numquadconstr(m::Model) + +returns the number of quadratic constraints associated with the `Model m` +""" MathProgBase.numquadconstr(m::Model) = length(m.quadconstr) + +""" + numsocconstr(m::Model) + +returns the number of second order cone constraints associated with the `Model m` +""" numsocconstr(m::Model) = length(m.socconstr) + +""" + numsosconstr(m::Model) + +returns the number of sos constraints associated with the `Model m` +""" numsosconstr(m::Model) = length(m.sosconstr) + +""" + numsdconstr(m::Model) + +returns the number of semi-definite constraints associated with the `Model m` +""" numsdconstr(m::Model) = length(m.sdpconstr) + +""" + numnlconstr(m::Model) + +returns the number of nonlinear constraints associated with the `Model m` +""" numnlconstr(m::Model) = m.nlpdata !== nothing ? length(m.nlpdata.nlconstr) : 0 +""" + MathProgBase.numconstr(m::Model) + +returns the total number of constraints associated with the `Model m` +""" function MathProgBase.numconstr(m::Model) c = length(m.linconstr) + length(m.quadconstr) + length(m.socconstr) + length(m.sosconstr) + length(m.sdpconstr) if m.nlpdata !== nothing From ced313ed367b861828003db0b623dfd301e8118c Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Wed, 14 Jun 2017 23:41:12 -0400 Subject: [PATCH 04/10] inital migration of varref to docstrings --- docs/src/refmodel.md | 24 +++------ docs/src/refvariable.md | 32 +++++++++--- src/JuMP.jl | 113 +++++++++++++++++++++++++++++++++++++++- src/solvers.jl | 18 ++++++- 4 files changed, 158 insertions(+), 29 deletions(-) diff --git a/docs/src/refmodel.md b/docs/src/refmodel.md index 880313abbaa..76bcfd74723 100644 --- a/docs/src/refmodel.md +++ b/docs/src/refmodel.md @@ -41,19 +41,14 @@ numsosconstr numsdconstr numnlconstr MathProgBase.numconstr +internalmodel +solve +build +setsolver +Base.getindex(m::Model, name::Symbol) +Base.setindex!(m::Model, value, name::Symbol) ``` -- `MathProgBase.numvar(m::Model)` - returns the number of variables associated with the `Model m`. -- `MathProgBase.numlinconstr(m::Model)` - returns the number of linear constraints associated with the `Model m`. -- `MathProgBase.numquadconstr(m::Model)` - returns the number of quadratic constraints associated with the `Model m`. - -- `JuMP.numsocconstr(m::Model)` - returns the number of second order cone constraints associated with the `Model m`. -- `JuMP.numsosconstr(m::Model)` - returns the number of sos constraints associated with the `Model m`. -- `JuMP.numsdconstr(m::Model)` - returns the number of semi-definite constraints associated with the `Model m`. -- `JuMP.numnlconstr(m::Model)` - returns the number of nonlinear constraints associated with the `Model m`. - -- `MathProgBase.numconstr(m::Model)` - returns the total number of constraints associated with the `Model m`. - - `getsolvetime(m::Model)` - returns the solve time reported by the solver if it is implemented. - `getnodecount(m::Model)` - returns the number of explored branch-and-bound nodes, if it is implemented. - `getobjbound(m::Model)` - returns the best known bound on the optimal objective value. This is used, for example, when a branch-and-bound method is stopped before finishing. @@ -61,12 +56,7 @@ MathProgBase.numconstr - `getrawsolver(m::Model)` - returns an object that may be used to access a solver-specific API. - `getsimplexiter(m::Model)` - returns the cumulative number of simplex iterations during the optimization process. In particular, for a MIP it returns the total simplex iterations for all nodes. - `getbarrieriter(m::Model)` - returns the cumulative number of barrier iterations during the optimization process. -- `internalmodel(m::Model)` - returns the internal low-level `AbstractMathProgModel` object which can be used to access any functionality that is not exposed by JuMP. See the MathProgBase [documentation](https://mathprogbasejl.readthedocs.org/en/latest/). -- `solve(m::Model; suppress_warnings=false, relaxation=false)` - solves the model using the selected solver (or a default for the problem class), and takes two optional arguments that are disabled by default. Setting `suppress_warnings` to `true` will suppress all JuMP-specific output (e.g. warnings about infeasibility and lack of dual information) but will not suppress solver output (which should be done by passing options to the solver). Setting `relaxation=true` solves the standard continuous relaxation for the model: that is, integrality is dropped, special ordered set constraints are not enforced, and semi-continuous and semi-integer variables with bounds `[l,u]` are replaced with bounds `[min(l,0),max(u,0)]`. -- `JuMP.build(m::Model)` - builds the model in memory at the MathProgBase level without optimizing. -- `setsolver(m::Model,s::AbstractMathProgSolver)` - changes the solver which will be used for the next call to `solve()`, discarding the current internal model if present. -- `getindex(m::Model,name::Symbol)` - returns the variable, or group of variables, or constraint, or group of constraints, of the given name which were added to the model. This errors if multiple variables or constraints share the same name. -- `setindex!(m::Model, value, name::Symbol)` - stores the object `value` in the model `m` so that it can be accessed via `getindex`. + **Objective** diff --git a/docs/src/refvariable.md b/docs/src/refvariable.md index 8b05e3fec33..57eaa902816 100644 --- a/docs/src/refvariable.md +++ b/docs/src/refvariable.md @@ -1,3 +1,7 @@ +```@meta +CurrentModule = JuMP +``` + Variables ========= @@ -144,13 +148,19 @@ Methods **Bounds** -- `setlowerbound(x::Variable, lower)`, `getlowerbound(x::Variable)` - Set/get the lower bound of a variable. -- `setupperbound(x::Variable, upper)`, `getupperbound(x::Variable)` - Set/get the upper bound of a variable. +```@docs +setlowerbound +getlowerbound +setupperbound +getupperbound +``` **Variable Category** -- `setcategory(x::Variable, v_type::Symbol)` - Set the variable category for `x` after construction. Possible categories are listed above. -- `getcategory(x::Variable)` - Get the variable category for `x`. +```@docs +setcategory +getcategory +``` **Helper functions** @@ -159,9 +169,11 @@ Methods **Values** -- `getvalue(x)` - Get the value of this variable in the solution. If `x` is a single variable, this will simply return a number. If `x` is indexable then it will return an indexable dictionary of values. When the model is unbounded, `getvalue` will instead return the corresponding components of an unbounded ray, if available from the solver. -- `setvalue(x,v)` - Provide an initial value `v` for this variable that can be used by supporting MILP solvers. If `v` is `NaN`, the solver may attempt to fill in this value to construct a feasible solution. `setvalue` cannot be used with fixed variables; instead their value may be set with `JuMP.fix(x,v)`. -- `getdual(x)` - Get the reduced cost of this variable in the solution. Similar behavior to `getvalue` for indexable variables. +```@docs +getvalue +setvalue +getdual +``` !!! note The `getvalue` function always returns a floating-point value, even when a variable is constrained to take integer values, as most solvers only guarantee integrality up to a particular numerical tolerance. The built-in `round` function should be used to obtain integer values, e.g., by calling `round(Integer, getvalue(x))`. @@ -170,7 +182,11 @@ Methods Variables (in the sense of columns) can have internal names (different from the Julia variable name) that can be used for writing models to file. This feature is disabled for performance reasons, but will be added if there is demand or a special use case. -- `setname(x::Variable, newName)`, `getname(x::Variable)` - Set/get the variable's internal name. +```@docs +setname +getname +``` + Fixed variables --------------- diff --git a/src/JuMP.jl b/src/JuMP.jl index 335912cc77b..720ded46cf6 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -314,12 +314,18 @@ setobjective(m::Model, something::Any) = setobjective(::Model, ::Symbol, x::AbstractArray) = error("in setobjective: array of size $(_size(x)) passed as objective; only scalar objectives are allowed") +""" + setsolver(m::Model, solver::MathProgBase.AbstractMathProgSolver) + +changes the solver which will be used for the next call to `solve()`, discarding the current internal model if present. +""" function setsolver(m::Model, solver::MathProgBase.AbstractMathProgSolver) m.solver = solver m.internalModel = nothing m.internalModelLoaded = false nothing end + # Deep copy the model function Base.copy(source::Model) @@ -387,6 +393,11 @@ function Base.copy(source::Model) return dest end +""" + internalmodel(m::Model) + +returns the internal low-level `AbstractMathProgModel` object which can be used to access any functionality that is not exposed by JuMP. See the MathProgBase [documentation](https://mathprogbasejl.readthedocs.org/en/latest/) +""" internalmodel(m::Model) = m.internalModel setsolvehook(m::Model, f) = (m.solvehook = f) @@ -449,28 +460,74 @@ function Variable(m::Model,lower::Number,upper::Number,cat::Symbol,name::Abstrac return Variable(m, m.numCols) end + # Name setter/getters +""" + setname(v::Variable,n::AbstractString) + +Set the variable's internal name. +""" function setname(v::Variable,n::AbstractString) push!(v.m.customNames, v) v.m.colNames[v.col] = n v.m.colNamesIJulia[v.col] = n end + +""" + getname(m::Model, col) + +Get the variable's internal name. +""" getname(m::Model, col) = var_str(REPLMode, m, col) + +""" + getname(v::Variable) + +Get the variable's internal name. +""" getname(v::Variable) = var_str(REPLMode, v.m, v.col) + # Bound setter/getters +""" + setlowerbound(v::Variable,lower::Number) + +set the lower bound of a variable. +""" function setlowerbound(v::Variable,lower::Number) v.m.colCat[v.col] == :Fixed && error("use setvalue for changing the value of a fixed variable") v.m.colLower[v.col] = lower end + +""" + setupperbound(v::Variable,upper::Number) + +set the upper bound of a variable. +""" function setupperbound(v::Variable,upper::Number) v.m.colCat[v.col] == :Fixed && error("use setvalue for changing the value of a fixed variable") v.m.colUpper[v.col] = upper end + +""" + getlowerbound(v::Variable) + +get the lower bound of a variable. +""" getlowerbound(v::Variable) = v.m.colLower[v.col] + +""" + getupperbound(v::Variable) + +get the upper bound of a variable. +""" getupperbound(v::Variable) = v.m.colUpper[v.col] -# Value setter/getter +""" + setvalue(v::Variable, val::Number) + +Provide an initial value `v` for this variable that can be used by supporting MILP solvers. If `v` is `NaN`, the solver may attempt to fill in this value to construct a feasible solution. `setvalue` cannot be used with fixed variables; instead their value may be set with `JuMP.fix(x,v)` +""" function setvalue(v::Variable, val::Number) v.m.colVal[v.col] = val if v.m.colCat[v.col] == :Fixed @@ -491,6 +548,11 @@ _getValue(v::Variable) = v.m.colVal[v.col] getvaluewarn(v) = Base.warn("Variable value not defined for $(getname(v)). Check that the model was properly solved.") +""" + getvalue(v::Variable) + +Get the value of this variable in the solution. +""" function getvalue(v::Variable) ret = _getValue(v) if isnan(ret) @@ -499,6 +561,11 @@ function getvalue(v::Variable) ret end +""" + getvalue(arr::Array{Variable}) + +Returns an indexable dictionary of values. When the model is unbounded, returns the corresponding components of an unbounded ray, if available from the solver. +""" function getvalue(arr::Array{Variable}) ret = similar(arr, Float64) # return immediately for empty array @@ -537,6 +604,11 @@ _getDual(v::Variable) = v.m.redCosts[v.col] getdualwarn(::Variable) = warn("Variable bound duals (reduced costs) not available. Check that the model was properly solved and no integer variables are present.") +""" + getdual(v::Variable) + +Get the reduced cost of this variable in the solution. Similar behavior to `getvalue` for indexable variables. +""" function getdual(v::Variable) if length(v.m.redCosts) < MathProgBase.numvar(v.m) getdualwarn(v) @@ -547,11 +619,22 @@ function getdual(v::Variable) end const var_cats = [:Cont, :Int, :Bin, :SemiCont, :SemiInt] + +""" + setcategory(v::Variable, cat::Symbol) + +Set the variable category for `v` after construction. Possible categories are `:Cont, :Int, :Bin, :SemiCont, :SemiInt`. +""" function setcategory(v::Variable, cat::Symbol) cat in var_cats || error("Unrecognized variable category $cat. Should be one of:\n $var_cats") v.m.colCat[v.col] = cat end +""" + getcategory(v::Variable) + +Get the variable category for `v` +""" getcategory(v::Variable) = v.m.colCat[v.col] Base.zero(::Type{Variable}) = AffExpr(Variable[],Float64[],0.0) @@ -657,6 +740,10 @@ _getDual(c::LinConstrRef) = c.m.linconstrDuals[c.idx] getdualwarn{T<:Union{ConstraintRef, Int}}(::T) = warn("Dual solution not available. Check that the model was properly solved and no integer variables are present.") +""" + getdual(c::LinConstrRef) + +""" function getdual(c::LinConstrRef) if length(c.m.linconstrDuals) != MathProgBase.numlinconstr(c.m) getdualwarn(c) @@ -747,6 +834,11 @@ function getconicdualaux(m::Model, idx::Int, issdp::Bool) end end +""" + getdual(c::ConstraintRef{Model,SOCConstraint}) + + +""" function getdual(c::ConstraintRef{Model,SOCConstraint}) getconicdualaux(c.m, c.idx, false) end @@ -786,6 +878,11 @@ end # Z ∈ S₊ y_k free ∀k # where "∈ S₊" only look at the diagonal and upper diagonal part. # In the last primal program, we have the variables Z = X + Xᵀ and a upper triangular matrix S such that X = Z + S - Sᵀ + +""" + getdual(c::ConstraintRef{Model,SDConstraint}) + +""" function getdual(c::ConstraintRef{Model,SDConstraint}) dual, symdual = getconicdualaux(c.m, c.idx, true) n = size(c.m.sdpconstr[c.idx].terms, 1) @@ -900,7 +997,13 @@ function registerobject(m::Model, name::Symbol, value, warnstring::String) return value end -# allow easy accessing of JuMP Variables and Constraints + +""" + Base.getindex(m::JuMP.Model, name::Symbol) + +To allow easy accessing of JuMP Variables and Constraints via `[]` syntax. +Returns the variable, or group of variables, or constraint, or group of constraints, of the given name which were added to the model. This errors if multiple variables or constraints share the same name. +""" function Base.getindex(m::JuMP.Model, name::Symbol) if !haskey(m.objDict, name) throw(KeyError("No object with name $name")) @@ -910,6 +1013,12 @@ function Base.getindex(m::JuMP.Model, name::Symbol) return m.objDict[name] end end + +""" + Base.setindex!(m::JuMP.Model, value, name::Symbol) + +stores the object `value` in the model `m` using so that it can be accessed via `getindex`. Can be called with `[]` syntax. +""" function Base.setindex!(m::JuMP.Model, value, name::Symbol) # if haskey(m.objDict, name) # warn("Overwriting the object $name stored in the model. Consider using anonymous variables and constraints instead") diff --git a/src/solvers.jl b/src/solvers.jl index 8a4f0334e08..fb31403e9a9 100644 --- a/src/solvers.jl +++ b/src/solvers.jl @@ -141,6 +141,14 @@ function fillConicDuals(m::Model) end +""" + solve(m::Model; suppress_warnings=false, + ignore_solve_hook=(m.solvehook===nothing), + relaxation=false, + kwargs...) + +solves the model using the selected solver (or a default for the problem class), and takes two optional arguments that are disabled by default. Setting `suppress_warnings` to `true` will suppress all JuMP-specific output (e.g. warnings about infeasibility and lack of dual information) but will not suppress solver output (which should be done by passing options to the solver). Setting `relaxation=true` solves the standard continuous relaxation for the model: that is, integrality is dropped, special ordered set constraints are not enforced, and semi-continuous and semi-integer variables with bounds `[l,u]` are replaced with bounds `[min(l,0),max(u,0)]` +""" function solve(m::Model; suppress_warnings=false, ignore_solve_hook=(m.solvehook===nothing), relaxation=false, @@ -291,8 +299,14 @@ function solve(m::Model; suppress_warnings=false, stat end -# Converts the JuMP Model into a MathProgBase model based on the -# traits of the model + +""" + build(m::Model; suppress_warnings=false, + relaxation=false, + traits=ProblemTraits(m,relaxation=relaxation)) + +builds the model in memory at the MathProgBase level without optimizing. +""" function build(m::Model; suppress_warnings=false, relaxation=false, traits=ProblemTraits(m,relaxation=relaxation)) if isa(m.solver, UnsetSolver) no_solver_error(traits) From 2b2534a42643bebc539f0539b177044e6dd7be51 Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Thu, 15 Jun 2017 00:38:37 -0400 Subject: [PATCH 05/10] inital migration of refexpr to docstrings --- docs/src/refexpr.md | 49 ++++++++++++++++++----------------------- docs/src/refvariable.md | 3 ++- src/JuMP.jl | 5 +++++ src/affexpr.jl | 28 +++++++++++++++++++++-- src/macros.jl | 45 +++++++++++++++++++++++++++++++++++++ src/norms.jl | 5 +++++ src/quadexpr.jl | 10 +++++++++ src/sos.jl | 10 +++++++++ 8 files changed, 124 insertions(+), 31 deletions(-) diff --git a/docs/src/refexpr.md b/docs/src/refexpr.md index 80145aaa010..fffc3d09fd8 100644 --- a/docs/src/refexpr.md +++ b/docs/src/refexpr.md @@ -1,3 +1,7 @@ +```@meta +CurrentModule = JuMP +``` + Expressions and Constraints =========================== @@ -20,9 +24,22 @@ The corresponding constraint is `QuadConstraint`, which is expected to be a conv Methods ------- -- `@constraint(m::Model, con)` - add linear or quadratic constraints. -- `@constraint(m::Model, ref, con)` - add groups of linear or quadratic constraints. See Constraint Reference section for details. -- `JuMP.addconstraint(m::Model, con)` - general way to add linear and quadratic constraints. +```@docs +addconstraint +@constraint +@expression +@SDconstraint +addSOS1 +addSOS2 +@LinearConstraint +@QuadConstraint +Base.push!{C,V}(aff::GenericAffExpr{C,V}, new_coeff::C, new_var::V) +Base.append!{C,V}(aff::GenericAffExpr{C,V}, other::GenericAffExpr{C,V}) +linearterms +getvalue(a::AffExpr) +getvalue(a::QuadExpr) +``` + - `@constraints` - add groups of constraints at once, in the same fashion as @constraint. The model must be the first argument, and multiple constraints can be added on multiple lines wrapped in a `begin ... end` block. For example: @constraints(m, begin @@ -30,33 +47,9 @@ Methods y - w <= 2 sum_to_one[i=1:3], z[i] + y == 1 end) - -- `@expression(m::Model, ref, expr)` - efficiently builds a linear, quadratic, or second-order cone expression but does not add to model immediately. Instead, returns the expression which can then be inserted in other constraints. For example: - - @expression(m, shared, sum(i*x[i] for i=1:5)) - @constraint(m, shared + y >= 5) - @constraint(m, shared + z <= 10) - -The `ref` accepts index sets in the same way as `@variable`, and those indices can be used in the construction of the expressions: - - @expression(m, expr[i=1:3], i*sum(x[j] for j=1:3)) - -Anonymous syntax is also supported: - - expr = @expression(m, [i=1:3], i*sum(x[j] for j=1:3)) - -- `@SDconstraint(m::Model, expr)` - adds a semidefinite constraint to the model `m`. The expression `expr` must be a square, two-dimensional array. -- `addSOS1(m::Model, coll::Vector{AffExpr})` - adds special ordered set constraint of type 1 (SOS1). Specify the set as a vector of weighted variables, e.g. `coll = [3x, y, 2z]`. Note that solvers expect the weights to be unique. See [here](http://lpsolve.sourceforge.net/5.5/SOS.htm) for more details. If there is no inherent weighting in your model, an SOS constraint is probably unnecessary. -- `addSOS2(m::Model, coll::Vector{AffExpr})` - adds special ordered set constraint of type 2 (SOS2). Specify the set as a vector of weighted variables, e.g. `coll = [3x, y, 2z]`. Note that solvers expect the weights to be unique. See [here](http://lpsolve.sourceforge.net/5.5/SOS.htm) for more details. -- `@LinearConstraint(expr)` - Constructs a `LinearConstraint` instance efficiently by parsing the `expr`. The same as `@constraint`, except it does not attach the constraint to any model. - `@LinearConstraints(expr)` - Constructs a vector of `LinearConstraint` objects. Similar to `@LinearConstraint`, except it accepts multiple constraints as input as long as they are separated by newlines. -- `@QuadConstraint(expr)` - Constructs a `QuadConstraint` instance efficiently by parsing the `expr`. The same as `@constraint`, except it does not attach the constraint to any model. - `@QuadConstraints(expr)` - Constructs a vector of `QuadConstraint` objects. Similar to `@QuadConstraint`, except it accepts multiple constraints as input as long as they are separated by newlines. -- `push!(aff::AffExpr, new_coeff::Float64, new_var::Variable)` - efficient way to grow an affine expression by one term. For example, to add `5x` to an existing expression `aff`, use `push!(aff, 5.0, x)`. This is significantly more efficient than `aff += 5.0*x`. -- `append!(aff::AffExpr, other::AffExpr)` - efficiently append the terms of an affine expression to an existing affine expression. For example, given `aff = 5.0*x` and `other = 7.0*y + 3.0*z`, we can grow `aff` using `append!(aff, other)` which results in `aff` equaling `5x + 7y + 3z`. This is significantly more efficient than using `aff += other`. -- `sum(affs::Array{AffExpr})` - efficiently sum an array of affine expressions. -- `getvalue(expr)` - evaluate an `AffExpr` or `QuadExpr`, given the current solution values. -- `linearterms{C,V}(aff::GenericAffExpr{C,V})` - provides an iterator over the `(a_i::C,x_i::V)` terms in affine expression ``\sum_i a_i x_i + b``. + Constraint References --------------------- diff --git a/docs/src/refvariable.md b/docs/src/refvariable.md index 57eaa902816..9b2074c4214 100644 --- a/docs/src/refvariable.md +++ b/docs/src/refvariable.md @@ -170,7 +170,8 @@ getcategory **Values** ```@docs -getvalue +getvalue(v::Variable) +getvalue(arr::Array{Variable}) setvalue getdual ``` diff --git a/src/JuMP.jl b/src/JuMP.jl index 720ded46cf6..f9745ef3d6b 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -707,6 +707,11 @@ function SDConstraint(lhs::AbstractMatrix, rhs::Number) SDConstraint(lhs) end +""" + addconstraint(m::Model, c::SDConstraint) + +Add a SD constraint to `Model m`. +""" function addconstraint(m::Model, c::SDConstraint) push!(m.sdpconstr,c) m.internalModelLoaded = false diff --git a/src/affexpr.jl b/src/affexpr.jl index 8a33547dce6..63621a0432d 100644 --- a/src/affexpr.jl +++ b/src/affexpr.jl @@ -37,20 +37,34 @@ immutable LinearTermIterator{GAE<:GenericAffExpr} aff::GAE end +""" + linearterms(aff::GenericAffExpr) + +Provides an iterator over the `(a_i::C,x_i::V)` terms in affine expression ``\sum_i a_i x_i + b``. +""" linearterms(aff::GenericAffExpr) = LinearTermIterator(aff) Base.start(lti::LinearTermIterator) = 1 Base.done( lti::LinearTermIterator, state::Int) = state > length(lti.aff.vars) Base.next( lti::LinearTermIterator, state::Int) = ((lti.aff.coeffs[state], lti.aff.vars[state]), state+1) -# More efficient ways to grow an affine expression -# Add a single term to an affine expression +""" + Base.push!{C,V}(aff::GenericAffExpr{C,V}, new_coeff::C, new_var::V) + +An efficient way to grow an affine expression by one term. For example, to add `5x` to an existing expression `aff`, use `push!(aff, 5.0, x)`. This is significantly more efficient than `aff += 5.0*x`. +""" function Base.push!{C,V}(aff::GenericAffExpr{C,V}, new_coeff::C, new_var::V) push!(aff.coeffs, new_coeff) push!(aff.vars, new_var) aff end + # Add an affine expression to an existing affine expression +""" + Base.append!{C,V}(aff::GenericAffExpr{C,V}, other::GenericAffExpr{C,V}) + +Efficiently append the terms of an affine expression to an existing affine expression. For example, given `aff = 5.0*x` and `other = 7.0*y + 3.0*z`, we can grow `aff` using `append!(aff, other)` which results in `aff` equaling `5x + 7y + 3z`. This is significantly more efficient than using `aff += other`. +""" function Base.append!{C,V}(aff::GenericAffExpr{C,V}, other::GenericAffExpr{C,V}) append!(aff.vars, other.vars) append!(aff.coeffs, other.coeffs) @@ -119,6 +133,11 @@ function Base.copy(a::AffExpr, new_model::Model) AffExpr(copy(a.vars, new_model), copy(a.coeffs), a.constant) end +""" + getvalue(a::AffExpr) + +Evaluate an `AffExpr` given the current solution values. +""" function getvalue(a::AffExpr) ret = a.constant for it in 1:length(a.vars) @@ -171,6 +190,11 @@ function Base.copy(c::LinearConstraint, new_model::Model) return LinearConstraint(copy(c.terms, new_model), c.lb, c.ub) end +""" + addconstraint(m::Model, c::LinearConstraint) + +Add a linear constraint to `Model m`. +""" function addconstraint(m::Model, c::LinearConstraint) push!(m.linconstr,c) if m.internalModelLoaded diff --git a/src/macros.jl b/src/macros.jl index 3c9476c8937..dce2ba30500 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -348,6 +348,16 @@ constructconstraint!(x::AbstractMatrix, ::PSDCone) = SDConstraint(x) constraint_error(args, str) = error("In @constraint($(join(args,","))): ", str) +""" + @constraint(m::Model, con) + +add linear or quadratic constraints. + + @constraint(m::Model, ref, con) + +add groups of linear or quadratic constraints. + +""" macro constraint(args...) # Pick out keyword arguments if isexpr(args[1],:parameters) # these come if using a semicolon @@ -493,6 +503,12 @@ macro constraint(args...) end) end + +""" + @SDconstraint(m, x) + +Adds a semidefinite constraint to the `Model m`. The expression `x` must be a square, two-dimensional array. +""" macro SDconstraint(m, x) m = esc(m) @@ -529,6 +545,12 @@ macro SDconstraint(m, x) end) end + +""" + @LinearConstraint(x) + +Constructs a `LinearConstraint` instance efficiently by parsing the `x`. The same as `@constraint`, except it does not attach the constraint to any model. +""" macro LinearConstraint(x) (x.head == :block) && error("Code block passed as constraint. Perhaps you meant to use @LinearConstraints instead?") @@ -577,6 +599,11 @@ macro LinearConstraint(x) end end +""" + @QuadConstraint(x) + +Constructs a `QuadConstraint` instance efficiently by parsing the `x`. The same as `@constraint`, except it does not attach the constraint to any model. +""" macro QuadConstraint(x) (x.head == :block) && error("Code block passed as constraint. Perhaps you meant to use @QuadConstraints instead?") @@ -735,6 +762,24 @@ macro Expression(x) end +""" + @expression(args...) + +efficiently builds a linear, quadratic, or second-order cone expression but does not add to model immediately. Instead, returns the expression which can then be inserted in other constraints. For example: + + @expression(m, shared, sum(i*x[i] for i=1:5)) + @constraint(m, shared + y >= 5) + @constraint(m, shared + z <= 10) + +The `ref` accepts index sets in the same way as `@variable`, and those indices can be used in the construction of the expressions: + + @expression(m, expr[i=1:3], i*sum(x[j] for j=1:3)) + +Anonymous syntax is also supported: + + expr = @expression(m, [i=1:3], i*sum(x[j] for j=1:3)) + +""" macro expression(args...) if length(args) == 3 m = esc(args[1]) diff --git a/src/norms.jl b/src/norms.jl index 7d22614e563..4037f3c7d94 100644 --- a/src/norms.jl +++ b/src/norms.jl @@ -121,6 +121,11 @@ end # Alias for the AffExpr case const SOCConstraint = GenericSOCConstraint{SOCExpr} +""" + addconstraint(m::Model, c::SOCConstraint) + +Add a SOC constraint to `Model m`. +""" function addconstraint(m::Model, c::SOCConstraint) push!(m.socconstr,c) m.internalModelLoaded = false diff --git a/src/quadexpr.jl b/src/quadexpr.jl index 5790d204806..4bad42b647c 100644 --- a/src/quadexpr.jl +++ b/src/quadexpr.jl @@ -88,6 +88,11 @@ function Base.copy(q::QuadExpr, new_model::Model) copy(q.qcoeffs), copy(q.aff, new_model)) end +""" + getvalue(a::QuadExpr) + +Evaluate a `QuadExpr` given the current solution values. +""" function getvalue(a::QuadExpr) ret = getvalue(a.aff) for it in 1:length(a.qvars1) @@ -117,6 +122,11 @@ function Base.copy(c::QuadConstraint, new_model::Model) return QuadConstraint(copy(c.terms, new_model), c.sense) end +""" + addconstraint(m::Model, c::QuadConstraint) + +Add a quadratic constraint to `Model m`. +""" function addconstraint(m::Model, c::QuadConstraint) push!(m.quadconstr,c) if m.internalModelLoaded diff --git a/src/sos.jl b/src/sos.jl index 2fe82a678e1..910539a348f 100644 --- a/src/sos.jl +++ b/src/sos.jl @@ -43,6 +43,11 @@ end addSOS1(m::Model, coll) = addSOS1(m, convert(Vector{AffExpr}, coll)) +""" + addSOS1(m::Model, coll::Vector{AffExpr}) + +Adds special ordered set constraint of type 1 (SOS1). Specify the set as a vector of weighted variables, e.g. `coll = [3x, y, 2z]`. Note that solvers expect the weights to be unique. See [here](http://lpsolve.sourceforge.net/5.5/SOS.htm) for more details. If there is no inherent weighting in your model, an SOS constraint is probably unnecessary. +""" function addSOS1(m::Model, coll::Vector{AffExpr}) vars, weight = constructSOS(m,coll) push!(m.sosconstr, SOSConstraint(vars, weight, :SOS1)) @@ -59,6 +64,11 @@ end addSOS2(m::Model, coll) = addSOS2(m, convert(Vector{AffExpr}, coll)) +""" + addSOS2(m::Model, coll::Vector{AffExpr}) + +Adds special ordered set constraint of type 2 (SOS2). Specify the set as a vector of weighted variables, e.g. `coll = [3x, y, 2z]`. Note that solvers expect the weights to be unique. See [here](http://lpsolve.sourceforge.net/5.5/SOS.htm) for more details. +""" function addSOS2(m::Model, coll::Vector{AffExpr}) vars, weight = constructSOS(m,coll) push!(m.sosconstr, SOSConstraint(vars, weight, :SOS2)) From d503837028cceeacbb5e303422737688d727038e Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Thu, 15 Jun 2017 09:21:16 -0400 Subject: [PATCH 06/10] adding doc strings for auto wrapped solver functions in refmodel --- docs/src/refmodel.md | 24 +++++++++++++--------- src/JuMP.jl | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/docs/src/refmodel.md b/docs/src/refmodel.md index 76bcfd74723..6e2a83cb957 100644 --- a/docs/src/refmodel.md +++ b/docs/src/refmodel.md @@ -49,15 +49,6 @@ Base.getindex(m::Model, name::Symbol) Base.setindex!(m::Model, value, name::Symbol) ``` -- `getsolvetime(m::Model)` - returns the solve time reported by the solver if it is implemented. -- `getnodecount(m::Model)` - returns the number of explored branch-and-bound nodes, if it is implemented. -- `getobjbound(m::Model)` - returns the best known bound on the optimal objective value. This is used, for example, when a branch-and-bound method is stopped before finishing. -- `getobjgap(m::Model)` - returns the final relative optimality gap as optimization terminated. That is, it returns ``\frac{|b-f|}{|f|}``, where *b* is the best bound and *f* is the best feasible objective value. -- `getrawsolver(m::Model)` - returns an object that may be used to access a solver-specific API. -- `getsimplexiter(m::Model)` - returns the cumulative number of simplex iterations during the optimization process. In particular, for a MIP it returns the total simplex iterations for all nodes. -- `getbarrieriter(m::Model)` - returns the cumulative number of barrier iterations during the optimization process. - - **Objective** ```@docs @@ -68,6 +59,21 @@ getobjectivevalue getobjectivebound ``` +**Solver** + +These functions are JuMP versions of the similarly named functions in MathProgBase. + +```@docs +getsolvetime +getnodecount +getobjbound +getobjgap +getrawsolver +getsimplexiter +getbarrieriter +``` + + **Output** ```@docs diff --git a/src/JuMP.jl b/src/JuMP.jl index f9745ef3d6b..16a7640853b 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -263,6 +263,53 @@ for f in MathProgBase.SolverInterface.methods_by_tag[:rewrap] eval(Expr(:export,f)) end + +# Doc strings for the auto-wrapped MPB functions above +# it would be preferable to problematically use the docstrings from MPB functions instead + +@doc """ + getsolvetime(m::Model) + +returns the solve time reported by the solver if it is implemented. +""" getsolvetime(m::Model) + +@doc """ + getnodecount(m::Model) + +returns the number of explored branch-and-bound nodes, if it is implemented. +""" getnodecount(m::Model) + +@doc """ + getobjbound(m::Model) + +returns the best known bound on the optimal objective value. This is used, for example, when a branch-and-bound method is stopped before finishing. +""" getobjbound(m::Model) + +@doc """ + getobjgap(m::Model) + +returns the final relative optimality gap as optimization terminated. That is, it returns ``\frac{|b-f|}{|f|}``, where *b* is the best bound and *f* is the best feasible objective value. +""" getobjgap(m::Model) + +@doc """ + getrawsolver(m::Model) + +returns an object that may be used to access a solver-specific API. +""" getrawsolver(m::Model) + +@doc """ + getsimplexiter(m::Model) + +returns the cumulative number of simplex iterations during the optimization process. In particular, for a MIP it returns the total simplex iterations for all nodes. +""" getsimplexiter(m::Model) + +@doc """ + getbarrieriter(m::Model) + +returns the cumulative number of barrier iterations during the optimization process. +""" getbarrieriter(m::Model) + + """ getobjective(m::Model) From 02e2863d36daaea326f0f0b9ba26305255ee5ef5 Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Thu, 15 Jun 2017 09:34:54 -0400 Subject: [PATCH 07/10] adding docstrings for macro plurals in refexpr --- docs/src/refexpr.md | 13 +++---------- src/macros.jl | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/docs/src/refexpr.md b/docs/src/refexpr.md index fffc3d09fd8..e1e65dccf80 100644 --- a/docs/src/refexpr.md +++ b/docs/src/refexpr.md @@ -27,12 +27,15 @@ Methods ```@docs addconstraint @constraint +@constraints @expression @SDconstraint addSOS1 addSOS2 @LinearConstraint +@LinearConstraints @QuadConstraint +@QuadConstraints Base.push!{C,V}(aff::GenericAffExpr{C,V}, new_coeff::C, new_var::V) Base.append!{C,V}(aff::GenericAffExpr{C,V}, other::GenericAffExpr{C,V}) linearterms @@ -40,16 +43,6 @@ getvalue(a::AffExpr) getvalue(a::QuadExpr) ``` -- `@constraints` - add groups of constraints at once, in the same fashion as @constraint. The model must be the first argument, and multiple constraints can be added on multiple lines wrapped in a `begin ... end` block. For example: - - @constraints(m, begin - x >= 1 - y - w <= 2 - sum_to_one[i=1:3], z[i] + y == 1 - end) -- `@LinearConstraints(expr)` - Constructs a vector of `LinearConstraint` objects. Similar to `@LinearConstraint`, except it accepts multiple constraints as input as long as they are separated by newlines. -- `@QuadConstraints(expr)` - Constructs a vector of `QuadConstraint` objects. Similar to `@QuadConstraint`, except it accepts multiple constraints as input as long as they are separated by newlines. - Constraint References --------------------- diff --git a/src/macros.jl b/src/macros.jl index dce2ba30500..406eab9ec1f 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -730,6 +730,37 @@ for (mac,sym) in [(:constraints, Symbol("@constraint")), end end + +# Doc strings for the auto-generated macro pluralizations +@doc """ + @constraints(m, args...) + +adds groups of constraints at once, in the same fashion as @constraint. The model must be the first argument, and multiple constraints can be added on multiple lines wrapped in a `begin ... end` block. For example: + + @constraints(m, begin + x >= 1 + y - w <= 2 + sum_to_one[i=1:3], z[i] + y == 1 + end) +""" :(@constraints) + +@doc """ + @LinearConstraints(m, args...) + +Constructs a vector of `LinearConstraint` objects. Similar to `@LinearConstraint`, except it accepts multiple constraints as input as long as they are separated by newlines. +""" :(@LinearConstraints) + +@doc """ + @QuadConstraints(m, args...) + +Constructs a vector of `QuadConstraint` objects. Similar to `@QuadConstraint`, except it accepts multiple constraints as input as long as they are separated by newlines. +""" :(@QuadConstraints) + + + + + + macro objective(m, args...) m = esc(m) if length(args) != 2 From 4f44871d695912c4fd61b4b5b21bbfd063fd34ed Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Thu, 15 Jun 2017 09:43:07 -0400 Subject: [PATCH 08/10] moving helper function notes out of library docs --- docs/src/refvariable.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/src/refvariable.md b/docs/src/refvariable.md index 9b2074c4214..431a5188909 100644 --- a/docs/src/refvariable.md +++ b/docs/src/refvariable.md @@ -162,11 +162,6 @@ setcategory getcategory ``` -**Helper functions** - -- `sum(x)` - Operates on arrays of variables, efficiently produces an affine expression. Available in macros. -- `dot(x, coeffs)` - Performs a generalized "dot product" for arrays of variables and coefficients up to three dimensions, or equivalently the sum of the elements of the Hadamard product. Available in macros, and also as `dot(coeffs, x)`. - **Values** ```@docs @@ -189,6 +184,15 @@ getname ``` +Helper functions +---------------- +The following built-in functions are overloaded to provide easy construction of expressions from variables, + +- `sum(x)` - Operates on arrays of variables, efficiently produces an affine expression. Available in macros. +- `dot(x, coeffs)` - Performs a generalized "dot product" for arrays of variables and coefficients up to three dimensions, or equivalently the sum of the elements of the Hadamard product. Available in macros, and also as `dot(coeffs, x)`. + + + Fixed variables --------------- From 7a087ce2344598aee8ff76e1e70fc6d18fbcd1dd Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Thu, 15 Jun 2017 09:53:04 -0400 Subject: [PATCH 09/10] fixing rendering of latex in docstrings --- src/JuMP.jl | 2 +- src/affexpr.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JuMP.jl b/src/JuMP.jl index 16a7640853b..49996e8e61d 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -288,7 +288,7 @@ returns the best known bound on the optimal objective value. This is used, for e @doc """ getobjgap(m::Model) -returns the final relative optimality gap as optimization terminated. That is, it returns ``\frac{|b-f|}{|f|}``, where *b* is the best bound and *f* is the best feasible objective value. +returns the final relative optimality gap as optimization terminated. That is, it returns ``\\frac{|b-f|}{|f|}``, where *b* is the best bound and *f* is the best feasible objective value. """ getobjgap(m::Model) @doc """ diff --git a/src/affexpr.jl b/src/affexpr.jl index 63621a0432d..85332b97ea9 100644 --- a/src/affexpr.jl +++ b/src/affexpr.jl @@ -40,7 +40,7 @@ end """ linearterms(aff::GenericAffExpr) -Provides an iterator over the `(a_i::C,x_i::V)` terms in affine expression ``\sum_i a_i x_i + b``. +Provides an iterator over the `(a_i::C,x_i::V)` terms in affine expression ``\\sum_i a_i x_i + b``. """ linearterms(aff::GenericAffExpr) = LinearTermIterator(aff) From c0073ea4a074cf60f0eac22a4ce46d194db81618 Mon Sep 17 00:00:00 2001 From: ccoffrin Date: Thu, 15 Jun 2017 10:10:50 -0400 Subject: [PATCH 10/10] adding julia code blocks in @expression docstring --- src/macros.jl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/macros.jl b/src/macros.jl index 406eab9ec1f..15f02d37a08 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -798,18 +798,23 @@ end efficiently builds a linear, quadratic, or second-order cone expression but does not add to model immediately. Instead, returns the expression which can then be inserted in other constraints. For example: - @expression(m, shared, sum(i*x[i] for i=1:5)) - @constraint(m, shared + y >= 5) - @constraint(m, shared + z <= 10) +```julia +@expression(m, shared, sum(i*x[i] for i=1:5)) +@constraint(m, shared + y >= 5) +@constraint(m, shared + z <= 10) +``` The `ref` accepts index sets in the same way as `@variable`, and those indices can be used in the construction of the expressions: - @expression(m, expr[i=1:3], i*sum(x[j] for j=1:3)) +```julia +@expression(m, expr[i=1:3], i*sum(x[j] for j=1:3)) +``` Anonymous syntax is also supported: - expr = @expression(m, [i=1:3], i*sum(x[j] for j=1:3)) - +```julia +expr = @expression(m, [i=1:3], i*sum(x[j] for j=1:3)) +``` """ macro expression(args...) if length(args) == 3