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

[docs] clean up and clarify macro docstrings #3617

Merged
merged 1 commit into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 34 additions & 54 deletions src/macros/@constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,59 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

"""
@constraint(model::GenericModel, expr, kwargs...)
@constraint(model, expr, args...; kwargs...)
@constraint(model, [index_sets...], expr, args...; kwargs...)
@constraint(model, name, expr, args...; kwargs...)
@constraint(model, name[index_sets...], expr, args...; kwargs...)

Add a constraint described by the expression `expr`.

@constraint(model::GenericModel, ref[i=..., j=..., ...], expr, kwargs...)
The `name` argument is optional. If index sets are passed, a container is built
and the connstraint may depend on the indices of the index ssets.

Add a group of constraints described by the expression `expr` parametrized by
`i`, `j`, ...
The expression `expr` may be one of following forms:

The expression `expr` can either be
* `func in set`, constraining the function `func` to belong to the set `set`,
which is either a [`MOI.AbstractSet`](@ref) or one of the JuMP shortcuts like
[`SecondOrderCone`](@ref) or [`PSDCone`](@ref)

* of the form `func in set` constraining the function `func` to belong to the
set `set` which is either a [`MOI.AbstractSet`](@ref)
or one of the JuMP shortcuts [`SecondOrderCone`](@ref),
[`RotatedSecondOrderCone`](@ref) and [`PSDCone`](@ref), e.g.
`@constraint(model, [1, x-1, y-2] in SecondOrderCone())` constrains the norm
of `[x-1, y-2]` be less than 1;
* `a <op> b`, where `<op>` is one of `==`, `≥`, `>=`, `≤`, `<=`

* of the form `a sign b`, where `sign` is one of `==`, `≥`, `>=`, `≤` and
`<=` building the single constraint enforcing the comparison to hold for the
expression `a` and `b`, e.g. `@constraint(model, x^2 + y^2 == 1)` constrains
`x` and `y` to lie on the unit circle;
* `l <= f <= u` or `u >= f >= l`, constraining the expression `f` to lie
between `l` and `u`

* of the form `a ≤ b ≤ c` or `a ≥ b ≥ c` (where `≤` and `<=` (resp. `≥` and
`>=`) can be used interchangeably) constraining the paired the expression
`b` to lie between `a` and `c`;
* `f(x) ⟂ x`, which defines a complementarity constraint

* of the forms `@constraint(m, a .sign b)` or
`@constraint(m, a .sign b .sign c)` which broadcast the constraint creation to
each element of the vectors.
* `z --> {expr}`, which defines an indicator constraint that activates
when `z` is `1`

The recognized keyword arguments in `kwargs` are the following:
* `!z --> {expr}`, which defines an indicator constraint that activates
when `z` is `0`

* `base_name`: Sets the name prefix used to generate constraint names. It
corresponds to the constraint name for scalar constraints, otherwise, the
constraint names are set to `base_name[...]` for each index `...` of the axes
`axes`.
* `z <--> {expr}`, which defines a reified constraint

* `container`: Specify the container type.
* `expr := rhs`, which defines a Boolean equality constraint

* `set_string_name::Bool = true`: control whether to set the
[`MOI.ConstraintName`](@ref) attribute. Passing `set_string_name = false` can
improve performance.
Broadcasted comparison operators like `.==` are also supported for the case when
the left- and right-hand sides of the comparison operator are arrays.

## Note for extending the constraint macro
JuMP extensions may additionally provide support for constraint expressions
which are not listed here.

Each constraint will be created using
`add_constraint(m, build_constraint(error_fn, func, set))` where
## Keyword arguments

* `error_fn` is an error function showing the constraint call in addition to the
error message given as argument,
* `base_name`: sets the name prefix used to generate constraint names. It
corresponds to the constraint name for scalar constraints, otherwise, the
constraint names are set to `base_name[...]` for each index `...`.

* `func` is the expression that is constrained
* and `set` is the set in which it is constrained to belong.
* `container = :Auto`: force the container type by passing `container = Array`,
`container = DenseAxisArray`, `container = SparseAxisArray`, or any another
container type which is supported by a JuMP extension.

For `expr` of the first type (i.e. `@constraint(m, func in set)`), `func` and
`set` are passed unchanged to `build_constraint` but for the other types, they
are determined from the expressions and signs. For instance,
`@constraint(m, x^2 + y^2 == 1)` is transformed into
`add_constraint(m, build_constraint(error_fn, x^2 + y^2, MOI.EqualTo(1.0)))`.
* `set_string_name::Bool = true`: control whether to set the [`MOI.ConstraintName`](@ref)
attribute. Passing `set_string_name = false` can improve performance.

To extend JuMP to accept new constraints of this form, it is necessary to add
the corresponding methods to `build_constraint`. Note that this will likely mean
that either `func` or `set` will be some custom type, rather than e.g. a
`Symbol`, since we will likely want to dispatch on the type of the function or
set appearing in the constraint.

For extensions that need to create constraints with more information than just
`func` and `set`, an additional positional argument can be specified to
`@constraint` that will then be passed on `build_constraint`. Hence, we can
enable this syntax by defining extensions of
`build_constraint(error_fn, func, set, my_arg; kwargs...)`. This produces the
user syntax: `@constraint(model, ref[...], expr, my_arg, kwargs...)`.
Other keyword arguments may be supported by JuMP extensions.
"""
macro constraint(input_args...)
error_fn(str...) = _macro_error(:constraint, input_args, __source__, str...)
Expand Down
47 changes: 29 additions & 18 deletions src/macros/@expression.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,44 @@
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

