# Extending JuMP

## Extending MOI

### Adding a bridge

See the bridge section in the MOI manual.

`JuMP.add_bridge`

— Function.```
add_bridge(model::Model,
BridgeType::Type{<:MOI.Bridges.AbstractBridge})
```

Add `BridgeType`

to the list of bridges that can be used to transform unsupported constraints into an equivalent formulation using only constraints supported by the optimizer.

`JuMP.BridgeableConstraint`

— Type.```
struct BridgeableConstraint{C, B} <: AbstractConstraint
constraint::C
bridge_type::B
end
```

Constraint `constraint`

that can be bridged by the bridge of type `bridge_type`

. Adding this constraint to a model is equivalent to

```
add_bridge(model, bridge_type)
add_constraint(model, constraint)
```

**Examples**

Given a new scalar set type `CustomSet`

with a bridge `CustomBridge`

that can bridge `F`

-in-`CustomSet`

constraints, when the user does

```
model = Model()
@variable(model, x)
@constraint(model, x + 1 in CustomSet())
optimize!(model)
```

with an optimizer that does not support `F`

-in-`CustomSet`

constraints, the constraint will not be bridge unless he manually calls `add_bridge(model, CustomBridge)`

. In order to automatically add the `CustomBridge`

to any model to which an `F`

-in-`CustomSet`

is added, simply add the following method:

```
function JuMP.build_constraint(_error::Function, func::AbstractJuMPScalar,
set::CustomSet)
constraint = ScalarConstraint(func, set)
return JuMP.BridgeableConstraint(constraint, CustomBridge)
end
```

**Note**

JuMP extensions should extend `JuMP.build_constraint`

only if they also defined `CustomSet`

, for three reasons:

- It is problematic if multiple extensions overload the same JuMP method.
- A missing method will not inform the users that they forgot to load the extension module defining the
`build_constraint`

method. - Defining a method where neither the function nor any of the argument types are defined in the package is called
*type piracy*and is discouraged in the Julia style guide.

```

## Extending JuMP macros

In order to provide a convenient syntax for the user to create variables, constraints or set the objective of a JuMP extension, it might be required to use macros similar to `@variable`

, `@constraint`

and `@objective`

. It is recommended to first check whether it is possible to extend one of these three macros before creating a new one so as to leverage all their features and provide a more consistent interface to the user.

### Extending the `@constraint`

macro

The `@constraint`

macro always calls the same three functions:

`parse_constraint`

: is called at parsing time, it parses the constraint expression and returns a`build_constraint`

call expression;`build_constraint`

: given the functions and sets involved in the constraints, it returns a`AbstractConstraint`

;`add_constraint`

: given the model, the`AbstractConstraint`

constructed in`build_constraint`

and the constraint name, it stores them in the model and returns a`ConstraintRef`

.

Adding methods to these functions is the recommended way to extend the `@constraint`

macro.

#### Adding `parse_constraint`

methods

`JuMP.sense_to_set`

— Function.`sense_to_set(_error::Function, ::Val{sense_symbol})`

Converts a sense symbol to a set `set`

such that `@constraint(model, func sense_symbol 0) is equivalent to`

@constraint(model, func in set)`for any`

func::AbstractJuMPScalar`.

**Example**

Once a custom set is defined you can directly create a JuMP constraint with it:

```
julia> struct CustomSet{T} <: MOI.AbstractScalarSet
value::T
end
julia> model = Model();
julia> @variable(model, x)
x
julia> cref = @constraint(model, x in CustomSet(1.0))
x ∈ CustomSet{Float64}(1.0)
```

However, there might be an appropriate sign that could be used in order to provide a more convenient syntax:

```
julia> JuMP.sense_to_set(::Function, ::Val{:⊰}) = CustomSet(0.0)
julia> MOIU.shift_constant(set::CustomSet, value) = CustomSet(set.value + value)
julia> cref = @constraint(model, x ⊰ 1)
x ∈ CustomSet{Float64}(1.0)
```

Note that the whole function is first moved to the right-hand side, then the sign is transformed into a set with zero constant and finally the constant is moved to the set with `MOIU.shift_constant`

.

#### Adding `build_constraint`

methods

There is typically two choices when creating a `build_constraint`

method, either return an `AbstractConstraint`

already supported by the model, i.e. `ScalarConstraint`

or `VectorConstraint`

, or a custom `AbstractConstraint`

with a corresponding `add_constraint`

method (see Adding `add_constraint`

methods).

`JuMP.build_constraint`

— Function.```
function build_constraint(_error::Function, Q::Symmetric{V, M},
::PSDCone) where {V <: AbstractJuMPScalar,
M <: AbstractMatrix{V}}
```

Return a `VectorConstraint`

of shape `SymmetricMatrixShape`

constraining the matrix `Q`

to be positive semidefinite.

This function is used by the `@variable`

macro to create a symmetric semidefinite matrix of variables and by the `@constraint`

macros as follows:

`@constraint(model, Symmetric(Q) in PSDCone())`

The form above is usually used when the entries of `Q`

are affine or quadratic expressions but it can also be used when the entries are variables to get the reference of the semidefinite constraint, e.g.,

