Equations in GAMSPy are associated with the symbolic algebraic relationships
that will be used to generate the constraints in a model. The algebraic
relationships are defined by using constants, mathematical operators,
functions, sets, parameters and variables. As with variables,
an equation may be defined over a group of sets and in turn map into several
individual constraints associated with the elements of those sets.
Equations are specified in two steps. First they have to be declared, afterwards
they get a definition. Finally, in order
to be considered, they have to be added to an instance of gamspy.Model through
equations argument of its constructor. A handy shortcut to retrieve all equations
contained in a gamspy.Container is the
A GAMSPy equation must be declared before it may
be referenced by creating an instance of
The declaration of an equation is similar to a set, parameter,
or variable, in that it requires a container, a name, a domain (if applicable),
and an optional description. For a complete list of available arguments, see
An example for equation declarations adapted from trnsport.py is shown below for illustration:
from gamspy import Container, Set, Equation m = Container() i = Set(m, name="i", records=["seattle", "san-diego"]) j = Set(m, name="j", records=["new-york", "chicago", "topeka"]) supply = Equation( m, name="supply", domain=[i], description="observe supply limit at plant i", ) demand = Equation( m, name="demand", domain=[j], description="satisfy demand at market j", )
gamspy.Container is created and two sets
j are added.
Those will be used as domain for the equations about to be declared.
Two instances of
gamspy.Equation are created and assigned to Python
variables for later use. In this case both equations are one dimensional
and use the sets
demand) as domain.
In typical circumstances an indexed equation declaration implies that a block
of constraints will be generated. For example, equation
supply implies that
two constraints will be generated, one for each element of the set
After declaring equations they have to be defined. The definition of an
equation specifies its algebraic structure by using sets, parameters,
logical operators and functions. A definition is made
by assigning an expression of the form
expression [==|>=|<=] expression
to the Python variable that references the
gamspy.Equation instance. For
indexed equations, the index operator is used to specify the domain:
equation[index_list] = expression [==|>=|<=] expression
index_list consists of one or multiple sets which correspond to the
sets that were used when the equation was declared using the
Equation constructor. One or more logical conditions are optional.
After the assignment operator
=, the left hand side of the equation follows.
It is an arbitrary algebraic expression which may include variables, parameters,
functions, and constants among other items. The left hand side is followed by one
of the supported relational operators which define the relation between the left hand side
and the right hand side of the equation:
>=: Greater than or equal
<=: Less than or equal
Note that other operators like
!= are not supported. Furthermore
the operator is only significant in case of equations of
type="regular" which is
the default. See the Equation Types section for more details about the available
A zero dimensional or scalar equation which is not declared over one or multiple sets
has to use the ellipsis literal
[...] instead of the indexing operator like
equation[...] = expression [==|>=|<=] expression
Note that each equation has to be declared before it can be defined.
A scalar equation will produce one equation in the associated optimization problem. The following is an example of a scalar equation definition from the ramsey.py model:
utility[...] = W == Sum(t, beta[t] * gams_math.log(C[t]))
utility defined above is an example of a scalar equation that uses the scalar
W. In addition, scalar equations may contain indexed variables like
However, they must occur with an indexed operator such as
Product, unless the indexed
variables refer to a singleton set (a set with only one element).
All the set references in scalar equations are within the scope of indexed operators or
they refer to singleton sets, thus many variable, set and parameter references can be
included in one equation. In addition, GAMSPy also allows for equations to be defined
over a domain, thereby developing a compact representation for constraints. The
index sets to the left of the Python assignment operator
= are called the domain
of definition of the equation.
Domain checking ensures that the domain over which an equation is defined is the set (or the sets) or a subset of the set (or the sets) over which the equation was declared.
As a corollary, domain checking also catches the error of the indices being listed in an inconsistent order. For example, declaring an equation with
domain=[s,t]and then naming it in the definition as
myequation[t,s]causes an error (unless
tare aliases of the same set). For more information, see section Domain Checking in the GAMS documentation.
The following is an example of indexed equation definitions, again taken from the
trnsport.py model. Besides the already introduced sets
b are used as well as the
from gamspy Parameter, Sum capacities = [["seattle", 350], ["san-diego", 600]] demands = [["new-york", 325], ["chicago", 300], ["topeka", 275]] a = Parameter(m, name="a", domain=[i], records=capacities) b = Parameter(m, name="b", domain=[j], records=demands) supply[i] = Sum(j, x[i, j]) <= a[i] demand[j] = Sum(i, x[i, j]) >= b[j]
Given the set
i containing the elements
following two individual equations are generated for
supply["seattle"] = Sum(j, x["seattle", j]) <= a["seattle"] supply["san-diego"] = Sum(j, x["san-diego", j]) <= a["san-diego"]
For the equation
demand, the number of generated constraints in three:
demand["new-york"] = Sum(i, x[i, "new-york"]) >= b["new-york"] demand["chicago"] = Sum(i, x[i, "chicago"]) >= b["chicago"] demand["topeka"] = Sum(i, x[i, "topeka"]) >= b["topeka"]
Combining Equation Declaration and Definition#
Sometimes it can be handy to combine an equation declaration and definition.
This is possible by using the optional
definition argument of
Equation constructor. A combined declaration and definition of the
preceding example would look like follows:
from gamspy import Container, Equation, Sum supply = Equation( m, name="supply", domain=[i], description="observe supply limit at plant i", definition=Sum(j, x[i, j]) <= a[i], ) demand = Equation( m, name="demand", domain=[j], description="satisfy demand at market j", definition=Sum(i, x[i, j]) >= b[j], )
The arrangement of the terms in the equation is a matter of choice, but often a particular one is chosen because it makes the model easier to understand.
Using Labels Explicitly in Equations#
Sometimes it can be necessary to refer to specific set elements in equations.
This can be done as with parameters - by using quotes or double quotes around
the label. Consider the following example from the model cta.py where
"total" is used on the second index position of the variable
addrow[i, k] = Sum(v[i, j, k], t[v]) == 2 * t[i, "total", k]
Logic equations defined by using
type="boolean" in the
use boolean algebra and have to evaluate to
1) to be feasible. Most
boolean functions can be used with the a Python operator as well as an equivalent method
gamspy.math, but some do exist in the latter only. The following
table gives an overview of the available boolean functions in GAMSPy:
Equations can have different types. Most of the time, the default
is sufficient, but there are other types for specific needs
and modelling practices. The following table gives an overview of the available
equation types in GAMSPy:
This is the default equation type which is suitible for ordinary equations using the
No relationship implied between left-hand side and right-hand side. This equation type is ideally suited for use in MCP models and in variational inequalities.
Equation is defined by external programs. See the section External Equations in the GAMS documentation.
Conic constraint. See the section Conic Programming in the GAMS documentation.
Boolean equations. See the section Logic Equations.
Expressions in Equation Definitions#
The arithmetic operators and some of the functions provided by GAMSPy may be used in equation definitions. But also certain native Python operators can be used. Consider the following example adapted from the model ramsey.py demonstrating the use of parentheses and exponentiation:
production[t] = Y[t] == a * (K[t] ** b) * (L[t] ** (1 - b))
Functions in Equation Definitions#
The functions provided by GAMSPy can be found in
Note that some functions like
normal are not allowed in equation definitions.
The use of the other functions is determined by the type of arguments in the model.
There are two types of arguments:
Exogenous arguments: The arguments are known. Parameters and variable attributes (for example,
.mattributes) are used as arguments. The expression is evaluated once when the model is being set up and most mathematical functions are allowed.
Endogenous arguments: The arguments are variables and therefore unknown at the time of model setup. The function will be evaluated many times at intermediate points while the model is being solved. Note that the occurrence of any function with endogenous arguments implies that the model is not linear.
There are two types of functions allowing endogenous arguments: smooth functions
and discontinuous functions. Smooth functions are continuous functions with
continuous derivatives (like
log). Discontinuous functions
include continuous functions with discontinuous derivatives
and discontinuous functions (like
Smooth functions may be used routinely in nonlinear models. However, discontinuous
functions may cause numerical problems and should be used only if unavoidable,
and only in a special model type called
DNLP. For more details on model types see
The best way to model discontinuous functions is with binary variables.
The result is a model of the type
MINLP. The GAMS model
demonstrates this formulation technique for the functions
sign. See also section Reformulating DNLP Models in the GAMS documentation.
We strongly discourage the use of the
DNLP model type.
Preventing Undefined Operations in Equations#
Some operations are not defined at particular values of the arguments. Two examples
are division by
0 and the
0. While this can easily be identified
at model setup for exogenous functions and expressions, it is a lot more difficult
when the terms involve variables. The expression may be evaluated many times when
the problem is being solved and the undefined result may arise only under certain
cases. One way to avoid an expression becoming undefined is adding bounds to the
respective variables. Consider the following example from the ramsey.py
C.lo[t] = 0.001 utility[...] = W == Sum(t, beta[t] * gams_math.log(C[t]))
Specifying a lower bound for
C[t] that is slightly larger than
log function from becoming undefined.
Similar to variables, equations have five attributes. Five values are
associated with each unique label combination of every equation. They
are denoted by the properties
.scale. A list of the attributes and their description is given in
the following table:
Negative infinity for
Positive infinity for
Level of the equation in the current solution, equal to the level of all terms involving variables.
Marginal value for equation. This attribute is reset to a new value when a model containing the equation is solved. The marginal value for an equation is also known as the shadow price for the equation and in general not defined before solution but if present it can help to provide a basis for the model
Numerical scaling factor that scales all coefficients in the equation.
This is only used when the model attribute
This attribute allows to assign equations to stages in a stochastic
program or other block structured model. Its current use is limited to
2-stage stochastic programs solved with
Note that all properties except for
.stage contain the
attribute values of equations after a solution of the model has been obtained.
For some solvers it can be useful to specify marginal values
.m and level
.l on input to provide starting information. Also note that the
marginal value is also known as the dual or shadow price. Roughly speaking, the
.m of an equation is the amount by which the value of the
objective variable would change if the equation level were moved one unit.
Equation attributes may be referenced in expressions and can be used to specify
starting values. In addition, they serve for scaling purposes and for reporting
after a model was solved. Here the attributes are not accessed via the Python
properties, but are contained in the data of the equation itself which can be
retrieved via the
records property as the following example shows:
transport = Model( m, name="transport", equations=m.getEquations(), problem="LP", sense=Sense.MIN, objective=Sum((i, j), c[i, j] * x[i, j]), ) transport.solve() print(supply.records)
i level marginal lower upper scale 0 seattle 350.0 -0.0 -inf 350.0 1.0 1 san-diego 550.0 0.0 -inf 600.0 1.0
The level values of the equation
supply are displayed. As expected, there
are two level values, one for each member of the set
i over which the
supply was defined.
In addition to the equation attributes introduced above, there are a number of equation attributes that cannot be assigned but may be used in computations. They are given in the following table:
The difference between the lower and upper bounds of an equation.
Slack lower bound
Slack from equation lower bound. This is defined as the greater of two values: zero or the difference between the level value and the lower bound of an equation.
Slack upper bound
Slack from equation upper bound. This is defined as the greater of two values: zero or the difference between the upper bound and the level value of an equation.
Minimum slack from equation bound. This is defined as the minimum of two values: the slack from equation lower bound and the slack from equation upper bound.
Amount by which an equation is infeasible falling below its lower bound or above its upper bound. This is defined as max(0, lower bound - level, level - upper bound).