Discontinuous piecewise-linear functions

Describes the use of discontinuous piecewise-linear functions in OPL.

OPL also allows you to write discontinuous piecewise-linear functions. This is the case when, in the syntax of a piecewise-linear function with slopes and breakpoints, two successive breakpoints are identical and the value associated with the second one is considered to be a step instead of a slope. The CPLEX and the CP Optimizer engines behave differently with respect to what limit they consider as the discontinuity value. Because CPLEX allows either of these limits, note that the anchor point used to ground the breakpoints and slopes must not reside at the discontinuity. Otherwise, the piecewise-linear function would not be uniquely defined.

Behavior with the CPLEX engine

Example 1: the sign function

The following piecewise function:

piecewise{0 -> 0;2 -> 0; 0}(1,1) x; 

has a slope of 0 up to breakpoint 0, then a step of 2 at this breakpoint, then a slope of 0. It takes the value 1 at point 1. This piecewise represents the function sign() which returns the sign (1 or -1) of its argument, as represented in Figure 1 below.

Figure 1. The discontinuous piecewise-linear function sign()
Diagram of the discontinuous function “sign”

Then this model

dvar float x;
dvar float signx;

dvar float y;
dvar float signy;

maximize x;
subject to {
   x == 2;
   signx == piecewise{0->0; 2->0; 0}(1,1) x;
   y == -2;
   signy == piecewise{0->0; 2->0; 0}(1,1) y;
}

gives the following output:

Final solution with objective 2.0000:
x = 2.0000;
signx = 1.0000;
y = -2.0000;
signy = -1.0000;

Figure 1 shows that the value of the sign function at the breakpoint is either -1 (on the left-hand slope) or 1 (on the right-hand slope).

For example, this model takes this into account and sets the constraint x==y; on both values.

dvar float x;
dvar float signx;

dvar float y;
dvar float signy;

maximize signx-signy;
subject to {
   x == y;
   signx == piecewise{0->0; 2->0; 0}(1,1) x;
   signy == piecewise{0->0; 2->0; 0}(1,1) y;
}

This model solves with the following output:

Final solution with objective 2:
signx = 1;
signy = -1;
x = 0;
y = 0;

Example 2: discontinuous cost

The following piecewise function

piecewise{0->0; 10->0; 0->10; 5->10; 0->20; 5->20; 0} (5,10) unit;

represents a discontinuous cost.

This function is illustrated in Figure 2, Discontinuous costs, for the values summarized in Table 1.

Table 1. A discontinuous cost function
Values of Unit Cost
&lt0 0
0 to 10 10
10 to 20 15
>20 20
Figure 2. Discontinuous costs
Diagram of a discontinuous pwl costs function

Different behavior with the CP Optimizer engine

Consider the following model:

//using CP;
dvar int x in -10..10;
dvar int signx;
dvar int y in -10..10;
dvar int signy;
maximize signx-signy;
   subject to {
      x == y;
      signx == piecewise{0->0; 2->0; 0}(1,1) x;
      signy == piecewise{0->0; 2->0; 0}(1,1) y;
   }
execute
{
   writeln(signx-signy);
}

Depending on which solving engine you write for, you get a different result because CPLEX and CP Optimizer do not handle limit values in the same way.

  • If you comment out the using CP; statement, the model is solved by the CPLEX engine and the result is 2 because CPLEX handles symmetry in such a way that it interprets either limit as the discontinuity value.

  • However, if you uncomment the using CP; statement, the model is solved by the CP Optimizer engine and the result is 0 because CP Optimizer considers the left limit as the discontinuity value.