```
@variable model Q[1:2,1:2] Symmetric
# The type of `Q` is `Symmetric{VariableRef, Matrix{VariableRef}}`
var_psd = @constraint model Q in PSDCone()
# The `var_psd` variable contains a reference to the constraint
```

```
function build_constraint(_error::Function,
Q::AbstractMatrix{<:AbstractJuMPScalar},
::PSDCone)
```

Return a `VectorConstraint`

of shape `SquareMatrixShape`

constraining the matrix `Q`

to be symmetric and positive semidefinite.

This function is used by the `@constraint`

and `@SDconstraint`

macros as follows:

```
@constraint(model, Q in PSDCone())
@SDconstraint(model, P ⪰ Q)
```

The `@constraint`

call above is usually used when the entries of `Q`

are affine or quadratic expressions but it can also be used when the entries are variables to get the reference of the semidefinite constraint, e.g.,

```
@variable model Q[1:2,1:2]
# The type of `Q` is `Matrix{VariableRef}`
var_psd = @constraint model Q in PSDCone()
# The `var_psd` variable contains a reference to the constraint
```

##### Shapes

Shapes allow vector constraints, which are represented as flat vectors in MOI, to retain a matrix shape at the JuMP level. There is a `shape`

field in `VectorConstraint`

that can be set in `build_constraint`

and that is used to reshape the result computed in `value`

and `dual`

.

`JuMP.AbstractShape`

— Type.`AbstractShape`

Abstract vectorizable shape. Given a flat vector form of an object of shape `shape`

, the original object can be obtained by `reshape_vector`

.

`JuMP.shape`

— Function.`shape(c::AbstractConstraint)::AbstractShape`

Return the shape of the constraint `c`

.

`JuMP.reshape_vector`

— Function.`reshape_vector(vectorized_form::Vector, shape::AbstractShape)`

Return an object in its original shape `shape`

given its vectorized form `vectorized_form`

.

**Examples**

Given a `SymmetricMatrixShape`

of vectorized form `[1, 2, 3]`

, the following code returns the matrix `Symmetric(Matrix[1 2; 2 3])`

:

```
julia> reshape_vector([1, 2, 3], SymmetricMatrixShape(2))
2×2 LinearAlgebra.Symmetric{Int64,Array{Int64,2}}:
1 2
2 3
```

`JuMP.reshape_set`

— Function.`reshape_set(vectorized_set::MOI.AbstractSet, shape::AbstractShape)`

Return a set in its original shape `shape`

given its vectorized form `vectorized_form`

.

**Examples**

Given a `SymmetricMatrixShape`

of vectorized form `[1, 2, 3] in MOI.PositiveSemidefinieConeTriangle(2)`

, the following code returns the set of the original constraint `Symmetric(Matrix[1 2; 2 3]) in PSDCone()`

:

```
julia> reshape_set(MOI.PositiveSemidefiniteConeTriangle(2), SymmetricMatrixShape(2))
PSDCone()
```

`JuMP.dual_shape`

— Function.`dual_shape(shape::AbstractShape)::AbstractShape`

Returns the shape of the dual space of the space of objects of shape `shape`

. By default, the `dual_shape`

of a shape is itself. See the examples section below for an example for which this is not the case.

**Examples**

Consider polynomial constraints for which the dual is moment constraints and moment constraints for which the dual is polynomial constraints. Shapes for polynomials can be defined as follows:

```
struct Polynomial
coefficients::Vector{Float64}
monomials::Vector{Monomial}
end
struct PolynomialShape <: AbstractShape
monomials::Vector{Monomial}
end
JuMP.reshape_vector(x::Vector, shape::PolynomialShape) = Polynomial(x, shape.monomials)
```

and a shape for moments can be defined as follows:

```
struct Moments
coefficients::Vector{Float64}
monomials::Vector{Monomial}
end
struct MomentsShape <: AbstractShape
monomials::Vector{Monomial}
end
JuMP.reshape_vector(x::Vector, shape::MomentsShape) = Moments(x, shape.monomials)
```

The `dual_shape`

allows to define the shape of the dual of polynomial and moment constraints:

```
dual_shape(shape::PolynomialShape) = MomentsShape(shape.monomials)
dual_shape(shape::MomentsShape) = PolynomialShape(shape.monomials)
```

`JuMP.ScalarShape`

— Type.`ScalarShape`

Shape of scalar constraints.

`JuMP.VectorShape`

— Type.`VectorShape`

Vector for which the vectorized form corresponds exactly to the vector given.

`JuMP.SquareMatrixShape`

— Type.`SquareMatrixShape`

Shape object for a square matrix of `side_dimension`

rows and columns. The vectorized form contains the entries of the the matrix given column by column (or equivalently, the entries of the lower-left triangular part given row by row).

`JuMP.SymmetricMatrixShape`

— Type.`SymmetricMatrixShape`

Shape object for a symmetric square matrix of `side_dimension`

rows and columns. The vectorized form contains the entries of the upper-right triangular part of the matrix given column by column (or equivalently, the entries of the lower-left triangular part given row by row).

#### Adding `add_constraint`

methods

`JuMP.add_constraint`

— Function.`add_constraint(model::Model, c::AbstractConstraint, name::String="")`

Add a constraint `c`

to `Model model`

and sets its name.