"""
@expression(args...)
@expression(model::GenericModel, expression)
@expression(model::GenericModel, [index_sets...], expression)
@expression(model::GenericModel, name, expression)
@expression(model::GenericModel, name[index_sets...], expression)

Efficiently builds a linear or quadratic expression but does not add to model
immediately. Instead, returns the expression which can then be inserted in other
constraints.
Efficiently builds and returns an expression.

The `name` argument is optional. If index sets are passed, a container is built
and the expression may depend on the indices of the index ssets.

## Keyword arguments

* `container = :Auto`: force the container type by passing `container = Array`,
`container = DenseAxisArray`, `container = SparseAxisArray`, or any another
container type which is supported by a JuMP extension.

## Example

```jldoctest expression_docstring
```jldoctest
julia> model = Model();

julia> @variable(model, x[1:5]);

julia> @variable(model, y);

julia> @variable(model, z);

julia> @expression(model, shared, sum(i * x[i] for i in 1:5))
x[1] + 2 x[2] + 3 x[3] + 4 x[4] + 5 x[5]

julia> @constraint(model, shared + y >= 5)
x[1] + 2 x[2] + 3 x[3] + 4 x[4] + 5 x[5] + y ≥ 5

julia> @constraint(model, shared + z <= 10)
x[1] + 2 x[2] + 3 x[3] + 4 x[4] + 5 x[5] + z ≤ 10
julia> shared
x[1] + 2 x[2] + 3 x[3] + 4 x[4] + 5 x[5]
```

The `ref` accepts index sets in the same way as `@variable`, and those indices
can be used in the construction of the expressions:
In the same way as [`@variable`](@ref), the second argument may define index
sets, and those indices can be used in the construction of the expressions:

```jldoctest
julia> model = Model();

julia> @variable(model, x[1:3]);

```jldoctest expression_docstring
julia> @expression(model, expr[i = 1:3], i * sum(x[j] for j in 1:3))
3-element Vector{AffExpr}:
x[1] + x[2] + x[3]
Expand All @@ -44,7 +51,11 @@ julia> @expression(model, expr[i = 1:3], i * sum(x[j] for j in 1:3))

Anonymous syntax is also supported:

```jldoctest expression_docstring
```jldoctest
julia> model = Model();

julia> @variable(model, x[1:3]);

julia> expr = @expression(model, [i in 1:3], i * sum(x[j] for j in 1:3))
3-element Vector{AffExpr}:
x[1] + x[2] + x[3]
Expand Down
34 changes: 21 additions & 13 deletions src/macros/@objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@
"""
@objective(model::GenericModel, sense, func)

Set the objective sense to `sense` and objective function to `func`. The
objective sense can be either `Min`, `Max`, `MOI.MIN_SENSE`, `MOI.MAX_SENSE` or
`MOI.FEASIBILITY_SENSE`; see [`MOI.ObjectiveSense`](@ref).
Set the objective sense to `sense` and objective function to `func`.

In order to set the sense programmatically, i.e., when `sense` is a Julia
variable whose value is the sense, one of the three `MOI.ObjectiveSense` values
should be used.
The objective sense can be either `Min`, `Max`, `MOI.MIN_SENSE`, `MOI.MAX_SENSE`
or `MOI.FEASIBILITY_SENSE`. In order to set the sense programmatically, that is,
when `sense` is a variable whose value is the sense, one of the three
[`MOI.OptimizationSense`](@ref) values must be used.

## Example

To minimize the value of the variable `x`, do as follows:
```jldoctest @objective
Minimize the value of the variable `x`, do:
```jldoctest
julia> model = Model();

julia> @variable(model, x)
Expand All @@ -27,15 +26,24 @@ julia> @objective(model, Min, x)
x
```

To maximize the value of the affine expression `2x - 1`, do as follows:
```jldoctest @objective
Maximize the value of the affine expression `2x - 1`:
```jldoctest
julia> model = Model();

julia> @variable(model, x)
x

julia> @objective(model, Max, 2x - 1)
2 x - 1
```

To set a quadratic objective and set the objective sense programmatically, do
as follows:
```jldoctest @objective
Set the objective sense programmatically:
```jldoctest
julia> model = Model();

julia> @variable(model, x)
x

julia> sense = MIN_SENSE
MIN_SENSE::OptimizationSense = 0

Expand Down
Loading