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:

NameVariable Type ObjectDescriptionExample
InfiniteInfinitedecision functions$y(t, x, \xi)$
Semi-InfiniteSemiInfinitepartially evaluated decision functions$y(t_0, x, \xi)$
PointPointfully evaluated decision functions$y(t_0, x_0, \xi_k)$
FiniteNAclassical 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)
 w[2](t, x)
 w[3](t, x)

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.

Moreover, for infinite variables a function can be given to determine the start values over a range of support points (e.g., a guess 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> @variable(model, w0[i = 1:3], SemiInfinite(w[i], 0, x))
3-element Vector{GeneralVariableRef}:
 w0[1]
 w0[2]
 w0[3]

Thus we create a Julia array variable w0 whose elements w0[i] point to their respective semi-infinite variables w[i](0, x) stored in model. Alternatively, we can make a semi-infinite variable via our restriction syntax:

julia> [w[i](0, x) for i in 1:3]
3-element Vector{GeneralVariableRef}:
 w0[1]
 w0[2]
 w0[3]

These are often useful to define semi-infinite variables directly in constraint expressions. See Restricted Variables to learn about symbolic inline definition of semi-infinite variables.

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) with the alias y0 that is fixed at a value of 0:

julia> @variable(model, y0 == 0, Point(y, 0))
y0

Here we create a Julia variable y0 which points to the point variable y(0). Notice that in the second argument we specify the infinite variable indexed at the appropriate parameter value(s). Point variables automatically inherit attributes of the infinite variable (e.g., bounds, start values, etc.), but these are overwritten with properties specified for the point variable. In this case the lower bound inherited from y(t) is overwritten by instead fixing y(0) to a value of 0.

Alternatively, we can use the convenient restriction syntax:

julia> y(0)
y0

Again this is very useful when embedded directly in constraint expressions (e.g., when defining boundary conditions). See Restricted Variables to learn about symbolic inline definition of point variables.

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)
d

This 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:

  1. Define the variable information pertaining to JuMP.VariableInfo (e.g., bounds, indicate if it is integer, etc.)
  2. Construct a concrete subtype of InfOptVariableType to specify the desired type and its required additional information if appropriate
  3. Build the variable object via JuMP.build_variable
  4. Add the variable object to an InfiniteModel and assign a name via JuMP.add_variable
  5. Create a GeneralVariableRef that points to the variable object stored in the model
Note

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 a Bool it has a lower bound
  • lower_bound::Real: Specifies lower bound value
  • has_ub::Bool: Specifies a Bool it has a upper bound
  • upper_bound::Real: Specifies upper bound value
  • has_fix::Bool: Specifies a Bool it is fixed
  • fixed_value::Real: Specifies the fixed value
  • has_start::Bool: Specifies a Bool it has a start value
  • start::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: Specifies Bool if it is binary
  • integer: Specifies Bool if 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.

Tip

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, ξ)

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
z

For 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, ξ)

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
noname

Please 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:

julia> @variable(model, y_lb >= 0, Infinite(t, x)) # add w/ lower bound
y_lb(t, x)

julia> @variable(model, y_ub <= 10, Infinite(t, x)) # add w/ upper bound
y_ub(t, x)

julia> @variable(model, 0 <= y_bd <= 10, Infinite(t, x)) # add w/ bounds
y_bd(t, x)

julia> @variable(model, y_fix == 42, Infinite(t, x)) # add w/ fixed value 
y_fix(t, x)
Warning

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)) # okay

For 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_fix

Note 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.

Note

Point variables inherit all the bounds of their respective infinite variables by default. This can be overwritten by specifying different ones at creation.

@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 fixed
Note

Bounds cannot be specified on creation for semi-infinite variables. Note that they will inherit these from the infinite variable they depend on. Additional bound be created by directly adding constraints. For example:

@variable(model, y >= 0, Infinite(t, x)) # has lower bound
@variable(model, ys, SemiInfinite(w, 0, x)) # inherits the lower bound
@constraint(model, ys <= 10) # add upper bound to ys

Variable 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)

julia> @variable(model, y_int, Infinite(t, x), Int) # add as integer variable
y_int(t, x)

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)

julia> y_int = @variable(model, variable_type = Infinite(t, x), integer = true)
noname(t, x)

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)

See the Queries and Modification sections further below for more information on how to query/modify variable integralities.

Note

Point variables inherit the integrality of their respective infinite variables by default. This can be overwritten by specifying different ones at creation.

@variable(model, y, Infinite(t, x), Bin) # is binary
@variable(model, yp, Point(w, 0, [0, 0, 0]), Int) # is integer
Note

Integrality cannot be specified for semi-infinite variables. Note that they will inherit these from the infinite variable they depend on. ```

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_start

Moreover, 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...).

Note

Start values be specified for semi-infinite variables. Note that they will inherit these from the infinite variable they depend on.

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")
       end

Moreover, 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
 noname

For 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), 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]])

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)
2

Similarly, 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)

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)
true

Thus, 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) ≤ 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.0

Note 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)

For infinite and semi-infinite variables, the start_value_function should be used instead:

julia> start_value_function(y_sin)
sin (generic function with 18 methods)

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)
true

Other 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)
false

Type 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, GeneralVariableRef[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, ξ)

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)
false

Another 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)
false

Other 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.0

Thus, 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.0

Other 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.0

For infinite variables, this should be done using set_start_value_function. FOr example:

julia> set_start_value_function(myname, sin)

julia> start_value_function(myname)
sin (generic function with 18 methods)

Again 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...).

A number of other techniques exist for the various variable types can be found in the manual below.