From c4afbda691ab900ba8c44e19f9eb4f6ff11414dc Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 15:47:47 +0200 Subject: [PATCH 01/28] Create manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/src/tutorials/manipulating_expressions.md diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md new file mode 100644 index 0000000000..8efdef6fa1 --- /dev/null +++ b/docs/src/tutorials/manipulating_expressions.md @@ -0,0 +1,13 @@ +```@meta +CurrentModule = MathOptInterface +DocTestSetup = quote + using MathOptInterface + const MOI = MathOptInterface +end +DocTestFilters = [r"MathOptInterface|MOI"] +``` + +# Manipulating expressions + +This guide highlights a syncactically appealing way to build expressions at the +MOI level. These may be especially useful when writing models or bridge code. From 87259d487c1d1d5a75f8fb572a7139688c5691ef Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 16:22:16 +0200 Subject: [PATCH 02/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 8efdef6fa1..07b2cf072b 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -9,5 +9,36 @@ DocTestFilters = [r"MathOptInterface|MOI"] # Manipulating expressions -This guide highlights a syncactically appealing way to build expressions at the -MOI level. These may be especially useful when writing models or bridge code. +This guide highlights a syntactically appealing way to build expressions at the +MOI level, but also to look at their contents. It may be especially useful +when writing models or bridge code. + +## Creating scalar affine functions + +The simplest scalar function is simply a variable: + +``` +var_idx = MOI.add_variable(model) +f1 = MOI.SingleVariable(var_idx) +``` + +This type of function is extremely simple: to express more complex functions, other +types must be used. For instance, a `ScalarAffineFunction` is a sum of linear terms +(a factor times a variable) and a constant. Such an object can be built using the +standard constructor: + +``` +f2 = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm(1, var_idx), 2) +``` + +However, you can also use operators to build the same scalar function: + +``` +f2 = 1 * MOI.SingleVariable(var_idx) + 2 +``` + +## Creating scalar quadratic functions + +## Creating vector functions + +## Canonicalizing functions From 308852e04b2f5adc3e7ace444597fe069e5d1dd1 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 16:22:38 +0200 Subject: [PATCH 03/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 07b2cf072b..c7f66143e9 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -13,7 +13,9 @@ This guide highlights a syntactically appealing way to build expressions at the MOI level, but also to look at their contents. It may be especially useful when writing models or bridge code. -## Creating scalar affine functions +## Creating functions + +### Creating scalar affine functions The simplest scalar function is simply a variable: @@ -37,8 +39,8 @@ However, you can also use operators to build the same scalar function: f2 = 1 * MOI.SingleVariable(var_idx) + 2 ``` -## Creating scalar quadratic functions +### Creating scalar quadratic functions -## Creating vector functions +### Creating vector functions ## Canonicalizing functions From 0bcec0e51510f1f40d80e98847140c0c2b91ddb7 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 16:24:42 +0200 Subject: [PATCH 04/28] Update make.jl --- docs/make.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/make.jl b/docs/make.jl index d6774c32f6..c0ebaad576 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -29,6 +29,7 @@ makedocs( "tutorials/implementing.md", "tutorials/mathprogbase.md", "tutorials/bridging_constraint.md", + "tutorials/manipulating_expressions.md", ], "Manual" => [ "manual/standard_form.md", From 0c53f5f9d2b0be675c7514ebab55ef364cc6404e Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 16:30:42 +0200 Subject: [PATCH 05/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index c7f66143e9..7b274c45e0 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -24,21 +24,29 @@ var_idx = MOI.add_variable(model) f1 = MOI.SingleVariable(var_idx) ``` -This type of function is extremely simple: to express more complex functions, other -types must be used. For instance, a `ScalarAffineFunction` is a sum of linear terms -(a factor times a variable) and a constant. Such an object can be built using the -standard constructor: +This type of function is extremely simple: to express more complex functions, +other types must be used. For instance, a `ScalarAffineFunction` is a sum of +linear terms (a factor times a variable) and a constant. Such an object can be +built using the standard constructor: ``` -f2 = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm(1, var_idx), 2) +f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, var_idx)], 2) ``` However, you can also use operators to build the same scalar function: ``` -f2 = 1 * MOI.SingleVariable(var_idx) + 2 +f2 = MOI.SingleVariable(var_idx) + 2 ``` +!!! warning + If you get an error such as `ERROR: UndefVarError: T not defined`, it means + that the Julia compiler was not able to determine the type of the + coefficients for the function. In that case, you can insert a + multiplication by one (with the appropriate type). For instance, + `1.0 * MOI.SingleVariable(var_idx)` creates an `MOI.ScalarAffineFunction` + whose coefficients are of type `Float64` (the type of `1.0`). + ### Creating scalar quadratic functions ### Creating vector functions From 536fd989c8ab5bc82215dee3a038724c963cace3 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 16:33:35 +0200 Subject: [PATCH 06/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 7b274c45e0..63a7bd4825 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -19,9 +19,9 @@ when writing models or bridge code. The simplest scalar function is simply a variable: -``` -var_idx = MOI.add_variable(model) -f1 = MOI.SingleVariable(var_idx) +```julia +var_idx = MOI.add_variable(model) # Create the variable x +f1 = MOI.SingleVariable(var_idx) # x ``` This type of function is extremely simple: to express more complex functions, @@ -29,14 +29,14 @@ other types must be used. For instance, a `ScalarAffineFunction` is a sum of linear terms (a factor times a variable) and a constant. Such an object can be built using the standard constructor: -``` -f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, var_idx)], 2) +```julia +f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, var_idx)], 2) # x + 2 ``` However, you can also use operators to build the same scalar function: -``` -f2 = MOI.SingleVariable(var_idx) + 2 +```julia +f2 = MOI.SingleVariable(var_idx) + 2 # x + 2 ``` !!! warning @@ -49,6 +49,16 @@ f2 = MOI.SingleVariable(var_idx) + 2 ### Creating scalar quadratic functions +Scalar quadratic functions are stored in `ScalarQuadraticFunction` objects, in a +way that is highly similar to scalar affine functions. You can obtain a quadratic +function as a product of affine functions: + +```julia +f3 = 1 * MOI.SingleVariable(var_idx) * MOI.SingleVariable(var_idx) # x² +f4 = f2 * f2 # (x + 2)² +f4 = f2^2 # (x + 2)² too +``` + ### Creating vector functions ## Canonicalizing functions From 2a3c4b3eaebc572ecd7a61e2a921b915717c2568 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 16:34:25 +0200 Subject: [PATCH 07/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 63a7bd4825..5314bdf954 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -15,6 +15,9 @@ when writing models or bridge code. ## Creating functions +When working with MathOptInterface, the major use of functions is creating them, +which is reviewed in this section. + ### Creating scalar affine functions The simplest scalar function is simply a variable: @@ -62,3 +65,5 @@ f4 = f2^2 # (x + 2)² too ### Creating vector functions ## Canonicalizing functions + +In more advanced use cases, you might need to ensure that a function is "canonical". From 477f22a559bfb9c774193df07c74b2925a816ad5 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:22:53 +0200 Subject: [PATCH 08/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 5314bdf954..f53eb45e78 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -53,8 +53,8 @@ f2 = MOI.SingleVariable(var_idx) + 2 # x + 2 ### Creating scalar quadratic functions Scalar quadratic functions are stored in `ScalarQuadraticFunction` objects, in a -way that is highly similar to scalar affine functions. You can obtain a quadratic -function as a product of affine functions: +way that is highly similar to scalar affine functions. You can obtain a +quadratic function as a product of affine functions: ```julia f3 = 1 * MOI.SingleVariable(var_idx) * MOI.SingleVariable(var_idx) # x² From f8c1d1d11c046908ad9a25582b1a25968b574fa0 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:34:38 +0200 Subject: [PATCH 09/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index f53eb45e78..db4c6de9c8 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -64,6 +64,27 @@ f4 = f2^2 # (x + 2)² too ### Creating vector functions +A vector function is a function with several values, irrespective of the number +of input variables. Similarly to scalar functions, there are three main types +of vector functions: `VectorOfVariables`, `VectorAffineFunction`, and +`VectorQuadraticFunction`. + +The easiest way to create a vector function is to stack several scalar +functions using [`MOI.Utilities.vectorize`](@ref). It takes a vector as input, +and the generated vector function (of the most appropriate type) has each +dimension corresponding to a dimension of the vector. + +```julia +f5 = MOIU.vectorize([f2, 2 * f2]) +``` + +!!! warning + `MOIU.vectorize` only takes a vector of similar scalar functions: you cannot + mix `SingleVariable` and `ScalarAffineFunction`, for instance. In practice, + it means that `MOIU.vectorize([f1, f2])` does not work; you should rather use + `MOIU.vectorize([1 * f1, f2])` instead to only have + [`ScalarAffineFunction`](@ref) objects. + ## Canonicalizing functions In more advanced use cases, you might need to ensure that a function is "canonical". From 4cfb8a178909c8bdb3aa206ec378d2d8c3b035e1 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:39:51 +0200 Subject: [PATCH 10/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index db4c6de9c8..f5c66b2caf 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -88,3 +88,23 @@ f5 = MOIU.vectorize([f2, 2 * f2]) ## Canonicalizing functions In more advanced use cases, you might need to ensure that a function is "canonical". +Functions are stored as an array of terms, but there is no check that these terms +are redundant: a [`ScalarAffineFunction`](@ref) object might have two terms with +the same variable, like `x + x + 1`. These terms could be merged without changing +the semantics of the function: `2 * x + 1`. + +Working with these objects might be cumbersome. Canonicalization helps maintain +redundancy to zero. + +[`MOIU.is_canonical`](@ref) checks whether a function is already in its canonical +form: + +```julia +MOIU.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 2 +``` + +[`MOIU.canonical`](@ref) returns the equivalent canonical version of the function: + +```julia +MOIU.canonical(f2 + f2) # Returns 2 * x + 2 +``` From 67be66541f0015da4a03a5c7ef2943a120ca990a Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:41:01 +0200 Subject: [PATCH 11/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index f5c66b2caf..b12628f1f0 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -28,7 +28,7 @@ f1 = MOI.SingleVariable(var_idx) # x ``` This type of function is extremely simple: to express more complex functions, -other types must be used. For instance, a `ScalarAffineFunction` is a sum of +other types must be used. For instance, a [`ScalarAffineFunction`](@ref) is a sum of linear terms (a factor times a variable) and a constant. Such an object can be built using the standard constructor: @@ -47,12 +47,12 @@ f2 = MOI.SingleVariable(var_idx) + 2 # x + 2 that the Julia compiler was not able to determine the type of the coefficients for the function. In that case, you can insert a multiplication by one (with the appropriate type). For instance, - `1.0 * MOI.SingleVariable(var_idx)` creates an `MOI.ScalarAffineFunction` + `1.0 * MOI.SingleVariable(var_idx)` creates an [`MOI.ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` (the type of `1.0`). ### Creating scalar quadratic functions -Scalar quadratic functions are stored in `ScalarQuadraticFunction` objects, in a +Scalar quadratic functions are stored in [`ScalarQuadraticFunction`](@ref) objects, in a way that is highly similar to scalar affine functions. You can obtain a quadratic function as a product of affine functions: @@ -66,8 +66,8 @@ f4 = f2^2 # (x + 2)² too A vector function is a function with several values, irrespective of the number of input variables. Similarly to scalar functions, there are three main types -of vector functions: `VectorOfVariables`, `VectorAffineFunction`, and -`VectorQuadraticFunction`. +of vector functions: [`VectorOfVariables`](@ref), [`VectorAffineFunction`](@ref), and +[`VectorQuadraticFunction`](@ref). The easiest way to create a vector function is to stack several scalar functions using [`MOI.Utilities.vectorize`](@ref). It takes a vector as input, @@ -79,8 +79,8 @@ f5 = MOIU.vectorize([f2, 2 * f2]) ``` !!! warning - `MOIU.vectorize` only takes a vector of similar scalar functions: you cannot - mix `SingleVariable` and `ScalarAffineFunction`, for instance. In practice, + [`MOIU.vectorize`](@ref) only takes a vector of similar scalar functions: you cannot + mix [`SingleVariable`](@ref) and [`ScalarAffineFunction`](@ref), for instance. In practice, it means that `MOIU.vectorize([f1, f2])` does not work; you should rather use `MOIU.vectorize([1 * f1, f2])` instead to only have [`ScalarAffineFunction`](@ref) objects. From ec3ef24d88fd9ebe5089310ad7f0de1eade32517 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:42:56 +0200 Subject: [PATCH 12/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index b12628f1f0..4e0dad13da 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -15,8 +15,8 @@ when writing models or bridge code. ## Creating functions -When working with MathOptInterface, the major use of functions is creating them, -which is reviewed in this section. +When working with MathOptInterface, the major use of functions is creating +them, which is reviewed in this section. ### Creating scalar affine functions @@ -28,9 +28,9 @@ f1 = MOI.SingleVariable(var_idx) # x ``` This type of function is extremely simple: to express more complex functions, -other types must be used. For instance, a [`ScalarAffineFunction`](@ref) is a sum of -linear terms (a factor times a variable) and a constant. Such an object can be -built using the standard constructor: +other types must be used. For instance, a [`ScalarAffineFunction`](@ref) is a +sum of linear terms (a factor times a variable) and a constant. Such an object +can be built using the standard constructor: ```julia f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, var_idx)], 2) # x + 2 @@ -43,18 +43,19 @@ f2 = MOI.SingleVariable(var_idx) + 2 # x + 2 ``` !!! warning - If you get an error such as `ERROR: UndefVarError: T not defined`, it means - that the Julia compiler was not able to determine the type of the + If you get an error such as `ERROR: UndefVarError: T not defined`, it + means that the Julia compiler was not able to determine the type of the coefficients for the function. In that case, you can insert a multiplication by one (with the appropriate type). For instance, - `1.0 * MOI.SingleVariable(var_idx)` creates an [`MOI.ScalarAffineFunction`](@ref) - whose coefficients are of type `Float64` (the type of `1.0`). + `1.0 * MOI.SingleVariable(var_idx)` creates an + [`MOI.ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` + (the type of `1.0`). ### Creating scalar quadratic functions -Scalar quadratic functions are stored in [`ScalarQuadraticFunction`](@ref) objects, in a -way that is highly similar to scalar affine functions. You can obtain a -quadratic function as a product of affine functions: +Scalar quadratic functions are stored in [`ScalarQuadraticFunction`](@ref) +objects, in a way that is highly similar to scalar affine functions. You can +obtain a quadratic function as a product of affine functions: ```julia f3 = 1 * MOI.SingleVariable(var_idx) * MOI.SingleVariable(var_idx) # x² @@ -66,8 +67,8 @@ f4 = f2^2 # (x + 2)² too A vector function is a function with several values, irrespective of the number of input variables. Similarly to scalar functions, there are three main types -of vector functions: [`VectorOfVariables`](@ref), [`VectorAffineFunction`](@ref), and -[`VectorQuadraticFunction`](@ref). +of vector functions: [`VectorOfVariables`](@ref), +[`VectorAffineFunction`](@ref), and [`VectorQuadraticFunction`](@ref). The easiest way to create a vector function is to stack several scalar functions using [`MOI.Utilities.vectorize`](@ref). It takes a vector as input, @@ -79,31 +80,32 @@ f5 = MOIU.vectorize([f2, 2 * f2]) ``` !!! warning - [`MOIU.vectorize`](@ref) only takes a vector of similar scalar functions: you cannot - mix [`SingleVariable`](@ref) and [`ScalarAffineFunction`](@ref), for instance. In practice, - it means that `MOIU.vectorize([f1, f2])` does not work; you should rather use - `MOIU.vectorize([1 * f1, f2])` instead to only have - [`ScalarAffineFunction`](@ref) objects. + [`MOIU.vectorize`](@ref) only takes a vector of similar scalar functions: + you cannot mix [`SingleVariable`](@ref) and [`ScalarAffineFunction`](@ref), + for instance. In practice, it means that `MOIU.vectorize([f1, f2])` does + not work; you should rather use `MOIU.vectorize([1 * f1, f2])` instead to + only have [`ScalarAffineFunction`](@ref) objects. ## Canonicalizing functions -In more advanced use cases, you might need to ensure that a function is "canonical". -Functions are stored as an array of terms, but there is no check that these terms -are redundant: a [`ScalarAffineFunction`](@ref) object might have two terms with -the same variable, like `x + x + 1`. These terms could be merged without changing -the semantics of the function: `2 * x + 1`. +In more advanced use cases, you might need to ensure that a function is +"canonical". Functions are stored as an array of terms, but there is no check +that these terms are redundant: a [`ScalarAffineFunction`](@ref) object might +have two terms with the same variable, like `x + x + 1`. These terms could be +merged without changing the semantics of the function: `2 * x + 1`. Working with these objects might be cumbersome. Canonicalization helps maintain redundancy to zero. -[`MOIU.is_canonical`](@ref) checks whether a function is already in its canonical -form: +[`MOIU.is_canonical`](@ref) checks whether a function is already in its +canonical form: ```julia MOIU.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 2 ``` -[`MOIU.canonical`](@ref) returns the equivalent canonical version of the function: +[`MOIU.canonical`](@ref) returns the equivalent canonical version of the +function: ```julia MOIU.canonical(f2 + f2) # Returns 2 * x + 2 From 5332c05ef2bfb13c3f7fefb12302d378fe4f9fe9 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:47:43 +0200 Subject: [PATCH 13/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 4e0dad13da..9efa2ab96c 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -110,3 +110,24 @@ function: ```julia MOIU.canonical(f2 + f2) # Returns 2 * x + 2 ``` + +## Exploring functions + +At some point, you might need to dig into a function, for instance to map it +into solver constructs. + +### Vector functions + +[`MOIU.scalarize`](@ref) returns a vector of scalar functions from a vector +function: + +```julia +MOIU.scalarize(MOIU.vectorize([f2, 2 * f2])) # Returns a vector [f2, 2 * f2]. +``` + +[`MOI.output_dimension`](@ref) returns the number of dimensions of the +output of a function: + +```julia +MOI.output_dimension(MOIU.vectorize([f2, 2 * f2])) # Returns 2. +``` From eb86709d0b35b600e2fbb01ca12bdfa9a042cf55 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 17:55:12 +0200 Subject: [PATCH 14/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 9efa2ab96c..7a0d82873b 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -125,6 +125,10 @@ function: MOIU.scalarize(MOIU.vectorize([f2, 2 * f2])) # Returns a vector [f2, 2 * f2]. ``` +!!! note + [`MOIU.eachscalar`](@ref) returns an iterator on the dimensions, which + serves the same purpose as [`MOIU.scalarize`](@ref). + [`MOI.output_dimension`](@ref) returns the number of dimensions of the output of a function: From 2394697367681f8802d11e00b2187983fb21f6d6 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 19:02:23 +0200 Subject: [PATCH 15/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 7a0d82873b..78339b349e 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -23,8 +23,8 @@ them, which is reviewed in this section. The simplest scalar function is simply a variable: ```julia -var_idx = MOI.add_variable(model) # Create the variable x -f1 = MOI.SingleVariable(var_idx) # x +var_idx = add_variable(model) # Create the variable x +f1 = SingleVariable(var_idx) # x ``` This type of function is extremely simple: to express more complex functions, @@ -33,13 +33,13 @@ sum of linear terms (a factor times a variable) and a constant. Such an object can be built using the standard constructor: ```julia -f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, var_idx)], 2) # x + 2 +f2 = ScalarAffineFunction([ScalarAffineTerm(1, var_idx)], 2) # x + 2 ``` However, you can also use operators to build the same scalar function: ```julia -f2 = MOI.SingleVariable(var_idx) + 2 # x + 2 +f2 = SingleVariable(var_idx) + 2 # x + 2 ``` !!! warning @@ -47,8 +47,8 @@ f2 = MOI.SingleVariable(var_idx) + 2 # x + 2 means that the Julia compiler was not able to determine the type of the coefficients for the function. In that case, you can insert a multiplication by one (with the appropriate type). For instance, - `1.0 * MOI.SingleVariable(var_idx)` creates an - [`MOI.ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` + `1.0 * SingleVariable(var_idx)` creates a + [`ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` (the type of `1.0`). ### Creating scalar quadratic functions @@ -58,7 +58,7 @@ objects, in a way that is highly similar to scalar affine functions. You can obtain a quadratic function as a product of affine functions: ```julia -f3 = 1 * MOI.SingleVariable(var_idx) * MOI.SingleVariable(var_idx) # x² +f3 = 1 * SingleVariable(var_idx) * SingleVariable(var_idx) # x² f4 = f2 * f2 # (x + 2)² f4 = f2^2 # (x + 2)² too ``` @@ -71,20 +71,21 @@ of vector functions: [`VectorOfVariables`](@ref), [`VectorAffineFunction`](@ref), and [`VectorQuadraticFunction`](@ref). The easiest way to create a vector function is to stack several scalar -functions using [`MOI.Utilities.vectorize`](@ref). It takes a vector as input, +functions using [`Utilities.vectorize`](@ref). It takes a vector as input, and the generated vector function (of the most appropriate type) has each dimension corresponding to a dimension of the vector. ```julia -f5 = MOIU.vectorize([f2, 2 * f2]) +f5 = Utilities.vectorize([f2, 2 * f2]) ``` !!! warning - [`MOIU.vectorize`](@ref) only takes a vector of similar scalar functions: - you cannot mix [`SingleVariable`](@ref) and [`ScalarAffineFunction`](@ref), - for instance. In practice, it means that `MOIU.vectorize([f1, f2])` does - not work; you should rather use `MOIU.vectorize([1 * f1, f2])` instead to - only have [`ScalarAffineFunction`](@ref) objects. + [`Utilities.vectorize`](@ref) only takes a vector of similar scalar + functions: you cannot mix [`SingleVariable`](@ref) and + [`ScalarAffineFunction`](@ref), for instance. In practice, it means that + `Utilities.vectorize([f1, f2])` does not work; you should rather use + `Utilities.vectorize([1 * f1, f2])` instead to only have + [`ScalarAffineFunction`](@ref) objects. ## Canonicalizing functions @@ -97,18 +98,18 @@ merged without changing the semantics of the function: `2 * x + 1`. Working with these objects might be cumbersome. Canonicalization helps maintain redundancy to zero. -[`MOIU.is_canonical`](@ref) checks whether a function is already in its +[`Utilities.is_canonical`](@ref) checks whether a function is already in its canonical form: ```julia -MOIU.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 2 +Utilities.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 2 ``` -[`MOIU.canonical`](@ref) returns the equivalent canonical version of the +[`Utilities.canonical`](@ref) returns the equivalent canonical version of the function: ```julia -MOIU.canonical(f2 + f2) # Returns 2 * x + 2 +Utilities.canonical(f2 + f2) # Returns 2 * x + 2 ``` ## Exploring functions @@ -118,20 +119,20 @@ into solver constructs. ### Vector functions -[`MOIU.scalarize`](@ref) returns a vector of scalar functions from a vector -function: +[`Utilities.scalarize`](@ref) returns a vector of scalar functions from a +vector function: ```julia -MOIU.scalarize(MOIU.vectorize([f2, 2 * f2])) # Returns a vector [f2, 2 * f2]. +Utilities.scalarize(f5) # Returns a vector [f2, 2 * f2]. ``` !!! note - [`MOIU.eachscalar`](@ref) returns an iterator on the dimensions, which - serves the same purpose as [`MOIU.scalarize`](@ref). + [`Utilities.eachscalar`](@ref) returns an iterator on the dimensions, which + serves the same purpose as [`Utilities.scalarize`](@ref). -[`MOI.output_dimension`](@ref) returns the number of dimensions of the +[`output_dimension`](@ref) returns the number of dimensions of the output of a function: ```julia -MOI.output_dimension(MOIU.vectorize([f2, 2 * f2])) # Returns 2. +output_dimension(f5) # Returns 2. ``` From 85f4f5abf3dd39cd839549c1389e7effdeca515c Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 19:23:11 +0200 Subject: [PATCH 16/28] Add docs for scalarize. --- src/Utilities/functions.jl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index a3f63d0ab2..ebc64ac456 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -3067,12 +3067,22 @@ function operate( return MOI.VectorQuadraticFunction(quadratic_terms, affine_terms, constant) end -# Similar to `eachscalar` but faster, see -# https://github.com/jump-dev/MathOptInterface.jl/issues/418 +""" + scalarize(func::MOI.VectorOfVariables, ignore_constants::Bool = false) + +Returns a vector of scalar functions making up the vector function in the form +of a `Vector{MOI.SingleVariable}`. +""" function scalarize(f::MOI.VectorOfVariables, ignore_constants::Bool = false) return MOI.SingleVariable.(f.variables) end +""" + scalarize(func::MOI.VectorAffineFunction{T}, ignore_constants::Bool = false) + +Returns a vector of scalar functions making up the vector function in the form +of a `Vector{MOI.ScalarAffineFunction{T}}`. +""" function scalarize( f::MOI.VectorAffineFunction{T}, ignore_constants::Bool = false, @@ -3092,6 +3102,12 @@ function scalarize( return functions end +""" + scalarize(func::MOI.VectorQuadraticFunction{T}, ignore_constants::Bool = false) + +Returns a vector of scalar functions making up the vector function in the form +of a `Vector{MOI.ScalarQuadraticFunction{T}}`. +""" function scalarize( f::MOI.VectorQuadraticFunction{T}, ignore_constants::Bool = false, From d6987b822ba1753688248b9751292d1adaef32f6 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 19:24:50 +0200 Subject: [PATCH 17/28] Add documentation for eachscalar. --- src/Utilities/functions.jl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index ebc64ac456..8d9bf8f880 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -492,7 +492,20 @@ function ScalarFunctionIterator(f::MOI.VectorQuadraticFunction) ) end +""" + eachscalar(f::MOI.AbstractVectorFunction) + +Returns an iterator for the scalar components of the vector function. + +See also [`scalarize`](@ref). +""" eachscalar(f::MOI.AbstractVectorFunction) = ScalarFunctionIterator(f) + +""" + eachscalar(f::MOI.AbstractVector) + +Returns an iterator for the scalar components of the vector. +""" eachscalar(f::AbstractVector) = f function Base.iterate(it::ScalarFunctionIterator, state = 1) @@ -3072,6 +3085,8 @@ end Returns a vector of scalar functions making up the vector function in the form of a `Vector{MOI.SingleVariable}`. + +See also [`eachscalar`](@ref). """ function scalarize(f::MOI.VectorOfVariables, ignore_constants::Bool = false) return MOI.SingleVariable.(f.variables) @@ -3082,6 +3097,8 @@ end Returns a vector of scalar functions making up the vector function in the form of a `Vector{MOI.ScalarAffineFunction{T}}`. + +See also [`eachscalar`](@ref). """ function scalarize( f::MOI.VectorAffineFunction{T}, @@ -3107,6 +3124,8 @@ end Returns a vector of scalar functions making up the vector function in the form of a `Vector{MOI.ScalarQuadraticFunction{T}}`. + +See also [`eachscalar`](@ref). """ function scalarize( f::MOI.VectorQuadraticFunction{T}, From 9bcb6849d77e86bd6fa40a3e1ccd612d1464563b Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 19:31:15 +0200 Subject: [PATCH 18/28] Update reference.md --- docs/src/submodules/Utilities/reference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/submodules/Utilities/reference.md b/docs/src/submodules/Utilities/reference.md index 25054cc243..e2d64d0b83 100644 --- a/docs/src/submodules/Utilities/reference.md +++ b/docs/src/submodules/Utilities/reference.md @@ -157,6 +157,8 @@ The following functions can be used to manipulate functions with basic algebra: ```@docs Utilities.scalar_type +Utilities.scalarize +Utilities.eachscalar Utilities.promote_operation Utilities.operate Utilities.operate! From 2317e35d8dd9672d5dc25e592975480ea2f75e17 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 22:57:41 +0200 Subject: [PATCH 19/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 78339b349e..348196f740 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -23,8 +23,8 @@ them, which is reviewed in this section. The simplest scalar function is simply a variable: ```julia -var_idx = add_variable(model) # Create the variable x -f1 = SingleVariable(var_idx) # x +x = add_variable(model) # Create the variable x +f1 = SingleVariable(x) # x ``` This type of function is extremely simple: to express more complex functions, @@ -33,13 +33,13 @@ sum of linear terms (a factor times a variable) and a constant. Such an object can be built using the standard constructor: ```julia -f2 = ScalarAffineFunction([ScalarAffineTerm(1, var_idx)], 2) # x + 2 +f2 = ScalarAffineFunction([ScalarAffineTerm(1, x)], 2) # x + 2 ``` However, you can also use operators to build the same scalar function: ```julia -f2 = SingleVariable(var_idx) + 2 # x + 2 +f2 = SingleVariable(x) + 2 # x + 2 ``` !!! warning @@ -47,7 +47,7 @@ f2 = SingleVariable(var_idx) + 2 # x + 2 means that the Julia compiler was not able to determine the type of the coefficients for the function. In that case, you can insert a multiplication by one (with the appropriate type). For instance, - `1.0 * SingleVariable(var_idx)` creates a + `1.0 * SingleVariable(x)` creates a [`ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` (the type of `1.0`). @@ -58,7 +58,7 @@ objects, in a way that is highly similar to scalar affine functions. You can obtain a quadratic function as a product of affine functions: ```julia -f3 = 1 * SingleVariable(var_idx) * SingleVariable(var_idx) # x² +f3 = 1 * SingleVariable(x) * SingleVariable(x) # x² f4 = f2 * f2 # (x + 2)² f4 = f2^2 # (x + 2)² too ``` From fa5cbe70a686ac17e9be1dcaff7604751bf259c3 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 23:01:10 +0200 Subject: [PATCH 20/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 348196f740..68b59bb2e5 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -102,7 +102,7 @@ redundancy to zero. canonical form: ```julia -Utilities.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 2 +Utilities.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 4 ``` [`Utilities.canonical`](@ref) returns the equivalent canonical version of the From 0c53e5e35422e396c34804b09eba5f120a08333a Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Tue, 24 Aug 2021 23:13:07 +0200 Subject: [PATCH 21/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 68b59bb2e5..463c1ce438 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -93,7 +93,7 @@ In more advanced use cases, you might need to ensure that a function is "canonical". Functions are stored as an array of terms, but there is no check that these terms are redundant: a [`ScalarAffineFunction`](@ref) object might have two terms with the same variable, like `x + x + 1`. These terms could be -merged without changing the semantics of the function: `2 * x + 1`. +merged without changing the semantics of the function: `2x + 1`. Working with these objects might be cumbersome. Canonicalization helps maintain redundancy to zero. @@ -109,7 +109,7 @@ Utilities.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 4 function: ```julia -Utilities.canonical(f2 + f2) # Returns 2 * x + 2 +Utilities.canonical(f2 + f2) # Returns 2x + 2 ``` ## Exploring functions From 89bdf204362e141d4bdeee0bfae8c391aab47399 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Wed, 25 Aug 2021 16:33:13 +0200 Subject: [PATCH 22/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 463c1ce438..a34aa82de1 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -22,9 +22,11 @@ them, which is reviewed in this section. The simplest scalar function is simply a variable: -```julia -x = add_variable(model) # Create the variable x -f1 = SingleVariable(x) # x +```jldoctest expr; setup=:(model = MOI.Utilities.CachingOptimizer(MOI.Utilities.Model{Float64}(), MOI.Utilities.AUTOMATIC); ) +julia> x = MOI.add_variable(model) # Create the variable x +MathOptInterface.VariableIndex(1) +julia> f1 = MOI.SingleVariable(x) # A function with the single variable x +MathOptInterface.SingleVariable(MathOptInterface.VariableIndex(1)) ``` This type of function is extremely simple: to express more complex functions, From 9088d17643301326e8065868aa12ff7ed28bcefb Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Wed, 25 Aug 2021 16:35:34 +0200 Subject: [PATCH 23/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index a34aa82de1..f0ecec90f7 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -34,14 +34,16 @@ other types must be used. For instance, a [`ScalarAffineFunction`](@ref) is a sum of linear terms (a factor times a variable) and a constant. Such an object can be built using the standard constructor: -```julia -f2 = ScalarAffineFunction([ScalarAffineTerm(1, x)], 2) # x + 2 +```jldoctest expr +julia> f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, x)], 2) # x + 2 +MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))], 2) ``` However, you can also use operators to build the same scalar function: -```julia -f2 = SingleVariable(x) + 2 # x + 2 +```jldoctest expr +julia> f2 = MOI.SingleVariable(x) + 2 # x + 2 +MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))], 2) ``` !!! warning @@ -51,7 +53,7 @@ f2 = SingleVariable(x) + 2 # x + 2 multiplication by one (with the appropriate type). For instance, `1.0 * SingleVariable(x)` creates a [`ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` - (the type of `1.0`). + (the type of `1.0`), i.e. a `ScalarAffineFunction{Float64}`. ### Creating scalar quadratic functions @@ -59,10 +61,13 @@ Scalar quadratic functions are stored in [`ScalarQuadraticFunction`](@ref) objects, in a way that is highly similar to scalar affine functions. You can obtain a quadratic function as a product of affine functions: -```julia -f3 = 1 * SingleVariable(x) * SingleVariable(x) # x² -f4 = f2 * f2 # (x + 2)² -f4 = f2^2 # (x + 2)² too +```jldoctest expr +julia> f3 = 1 * MOI.SingleVariable(x) * MOI.SingleVariable(x) # x² +MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[], MathOptInterface.ScalarQuadraticTerm{Int64}[MathOptInterface.ScalarQuadraticTerm{Int64}(2, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1))], 0) +julia> f4 = f2 * f2 # (x + 2)² +MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], MathOptInterface.ScalarQuadraticTerm{Int64}[MathOptInterface.ScalarQuadraticTerm{Int64}(2, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1))], 4) +julia> f4 = f2^2 # (x + 2)² too +MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], MathOptInterface.ScalarQuadraticTerm{Int64}[MathOptInterface.ScalarQuadraticTerm{Int64}(2, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1))], 4) ``` ### Creating vector functions From 743df9bc4ffaa2ea5895c20c21eab6332941bc2b Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Wed, 25 Aug 2021 16:37:35 +0200 Subject: [PATCH 24/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index f0ecec90f7..4f01d8424a 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -82,8 +82,9 @@ functions using [`Utilities.vectorize`](@ref). It takes a vector as input, and the generated vector function (of the most appropriate type) has each dimension corresponding to a dimension of the vector. -```julia -f5 = Utilities.vectorize([f2, 2 * f2]) +```jldoctest expr +julia> f5 = MOI.Utilities.vectorize([f2, 2 * f2]) +MathOptInterface.VectorAffineFunction{Int64}(MathOptInterface.VectorAffineTerm{Int64}[MathOptInterface.VectorAffineTerm{Int64}(1, MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))), MathOptInterface.VectorAffineTerm{Int64}(2, MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1)))], [2, 4]) ``` !!! warning @@ -108,15 +109,17 @@ redundancy to zero. [`Utilities.is_canonical`](@ref) checks whether a function is already in its canonical form: -```julia -Utilities.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 4 +```jldoctest expr +julia> MOI.Utilities.is_canonical(f2 + f2) # (x + 2) + (x + 2) is stored as x + x + 4 +false ``` [`Utilities.canonical`](@ref) returns the equivalent canonical version of the function: -```julia -Utilities.canonical(f2 + f2) # Returns 2x + 2 +```jldoctest expr +julia> MOI.Utilities.canonical(f2 + f2) # Returns 2x + 4 +MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], 4) ``` ## Exploring functions @@ -129,8 +132,11 @@ into solver constructs. [`Utilities.scalarize`](@ref) returns a vector of scalar functions from a vector function: -```julia -Utilities.scalarize(f5) # Returns a vector [f2, 2 * f2]. +```jldoctest expr +julia> MOI.Utilities.scalarize(f5) # Returns a vector [f2, 2 * f2]. +2-element Vector{MathOptInterface.ScalarAffineFunction{Int64}}: + MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))], 2) + MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], 4) ``` !!! note @@ -140,6 +146,7 @@ Utilities.scalarize(f5) # Returns a vector [f2, 2 * f2]. [`output_dimension`](@ref) returns the number of dimensions of the output of a function: -```julia -output_dimension(f5) # Returns 2. +```jldoctest expr +julia> MOI.output_dimension(f5) +2 ``` From 8b9804890bc1ec84c9c8999a2676aa8eadea0600 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Thu, 26 Aug 2021 00:34:14 +0200 Subject: [PATCH 25/28] Update manipulating_expressions.md --- .../src/tutorials/manipulating_expressions.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 4f01d8424a..b00357afec 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -36,14 +36,14 @@ can be built using the standard constructor: ```jldoctest expr julia> f2 = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1, x)], 2) # x + 2 -MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))], 2) +MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(1, VariableIndex(1))], 2) ``` However, you can also use operators to build the same scalar function: ```jldoctest expr julia> f2 = MOI.SingleVariable(x) + 2 # x + 2 -MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))], 2) +MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(1, VariableIndex(1))], 2) ``` !!! warning @@ -63,11 +63,11 @@ obtain a quadratic function as a product of affine functions: ```jldoctest expr julia> f3 = 1 * MOI.SingleVariable(x) * MOI.SingleVariable(x) # x² -MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[], MathOptInterface.ScalarQuadraticTerm{Int64}[MathOptInterface.ScalarQuadraticTerm{Int64}(2, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1))], 0) +MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarQuadraticTerm{Int64}[ScalarQuadraticTerm{Int64}(2, VariableIndex(1), VariableIndex(1))], MathOptInterface.ScalarAffineTerm{Int64}[], 0) julia> f4 = f2 * f2 # (x + 2)² -MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], MathOptInterface.ScalarQuadraticTerm{Int64}[MathOptInterface.ScalarQuadraticTerm{Int64}(2, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1))], 4) +MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarQuadraticTerm{Int64}[ScalarQuadraticTerm{Int64}(2, VariableIndex(1), VariableIndex(1))], MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(2, VariableIndex(1)), ScalarAffineTerm{Int64}(2, VariableIndex(1))], 4) julia> f4 = f2^2 # (x + 2)² too -MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1)), MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], MathOptInterface.ScalarQuadraticTerm{Int64}[MathOptInterface.ScalarQuadraticTerm{Int64}(2, MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(1))], 4) +MathOptInterface.ScalarQuadraticFunction{Int64}(MathOptInterface.ScalarQuadraticTerm{Int64}[ScalarQuadraticTerm{Int64}(2, VariableIndex(1), VariableIndex(1))], MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(2, VariableIndex(1)), ScalarAffineTerm{Int64}(2, VariableIndex(1))], 4) ``` ### Creating vector functions @@ -84,7 +84,7 @@ dimension corresponding to a dimension of the vector. ```jldoctest expr julia> f5 = MOI.Utilities.vectorize([f2, 2 * f2]) -MathOptInterface.VectorAffineFunction{Int64}(MathOptInterface.VectorAffineTerm{Int64}[MathOptInterface.VectorAffineTerm{Int64}(1, MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))), MathOptInterface.VectorAffineTerm{Int64}(2, MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1)))], [2, 4]) +MathOptInterface.VectorAffineFunction{Int64}(MathOptInterface.VectorAffineTerm{Int64}[VectorAffineTerm{Int64}(1, ScalarAffineTerm{Int64}(1, VariableIndex(1))), VectorAffineTerm{Int64}(2, ScalarAffineTerm{Int64}(2, VariableIndex(1)))], [2, 4]) ``` !!! warning @@ -119,7 +119,7 @@ function: ```jldoctest expr julia> MOI.Utilities.canonical(f2 + f2) # Returns 2x + 4 -MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], 4) +MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(2, VariableIndex(1))], 4) ``` ## Exploring functions @@ -134,9 +134,9 @@ vector function: ```jldoctest expr julia> MOI.Utilities.scalarize(f5) # Returns a vector [f2, 2 * f2]. -2-element Vector{MathOptInterface.ScalarAffineFunction{Int64}}: - MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(1, MathOptInterface.VariableIndex(1))], 2) - MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[MathOptInterface.ScalarAffineTerm{Int64}(2, MathOptInterface.VariableIndex(1))], 4) +2-element Array{MathOptInterface.ScalarAffineFunction{Int64},1}: + MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(1, VariableIndex(1))], 2) + MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(2, VariableIndex(1))], 4) ``` !!! note From 470ff4a16ef819f0c34296f92326024e91660898 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Fri, 27 Aug 2021 02:23:22 +0200 Subject: [PATCH 26/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index b00357afec..6298daf95f 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -15,8 +15,7 @@ when writing models or bridge code. ## Creating functions -When working with MathOptInterface, the major use of functions is creating -them, which is reviewed in this section. +This section details the ways to create functions with MathOptInterface. ### Creating scalar affine functions From 76e5e7d6e82dcb516d1bd72fbfc264e57652fb62 Mon Sep 17 00:00:00 2001 From: Thibaut Cuvelier Date: Fri, 27 Aug 2021 02:23:37 +0200 Subject: [PATCH 27/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index 6298daf95f..c1fa5540cf 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -28,7 +28,7 @@ julia> f1 = MOI.SingleVariable(x) # A function with the single variable x MathOptInterface.SingleVariable(MathOptInterface.VariableIndex(1)) ``` -This type of function is extremely simple: to express more complex functions, +This type of function is extremely simple; to express more complex functions, other types must be used. For instance, a [`ScalarAffineFunction`](@ref) is a sum of linear terms (a factor times a variable) and a constant. Such an object can be built using the standard constructor: From af5e20c20b772e943b81dc345839823b5736b585 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 27 Aug 2021 13:41:36 +1200 Subject: [PATCH 28/28] Update manipulating_expressions.md --- docs/src/tutorials/manipulating_expressions.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/src/tutorials/manipulating_expressions.md b/docs/src/tutorials/manipulating_expressions.md index c1fa5540cf..3efe0abbc2 100644 --- a/docs/src/tutorials/manipulating_expressions.md +++ b/docs/src/tutorials/manipulating_expressions.md @@ -45,15 +45,6 @@ julia> f2 = MOI.SingleVariable(x) + 2 # x + 2 MathOptInterface.ScalarAffineFunction{Int64}(MathOptInterface.ScalarAffineTerm{Int64}[ScalarAffineTerm{Int64}(1, VariableIndex(1))], 2) ``` -!!! warning - If you get an error such as `ERROR: UndefVarError: T not defined`, it - means that the Julia compiler was not able to determine the type of the - coefficients for the function. In that case, you can insert a - multiplication by one (with the appropriate type). For instance, - `1.0 * SingleVariable(x)` creates a - [`ScalarAffineFunction`](@ref) whose coefficients are of type `Float64` - (the type of `1.0`), i.e. a `ScalarAffineFunction{Float64}`. - ### Creating scalar quadratic functions Scalar quadratic functions are stored in [`ScalarQuadraticFunction`](@ref)