Initializing help system before first use

Piecewise linear functions

Other types of constraints are available for modelling. Piecewise linear constraints allow to define a variable as a piecewise linear function of another. The function does not have to be continuous, but please see the Optimizer's manual for information on how discontinuities are dealt with.

The most efficient way to model piecewise linear functions is through the API function problem.addpwlcons.

x = xp.var(lb=-xp.infinity)
y = xp.var()
z1 = xp.var(lb=-xp.infinity)
z2 = xp.var(lb=-xp.infinity)

p = xp.problem(x,y,z1,z2)

# Define z1 and z2 as a piecewise linear functions of x. Two functions
# are defined.
p.addpwlcons([x, x],   # independent variable of each function
             [z1, z2], # created variables
             [0,4],    # index of the first breakpoints for z1 and z2
             [0,4, 4  7, -2,-1,1,2],   # x values of the breakpoints
             [4,12,11,20,-2,-2,2,2])   # y values
p.setObjective(z1 + 2*y)
p.addConstraint(z2 <= y)
p.solve()

The above example creates variables x, y, z1, and z2, then constrains z1 and z2 to be (piecewise linear) functions of x, to be used with y in other constraints and in the objective function.

The Xpress Python interface provides another, more intuitive way of specifying such a function with the method xpress.pwl, which is passed a dictionary associating intervals (defined as tuples of two elements) with linear functions. The code below exemplifies the use of xpress.pwl to construct two functions. The first, which is included into the objective of the problem, is the piecewise linear function 2x+4 for x∈[0,4] and 3x-1 for x∈[4,7]; the second function is constant at -2 for x ≤ -1, it is equal to 2x for x∈[-1,1], and is constant at 2 for x ≥2:

x = xp.var(lb=-xp.infinity)
y = xp.var()
p = xp.problem(x, y)

# Create objective and constraint directly, without first creating
# piecewise linear functions.

p.setObjective(xp.pwl({(0, 4): 2*x + 4, (4, 7): 3*x - 1}) + 2*y)
p.addConstraint(xp.pwl({(-xp.infinity, -1):  -2,
                        (-1, 1):            2*x,
                        (1, xp.infinity):     2}) <= y)
p.solve()

Here the definition of auxiliary variables z1 and z2 becomes redundant as the calls to xpress.pwl do not need any extra variable. The dictionary that is used in xpress.pwl has tuples of two elements each as keys and linear expressions (or constants) as values.

The tuples are treated as (pairwise disjoint) intervals, hence every tuple (a,b) in the set of keys must be such that a≤b and such that, for any two tuples (a,b) and (c,d) in the keys, either b≤c or d≤a.

Piecewise linear functions should be defined over the whole domain of the independent variable (x in the example above); with the syntax of xpress.pwl, it is possible to omit a portion of the domain of the independent variable; in that case the value of the function is taken to be zero.

Piecewise linear functions can be used as operators when defining an optimization problem. For instance, one could write the constraint

y + 3*z**2 <= 3*xp.pwl({(0, 1): x + 4, (1, 3): 1})

Note that regardless of how a piecewise linear constraint is formulated, there must always be only one independent variable, i.e., the piecewise linear function is always univariate. In addition, piecewise constant functions need a further specification as a variable does not appear in the values: for this case, one can specify the key-value pair None: x as in the example below.

# Set a piecewise CONSTANT objective
p.setObjective(xp.pwl({(0, 1): 4, (1, 2): 1, (2,3): 3, None: x})