Skip to content

Commit 8356e1c

Browse files
authored
[docs] expand MatrixOfConstraints example (#2474)
1 parent 4b19d2d commit 8356e1c

File tree

1 file changed

+165
-42
lines changed

1 file changed

+165
-42
lines changed

Diff for: docs/src/submodules/Utilities/overview.md

+165-42
Original file line numberDiff line numberDiff line change
@@ -379,34 +379,156 @@ julia> f
379379
## Utilities.MatrixOfConstraints
380380

381381
The constraints of [`Utilities.Model`](@ref) are stored as a vector of tuples
382-
of function and set in a `Utilities.VectorOfConstraints`. Other representations
383-
can be used by parameterizing the type [`Utilities.GenericModel`](@ref)
384-
(resp. [`Utilities.GenericOptimizer`](@ref)). For instance, if all
385-
non-`VariableIndex` constraints are affine, the coefficients of all the
386-
constraints can be stored in a single sparse matrix using
387-
[`Utilities.MatrixOfConstraints`](@ref). The constraints storage can even be
388-
customized up to a point where it exactly matches the storage of the solver of
389-
interest, in which case [`copy_to`](@ref) can be implemented for the solver by
390-
calling [`copy_to`](@ref) to this custom model.
391-
392-
For instance, [Clp](https://github.com/jump-dev/Clp.jl) defines the following
382+
of function and set in a [`Utilities.VectorOfConstraints`](@ref).
383+
384+
Other representations can be used by parameterizing the type
385+
[`Utilities.GenericModel`](@ref) (resp. [`Utilities.GenericOptimizer`](@ref)).
386+
387+
For example, if all non-`VariableIndex` constraints are affine, the coefficients
388+
of all the constraints can be stored in a single sparse matrix using
389+
[`Utilities.MatrixOfConstraints`](@ref).
390+
391+
The constraints storage can even be customized up to a point where it exactly
392+
matches the storage of the solver of interest, in which case [`copy_to`](@ref)
393+
can be implemented for the solver by calling [`copy_to`](@ref) to this custom
394+
model.
395+
396+
For example, [Clp.jl](https://github.com/jump-dev/Clp.jl) defines the following
393397
model:
394-
```julia
395-
MOI.Utilities.@product_of_scalar_sets(LP, MOI.EqualTo{T}, MOI.LessThan{T}, MOI.GreaterThan{T})
396-
const Model = MOI.Utilities.GenericModel{
397-
Float64,
398-
MOI.Utilities.MatrixOfConstraints{
399-
Float64,
400-
MOI.Utilities.MutableSparseMatrixCSC{Float64,Cint,MOI.Utilities.ZeroBasedIndexing},
401-
MOI.Utilities.Hyperrectangle{Float64},
402-
LP{Float64},
403-
},
404-
}
398+
```jldoctest matrixofconstraints
399+
julia> MOI.Utilities.@product_of_sets(
400+
SupportedSets,
401+
MOI.EqualTo{T},
402+
MOI.LessThan{T},
403+
MOI.GreaterThan{T},
404+
MOI.Interval{T},
405+
);
406+
407+
julia> const OptimizerCache = MOI.Utilities.GenericModel{
408+
Float64,
409+
MOI.Utilities.ObjectiveContainer{Float64},
410+
MOI.Utilities.VariablesContainer{Float64},
411+
MOI.Utilities.MatrixOfConstraints{
412+
Float64,
413+
MOI.Utilities.MutableSparseMatrixCSC{
414+
# The data type of the coefficients
415+
Float64,
416+
# The data type of the variable indices
417+
Cint,
418+
# Can also be MOI.Utilities.OneBasedIndexing
419+
MOI.Utilities.ZeroBasedIndexing,
420+
},
421+
MOI.Utilities.Hyperrectangle{Float64},
422+
SupportedSets{Float64},
423+
},
424+
};
425+
```
426+
Given the input model:
427+
```jldoctest matrixofconstraints
428+
julia> src = MOI.Utilities.Model{Float64}();
429+
430+
julia> MOI.Utilities.loadfromstring!(
431+
src,
432+
"""
433+
variables: x, y, z
434+
maxobjective: x + 2.0 * y + -3.1 * z
435+
x + y <= 1.0
436+
2.0 * y >= 3.0
437+
-4.0 * x + z == 5.0
438+
x in Interval(0.0, 1.0)
439+
y <= 10.0
440+
z == 5.0
441+
""",
442+
)
405443
```
406444

407-
The [`copy_to`](@ref) operation can now be implemented as follows:
445+
We can construct a new cached model and copy `src` to it:
446+
```jldoctest matrixofconstraints
447+
julia> dest = OptimizerCache();
448+
449+
julia> index_map = MOI.copy_to(dest, src);
450+
```
451+
452+
From `dest`, we can access the `A` matrix in sparse matrix form:
453+
```jldoctest matrixofconstraints
454+
julia> A = dest.constraints.coefficients;
455+
456+
julia> A.n
457+
3
458+
459+
julia> A.m
460+
3
461+
462+
julia> A.colptr
463+
4-element Vector{Int32}:
464+
0
465+
1
466+
3
467+
5
468+
469+
julia> A.rowval
470+
5-element Vector{Int32}:
471+
0
472+
1
473+
2
474+
0
475+
1
476+
477+
julia> A.nzval
478+
5-element Vector{Float64}:
479+
1.0
480+
1.0
481+
2.0
482+
-4.0
483+
1.0
484+
```
485+
The lower and upper row bounds:
486+
```jldoctest matrixofconstraints
487+
julia> row_bounds = dest.constraints.constants;
488+
489+
julia> row_bounds.lower
490+
3-element Vector{Float64}:
491+
5.0
492+
-Inf
493+
3.0
494+
495+
julia> row_bounds.upper
496+
3-element Vector{Float64}:
497+
5.0
498+
1.0
499+
Inf
500+
```
501+
The lower and upper variable bounds:
502+
```jldoctest matrixofconstraints
503+
julia> dest.variables.lower
504+
3-element Vector{Float64}:
505+
5.0
506+
-Inf
507+
0.0
508+
509+
julia> dest.variables.upper
510+
3-element Vector{Float64}:
511+
5.0
512+
10.0
513+
1.0
514+
```
515+
Because of larger variations between solvers, the objective can be queried using
516+
the standard MOI methods:
517+
```jldoctest matrixofconstraints
518+
julia> MOI.get(dest, MOI.ObjectiveSense())
519+
MAX_SENSE::OptimizationSense = 1
520+
521+
julia> F = MOI.get(dest, MOI.ObjectiveFunctionType())
522+
MathOptInterface.ScalarAffineFunction{Float64}
523+
524+
julia> F = MOI.get(dest, MOI.ObjectiveFunction{F}())
525+
0.0 + 1.0 MOI.VariableIndex(3) + 2.0 MOI.VariableIndex(2) - 3.1 MOI.VariableIndex(1)
526+
```
527+
528+
Thus, Clp.jl implements [`copy_to`](@ref) methods similar to the following:
408529
```julia
409-
function _copy_to(dest::Optimizer, src::Model)
530+
# This method copies from the cache to the `Clp.Optimizer` object.
531+
function MOI.copy_to(dest::Optimizer, src::OptimizerCache)
410532
@assert MOI.is_empty(dest)
411533
A = src.constraints.coefficients
412534
row_bounds = src.constraints.constants
@@ -423,35 +545,36 @@ function _copy_to(dest::Optimizer, src::Model)
423545
row_bounds.lower,
424546
row_bounds.upper,
425547
)
426-
# Set objective sense and constant (omitted)
427-
return
428-
end
429-
430-
function MOI.copy_to(dest::Optimizer, src::Model)
431-
_copy_to(dest, src)
432548
return MOI.Utilities.identity_index_map(src)
433549
end
434550

435-
function MOI.copy_to(
436-
dest::Optimizer,
437-
src::MOI.Utilities.UniversalFallback{Model},
438-
)
439-
# Copy attributes from `src` to `dest` and error in case any unsupported
440-
# constraints or attributes are set in `UniversalFallback`.
441-
return MOI.copy_to(dest, src.model)
551+
# This method copies from an arbitrary model to the optimizer, by the
552+
# intermediate `OptimizerCache` representation.
553+
function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
554+
cache = OptimizerCache()
555+
index_map = MOI.copy_to(cache, src)
556+
MOI.copy_to(dest, cache)
557+
return index_map
442558
end
443559

560+
# This is a special method that gets called in some cases when `OptimizerCache`
561+
# is used as the backing data structure in a `MOI.Utilities.CachingOptimizer`.
562+
# It is needed for performance, but not correctness.
444563
function MOI.copy_to(
445564
dest::Optimizer,
446-
src::MOI.ModelLike,
565+
src::MOI.Utilities.UniversalFallback{OptimizerCache},
447566
)
448-
model = Model()
449-
index_map = MOI.copy_to(model, src)
450-
_copy_to(dest, model)
451-
return index_map
567+
MOI.Utilities.throw_unsupported(src)
568+
return MOI.copy_to(dest, src.model)
452569
end
453570
```
454571

572+
!!! tip
573+
For other examples of [`Utilities.MatrixOfConstraints`](@ref), see:
574+
* [Cbc.jl](https://github.com/jump-dev/Cbc.jl)
575+
* [ECOS.jl](https://github.com/jump-dev/ECOS.jl)
576+
* [SCS.jl](https://github.com/jump-dev/SCS.jl)
577+
455578
## ModelFilter
456579

457580
Utilities provides [`Utilities.ModelFilter`](@ref) as a useful tool to copy a

0 commit comments

Comments
 (0)