Variables
A guide for variables in InfiniteOpt. See the respective technical manual for more details.
Overview
Decision variables are at the very core of InfiniteOpt as its name alludes to mathematical programs that entail infinite decision spaces (i.e., contain infinite decision variables). Principally, 4 variable types are employed: infinite, semi-infinite, point, and finite. Infinite variables encompass any decision variable that is parameterized by an infinite parameter(s) (e.g., space-time variables and stochastic recourse variables). Semi-infinite variables denote infinite variables where certain infinite parameters are restricted to point values. Point variables are infinite variables at a particular point. Finally, finite variables are decisions that are made irrespective of the infinite domain (e.g., first stage variables and design variables).
Basic Usage
Infinite, semi-infinite, point, and finite variables are summarized in the following table:
| Name | Variable Type Object | Description | Example |
|---|---|---|---|
| Infinite | Infinite | decision functions | $y(t, x, \xi)$ |
| Semi-Infinite | SemiInfinite | partially evaluated decision functions | $y(t_0, x, \xi)$ |
| Point | Point | fully evaluated decision functions | $y(t_0, x_0, \xi_k)$ |
| Finite | NA | classical decision variables | $z$ |
Infinite, semi-infinite, point, and finite variables are defined via @variable (inherited from JuMP) with their respective variable type object arguments: Infinite, SemiInfinite, and Point (finite variables don't use a variable type object).
Let's first set up a simple space-time model with infinite parameters time t and spatial position x:
julia> using InfiniteOpt
julia> model = InfiniteModel();
julia> @infinite_parameter(model, t in [0, 10])
t
julia> @infinite_parameter(model, x[1:2] in [-1, 1], independent = true)
2-element Vector{GeneralVariableRef}:
x[1]
x[2]Infinite Variables
Now let's define a time dependent infinite variable y(t) with a lower bound of 0:
julia> @variable(model, y >= 0, Infinite(t))
y(t)This creates a Julia variable y that points to the decision variable y(t) that is stored in model which is added to include a lower bound of 0. Another useful case is that of defining an array of variables w that depend on both position and time:
julia> @variable(model, w[i = 1:3], Infinite(t, x...), start = [0, 2, 1][i])
3-element Vector{GeneralVariableRef}:
w[1](t, x[1], x[2])
w[2](t, x[1], x[2])
w[3](t, x[1], x[2])Thus, we create a Julia array variable w whose elements w[i] point to their respective infinite variables w[i](t, x) stored in model. Note that the i used in the array definition can be used to index attributes assigned to each variable in the array. In this case, we used i to assign different initial guess values for each variable via the start keyword argument. Also note that we use Infinite(t, x...) instead of Infinite(t, x) since each scalar independent infinite parameter must be in its own argument.
Previous versions of InfiniteOpt supported arrays of independent infinite parameters for infinite variables (e.g., Infinite(x) where x is an array of independent infinite parameters). Now each independent infinite parameter must be an individual argument. This can be readily accomplished by splatting the infinite parameters (e.g., Infinite(x...)).
Moreover, infinite variables can uses functions to define the start values and bounds over a range of support points (e.g., a trajectory). This is discussed further below in the Macro Definition section.
Semi-Infinite Variables
Now let's restrict the above infinite variables w[i](t, x) to a particular time via semi-infinite variables:
julia> [w[i](0, x...) for i in 1:3]
3-element Vector{GeneralVariableRef}:
w[1](0, x[1], x[2])
w[2](0, x[1], x[2])
w[3](0, x[1], x[2])Thus, we create a Julia array of semi-infinite variables w[i](0, x) stored in model that inherit all properties from the corresponding infinite variables. This functional syntax is the recommended method for defining semi-infinite variables. See Restricted Variables to learn more. Alternatively, we can make a semi-infinite variable via @variable:
julia> @variable(model, w0[i = 1:3] <= 3, SemiInfinite(w[i], 0, x...))
3-element Vector{GeneralVariableRef}:
w0[1]
w0[2]
w0[3]This syntax will always overwrite the lower bound, upper bound, fix value, and start value (or lack their of), so it should be used carefully and purposefully.
Point Variables
Now let's add some point variables. These allow us to consider an infinite variable evaluated at a certain infinite parameter point. For example, let's define a point variable for y(0):
julia> y0 = y(0)
y(0)Here we create a Julia variable y0 which points to the point variable y(0). Again, this inherits all properties (e.g., bounds and start values) from y(t). Again, this is the recommended syntax, see Restricted Variables to learn more. To overwrite all properties, we can instead define a point variable using @variable:
julia> @variable(model, y0 == 0, Point(y, 0))
y0In this case the lower bound inherited from y(t) is overwritten by instead fixing y(0) to a value of 0.
Finite Variables
Finally, we can add finite variables to our model. These denote variables that hold a single value over the infinite domain or some portion of it (e.g., design variables, first stage variables, etc.). Let's add a finite variable $0 \leq d \leq 42$ that is an integer variable and defined over all infinite domains (i.e., time and space):
julia> @variable(model, 0 <= d <= 42, Int)
dThis creates a Julia variable d that points to the finite variable d which has a lower bound of 0, an upper bound of 42, and is an integer variable. Thus, finite variables are equivalent to those employed in JuMP.
Now we have defined variables that we can use in the objective, measures, and constraints. Please note that the above tutorial only shows a small portion of the capabilities and options available in defining variables. A full description is provided in the documentation below.
Variable Definition Methodology
Defining/initializing a variable (what happens behind the scenes of the variable macros) principally involves the following steps:
- Define the variable information pertaining to
JuMP.VariableInfo(e.g., bounds, indicate if it is integer, etc.) - Construct a concrete subtype of
InfOptVariableTypeto specify the desired type and its required additional information if appropriate - Build the variable object via
JuMP.build_variable - Add the variable object to an
InfiniteModeland assign a name viaJuMP.add_variable - Create a
GeneralVariableRefthat points to the variable object stored in the model
This methodology is presented for those wanting to learn more about the ins and outs of variable definition. We recommend that all variables be created via @variable. See Macro Variable Definition.
The JuMP.VariableInfo data structure stores the following variable information:
has_lb::Bool: Specifies aBoolit has a lower boundlower_bound::Union{Real, Function}: Specifies lower bound valuehas_ub::Bool: Specifies aBoolit has a upper boundupper_bound::Union{Real, Function}: Specifies upper bound valuehas_fix::Bool: Specifies aBoolit is fixedfixed_value::Union{Real, Function}: Specifies the fixed valuehas_start::Bool: Specifies aBoolit has a start valuestart::Union{Real, Function}: Specifies the start guess value, this can be a function for infinite variables that intakes a support and maps it to a guess value (allowing to specify guess trajectories)binary: SpecifiesBoolif it is binaryinteger: SpecifiesBoolif it is integer.
Thus, the user specifies this information to prepare such an object:
julia> info = VariableInfo(true, 0., true, 42., false, 0., false, 0., false, true);Here we specified a lower bound of 0, an upper bound of 42, and that it is integer valued.
The variable type objects (InfOptVariableType subtypes) are used with build_variable to specify the desired variable type along with any additional information needed for that type. For example, let's build an infinite variable y(t) that has an lower bound of 0, an upper bound of 42, and is integer valued:
julia> @infinite_parameter(model, t in [0, 10])
t
julia> info = VariableInfo(true, 0, true, 42, false, 0, false, 0, false, true);
julia> inf_var = build_variable(error, info, Infinite(t));Thus, we create an InfiniteVariable object with the desired properties.
Once a variable has been built, it needs to be added to our model and a Julia variable should be defined to reference it. Variables are added via add_variable which adds a variable object to the model, assigns a name to the variable, adds any constraints associated with the JuMP.VariableInfo, and returns an appropriate variable reference variable (a GeneralVariableRef). For example, let's add inf_var to model:
julia> var_ref = add_variable(model, inf_var, "y")
y(t)Thus, we have added an infinite variable y that is parameterized by t with the variable information mentioned above and now have a GeneralVariableRef called var_ref that can be used in defining our infinite model.
Note that the use of GeneralVariableRefs and the corresponding concrete subtypes of DispatchVariableRefs is discussed on the Expressions page.
Macro Variable Definition
The @variable macro automates the variable definition process discussed above in the Variable Definition Methodology section via a straightforward symbolic syntax. The only key difference is that non-anonymous macro calls will register variable names to ensure they are not repeated. Anonymous macro calls forgo this step and exactly follow the process described above. This section will highlight the details of using this macro which is the recommended way to define variables.
JuMP's documentation on variables is a good place to start since InfiniteOpt simply extends JuMP to accommodate our additional variable types.
We directly build upon JuMP.@variable to create all of our decision variable types. To illustrate this via example, let's setup a model with a variety of infinite parameters $t \in [0,10]$, $x \in [-1, 1]^3$, and $\xi \in \mathcal{N}(0, 1)$:
julia> using InfiniteOpt, Distributions
julia> model = InfiniteModel();
julia> @infinite_parameter(model, t in [0, 10]);
julia> @infinite_parameter(model, x[1:3] in [-1, 1], independent = true);
julia> @infinite_parameter(model, ξ ~ Normal());Variable Types
We specify the variable type by providing a subtype of InfOptVariableType as an extra positional argument:
julia> @variable(model, y, Infinite(t, x..., ξ)) # explicit infinite variable
y(t, x[1], x[2], x[3], ξ)
julia> @variable(model, ys, SemiInfinite(y, 0, x..., ξ)) # explicit semi-infinite variable
ys
julia> @variable(model, yp, Point(y, 0, [1, 1, 1]..., 0)) # explicit point variable
yp
julia> @variable(model, z) # explicit finite variable
zFor anonymous definition, we use the variable_type keyword argument instead:
julia> y = @variable(model, variable_type = Infinite(t, x..., ξ)) # anon infinite variable
noname(t, x[1], x[2], x[3], ξ)
julia> ys = @variable(model, variable_type = SemiInfinite(y, 0, x..., ξ)) # anon semi-infinite variable
noname(0, x[1], x[2], x[3], ξ)
julia> yp = @variable(model, variable_type = Point(y, 0, [1, 1, 1]..., 0)) # anon point variable
noname(0, 1, 1, 1, 0)
julia> z = @variable(model) # anon finite variable
nonamePlease refer to Infinite, SemiInfinite, and Point for more information.
Variable Names
Variable inherit their names from the symbolic literal given with explicit definitions:
julia> @variable(model, myname, Infinite(t))
myname(t)This creates an infinite variable with name "myname" that is added to model and creates a Julia variable myname that stores a GeneralVariableRef which points to the infinite variable in model.
We can overwrite the inherited name using the base_name keyword argument:
julia> @variable(model, myjlvar, Infinite(t), base_name = "myname")
myname(t)This creates an infinite variable with name "myname" that is added to model and creates a Julia variable myjlvar that stores a GeneralVariableRef which points to the infinite variable in model.
This syntax is particularly useful for anonymous variables to have meaningful names:
julia> myjlvar = @variable(model, variable_type = Infinite(t), base_name = "myname")
myname(t)See the Queries and Modification sections further below for more information on how to query/modify variable names.
Variable Bounds
We can specify variable bounds in like manner to JuMP variables. Let's demonstrate this with infinite variables (which accept scalars or functions):
julia> @variable(model, y_lb >= (a, b, c, d) -> a + b + c + d, Infinite(t, x...)) # add w/ lower bound
y_lb(t, x[1], x[2], x[3])
julia> @variable(model, y_ub <= 10, Infinite(t, x...)) # add w/ upper bound
y_ub(t, x[1], x[2], x[3])
julia> @variable(model, 0 <= y_bd <= 10, Infinite(t, x...)) # add w/ bounds
y_bd(t, x[1], x[2], x[3])
julia> @variable(model, y_fix == 42, Infinite(t, x...)) # add w/ fixed value
y_fix(t, x[1], x[2], x[3])When creating a variable with only a single bound and the value of the bound is not an explicit numeric literal, the name of the variable must appear on the left-hand side. Otherwise, the macro will error.
@variable(model, 0 <= y, Infinite(t)) # okay
a = 0
@variable(model, a <= y, Infinite(t)) # bad
@variable(model, y >= a, Infinite(t)) # okayFor anonymous definition, we use the lower_bound and upper_bound. Let's use finite variables for example:
julia> z_lb = @variable(model, lower_bound = 0, base_name = "z_lb") # add w/ lower bound
z_lb
julia> z_ub = @variable(model, upper_bound = 10, base_name = "z_ub") # add w/ upper bound
z_ub
julia> z_bd = @variable(model, lower_bound = 0, upper_bound = 10,
base_name = "z_bd") # add w/ bounds
z_bd
julia> z_fix = @variable(model, lower_bound = 10, upper_bound = 10,
base_name = "z_fix") # ~add w/ fixed value
z_fixNote that there isn't a keyword for fixing variables. Instead, fix should be used.
See the Queries and Modification sections further below for more information on how to query/modify variable bounds.
Semi-infinite and point variables defined via @variable overwrite the bounds of their respective infinite variables:
@variable(model, y >= 0, Infinite(t, x...)) # has lower bound
@variable(model, yp == 0, Point(w, 0, 0, 0, 0)) # forces the point to be fixedVariable Integrality
We can constrain the integrality of decision variables in like manner to JuMP using the Bin and Int positional arguments for explicit macro definition:
julia> @variable(model, y_bin, Infinite(t, x...), Bin) # add as binary variable
y_bin(t, x[1], x[2], x[3])
julia> @variable(model, y_int, Infinite(t, x...), Int) # add as integer variable
y_int(t, x[1], x[2], x[3])For anonymous definition, we use the binary and integer keyword arguments:
julia> y_bin = @variable(model, variable_type = Infinite(t, x...), binary = true)
noname(t, x[1], x[2], x[3])
julia> y_int = @variable(model, variable_type = Infinite(t, x...), integer = true)
noname(t, x[1], x[2], x[3])Moreover, we can add bounds as needed to constrain the domain of integer variables:
julia> @variable(model, 0 <= y_int2 <= 10, Infinite(t, x...), Int)
y_int2(t, x[1], x[2], x[3])See the Queries and Modification sections further below for more information on how to query/modify variable integralities.
Semi-infinite and point variables inherit the integrality of their respective infinite variables and these cannot be overwritten.
Variable Start Values
Optimization solvers often benefit from giving initial guesses for the optimal decision variable values. Following JuMP vernacular, these are called start values. We use the keyword start to specify these at variable creation:
julia> @variable(model, z_start, start = 42)
z_startMoreover, infinite variables can accept a function that specifies the start value of over the range of its infinite parameters (e.g., a function that provides an initial guess trajectory). For example, consider the difference between these two infinite variables:
julia> @variable(model, y_uniform, Infinite(t), start = 0) # start with y(t) = 0
y_uniform(t)
julia> @variable(model, y_sin, Infinite(t), start = sin) # start with y(t) = sin(t)
y_sin(t)Note that such start functions must be able to accept parameter values as arguments that exactly match the format of the infinite parameters given in Infinite(params...).
See the Queries and Modification sections further below for more information on how to query/modify variable names.
Variable Containers
Optimization problems often involve multi-dimensional decision variables. Luckily, JuMP provides a versatile syntax for specifying collections (i.e., containers) of variables. See JuMP's container documentation for a thorough tutorial on the syntax. It uses Arrays, DenseAxisArrays, and SparseAxisArrays to contain the variable references created. Here DenseAxisArrays and SparseAxisArrays allow the use of nontraditional indices (i.e., can use indices that are not sequential integers).
To illustrate what this means, consider the two equivalent ways to define a 3-dimensional vector of variables with indices [1, 2, 3]:
julia> s = [0, 2, 1];
julia> var_refs = @variable(model, [i = 1:3], start = s[i], base_name = "z")
3-element Vector{GeneralVariableRef}:
z[1]
z[2]
z[3]
julia> var_refs = Vector{GeneralVariableRef}(undef, 3);
julia> for i in eachindex(var_refs)
var_refs[i] = @variable(model, start = s[i], base_name = "z")
endMoreover, here are a few illustrative examples:
julia> @variable(model, z_dense[2:4])
1-dimensional DenseAxisArray{GeneralVariableRef,1,...} with index sets:
Dimension 1, 2:4
And data, a 3-element Vector{GeneralVariableRef}:
z_dense[2]
z_dense[3]
z_dense[4]
julia> @variable(model, z_named[[:A, :C, :Z]])
1-dimensional DenseAxisArray{GeneralVariableRef,1,...} with index sets:
Dimension 1, [:A, :C, :Z]
And data, a 3-element Vector{GeneralVariableRef}:
z_named[A]
z_named[C]
z_named[Z]
julia> @variable(model, z_sparse[i = 1:2, j = 1:2; i + j <= 3])
JuMP.Containers.SparseAxisArray{GeneralVariableRef, 2, Tuple{Int64, Int64}} with 3 entries:
[1, 1] = z_sparse[1,1]
[1, 2] = z_sparse[1,2]
[2, 1] = z_sparse[2,1]The variable macro will by default automatically detect which container type should be used. However, the user can specify a particular container type using the container keyword. For example, if we want to use indices a:b where a = 1 and b = 3, a DenseAxisArray will be used by default, but we can force it to be a regular Array:
julia> a = 1; b = 3;
julia> var_refs1 = @variable(model, [a:b], base_name = "z")
1-dimensional DenseAxisArray{GeneralVariableRef,1,...} with index sets:
Dimension 1, 1:3
And data, a 3-element Vector{GeneralVariableRef}:
z[1]
z[2]
z[3]
julia> var_refs2 = @variable(model, [a:b], base_name = "z", container = Array)
3-element Vector{GeneralVariableRef}:
z[1]
z[2]
z[3]Variable Sets
Like JuMP variables, we can constrain variables on creation to lie in particular sets. This allows us to make semi-definite variables, cone constrained variables, and more.
For example:
julia> @variable(model, z_psd[1:2, 1:2], PSD) # positive semi-definite variable matrix
2×2 LinearAlgebra.Symmetric{GeneralVariableRef, Matrix{GeneralVariableRef}}:
z_psd[1,1] z_psd[1,2]
z_psd[1,2] z_psd[2,2]
julia> @variable(model, z_cone[1:3] in SecondOrderCone()) # 2nd order cone variables
3-element Vector{GeneralVariableRef}:
z_cone[1]
z_cone[2]
z_cone[3]Typically, variable sets can be defined symbolically using the syntax var in set. For anonymous variables, the set keyword argument must be used:
julia> z_cone = @variable(model, [1:3], set = SecondOrderCone())
3-element Vector{GeneralVariableRef}:
noname
noname
nonameFor a more thorough tutorial please see JuMP's semi-definite documentation and/or JuMP's variables constrained on creation documentation.
Anonymous Variables
Above we talked showed the syntax for both explicit and anonymous variable creation. Anonymous creation is typically helpful in the following situations:
- defining multiple variables with the same name
- creating variables in user defined extensions
- using nontraditional naming
For anonymous variables, the only accepted positional arguments are the model and the container expression [indices...]. Everything else must be specified via keyword arguments kwargs... as shown in the subsections above.
@variable(model, [indices...], kwargs...)For more information, see JuMP's anonymous variable documentation.
The @variables Macro
When using many @variable calls, we can instead use @variables to enhance the readability:
julia> @variables(model, begin
y1, Infinite(t, x...)
y2[i=1:2] >= i, Infinite(t), (start = i, base_name = "Y_$i")
z2, Bin
end)
(y1(t, x[1], x[2], x[3]), GeneralVariableRef[Y_1[1](t), Y_2[2](t)], z2)Restricted Variables
To define point and semi-infinite variables, we can also use restrict for convenient inline definitions.
For example, let's consider restricting the infinite variable y(t, x):
julia> model = InfiniteModel();
julia> @infinite_parameter(model, t in [0, 1]);
julia> @infinite_parameter(model, x[1:2] in [-1, 1]);
julia> @variable(model, y, Infinite(t, x))
y(t, x)
julia> pt = restrict(y, 0, [-1, 1]) # make point variable y(0, [-1, 1])
y(0, [-1, 1])
julia> semi = restrict(y, 0, x) # make semi-infinite variable y(0, x)
y(0, [x[1], x[2]])We can also, even more conveniently, treat the infinite variable as a function to accomplish this in a more intuitive syntax:
julia> pt = y(0, [-1, 1]) # make point variable y(0, [-1, 1])
y(0, [-1, 1])
julia> semi = y(0, x) # make semi-infinite variable y(0, x)
y(0, [x[1], x[2]])Parameters
Like JuMP, InfiniteOpt supports the use of MOI.Parameter. For instance, consider making a finite variable as a parameter:
julia> @variable(model, param in Parameter(42))
paramwhich simply produces a finite parameter (i.e., is equivalent to @finite_parameter(model, param == 42)). For infinite variables, the use of MOI.Parameter simply produces a parameter function:
julia> @variable(model, pfunc in Parameter(sin), Infinite(t))
pfunc(t)which is equivalent to @parameter_function(model, pfunc == sin(t)). All parameters can be updated via set_parameter_value, see Finite Parameters and Parameter Function to learn more.
Queries
InfiniteOpt contains a large suite of methods to query information about variables. This suite comprises extensions to all current JuMP query methods and many more that are specific to InfiniteOpt. A number of the more commonly used ones are explained in this section, but all the available methods are explained in the technical manual.
General Information
Here we describe some methods used to query general variable information such as the name. Variable names can be extracted via name which returns the name of a variable. The index of a variable (where it is stored in the infinite model) is accessed via index and the infinite model it belongs to is given by owner_model. These methods are demonstrated below:
julia> name(y)
"y"
julia> index(y)
InfiniteVariableIndex(2)
julia> model_where_stored = owner_model(y);Also, num_variables is useful in returning the total number of decision variables currently stored in an infinite model:
julia> num_variables(model)
61
julia> num_variables(model, PointVariable)
2Similarly, all_variables returns a list of all the variables currently added to the model.
Finally, variable_by_name can be employed to return the appropriate GeneralVariableRef based off of the variable name if it is unique. Returns nothing if such a name cannot be found and errors if it is not unique. For example, we can request the reference associated with "y_ub":
julia> variable_by_name(model, "y_ub")
y_ub(t, x[1], x[2], x[3])Variable Constraint Info
As described above, variables in InfiniteOpt can have constraints associated with them like JuMP variables. These constraints include:
- lower bounds
- upper bounds
- fixed values
- binary valued
- integer valued.
Thus, a number of methods exist to query information about these constraints.
First, the [has/is]_[variable constraint type] methods indicate whether a variable has that particular constraint type. For example, to query if a variable y_lb has a lower bound we can use has_lower_bound:
julia> has_lower_bound(y_bd)
trueThus, y_bd does have a lower bound. The other methods are has_upper_bound, is_fixed, is_binary, and is_integer.
Next, the [ConstraintType]Ref methods return an appropriate explicit type InfOptConstraintRef that points to the constraint (errors if no such constraint exists). For example, the upper bound constraint of y_bd can be obtained via UpperBoundRef:
julia> UpperBoundRef(y_bd)
y_bd(t, x[1], x[2], x[3]) ≤ 10, ∀ t ∈ [0, 10], x[1] ∈ [-1, 1], x[2] ∈ [-1, 1], x[3] ∈ [-1, 1]The other methods are LowerBoundRef, FixRef, BinaryRef, and IntegerRef.
Finally, variable constraints that entail values (i.e., lower bounds, upper bounds, and fixed values) have their values queried via the appropriate method. For example, the lower bound value of y_bd is obtained via lower_bound:
julia> lower_bound(y_bd)
0.0Note these methods error when no such constraint is associated with the variable. The other methods are upper_bound and fix_value.
The start value can also be queried via start_value where nothing is returned if not start value is specified:
julia> start_value(var_refs[1])
0.0
julia> start_value(yp)Variable Use
InfiniteOpt defines a number of methods to track if and how variables are used in an infinite model. For example, used_by_constraint is used to determine if a variable is used by a constraint. For example, let's see if y_bd is used by a constraint:
julia> used_by_constraint(y_bd)
trueOther methods include used_by_measure and used_by_objective. For infinite variables, used_by_point_variable can also be used similarly.
Finally, in general is_used can be used to determine if a variable is used at all in the infinite model or not. For example, if we check yp using is_used we find that it isn't:
julia> is_used(yp)
falseType Specific
InfiniteOpt also employs a few methods for specific variable types that return information pertaining to that particular variable type. For infinite variables and semi-infinite variables, parameter_refs returns the tuple of infinite parameters that the variable depends on. For example, consider y(t, x):
julia> parameter_refs(y)
(t, x[1], x[2], x[3], ξ)For point variables, infinite_variable_ref and parameter_values return the infinite variable it depends on and the infinite parameter point values, respectively. For example, consider the point variable yp:
julia> infinite_variable_ref(yp)
y(t, x[1], x[2], x[3], ξ)
julia> parameter_values(yp)
(0.0, 1.0, 1.0, 1.0, 0.0)Modification
InfiniteOpt employs a wide variety of methods to modify/delete variables. These are comprised of JuMP extensions and methods native only to InfiniteOpt. This section will highlight some of the more commonly used ones. All the methods/macros are detailed in the technical manual.
Deletion
Like JuMP v0.19+, InfiniteOpt fully supports deletion throughout its data types. Any variable and its dependencies can be deleted via delete. Thus, when delete is invoked any bound/type constraints associated with the variable will be removed and it will be removed from any other constraints, measures, and/or objectives. For example, if we delete y(t, x, ξ) it will be removed along with its bounds and the point variable yp will also be removed since it is a dependent:
julia> delete(model, y)
julia> is_valid(model, yp)
falseAnother class of deletion methods correspond to variable constraints. For example, delete_lower_bound is used to delete a lower bound associated with a variable if it has one. Let's illustrate this by deleting the lower bound of y_bd:
julia> delete_lower_bound(y_bd)
julia> has_lower_bound(y_bd)
falseOther similar methods are delete_upper_bound, unfix, unset_binary, and unset_integer.
Variable Constraints
Another class of methods seek to add/modify variable constraints such as bounds. For example, set_lower_bound specifies the lower bound of a variable. We can add a lower bound of 0 to z by:
julia> set_lower_bound(z, 0)
julia> lower_bound(z)
0.0Thus, adding a lower bound to z. Furthermore, we can later modify the lower bound using the same method:
julia> set_lower_bound(z, -2)
julia> lower_bound(z)
-2.0Other similar methods are set_upper_bound, fix, set_binary, and set_integer.
Start Values
We can update the start value of a variable using set_start_value. For example:
julia> set_start_value(z, 0)
julia> start_value(z)
0.0For effective resolves, warmstart_backend_start_values provides a convenient and efficient way to update the start values used by the backend using a previous solution stored in the backend.
optimize!(model)
warmstart_backend_start_values(model)
set_parameter_value(p, 42)
optimize!(model)A number of other techniques exist for the various variable types can be found in the manual.