The OPTMODEL Procedure
OPTMODEL Expression Extensions
- AND Aggregation Expression
- CARD Function
- CROSS Expression
- DIFF Expression
- Equality Expression
- IF-THEN/ELSE Expression
- IN Expression
- Index Set Expression
- INTER Expression
- INTER Aggregation Expression
- MAX Aggregation Expression
- MIN Aggregation Expression
- Nil Constant Expression
- OR Aggregation Expression
- PROD Aggregation Expression
- Range Expression
- Set Constructor Expression
- Set Literal Expression
- SETOF Aggregation Expression
- SLICE Expression
- SUM Aggregation Expression
- SYMDIFF Expression
- Tuple Expression
- UNION Expression
- UNION Aggregation Expression
- WITHIN Expression
PROC OPTMODEL defines several new types of expressions for the manipulation of sets. Aggregation operators combine values of an expression that is evaluated over the members of an index set. Other operators create new sets by combining existing sets, or they test relationships between sets. PROC OPTMODEL also supports an IF expression operator that can conditionally evaluate expressions. These and other such expressions are described in this section.
AND Aggregation Expression
AND { index-set } logic-expression
The AND aggregation operator evaluates the logical expression logic-expression jointly for each member of the index set index-set. The index set enumeration finishes early if the logic-expression evaluation produces a false value (zero or missing). The expression returns 0 if a false value is found or returns 1 otherwise. The following statements demonstrate both a true and a false result:
proc optmodel;
put (and{i in 1..5} i < 10); /* returns 1 */
put (and{i in 1..5} i NE 3); /* returns 0 */
CARD Function
CARD ( set-expression )
The CARD function returns the number of members of its set operand. For example, the following statements produce the output 3 since the set has 3 members:
proc optmodel;
put (card(1..3));
CROSS Expression
set-expression-1 CROSS set-expression-2
The CROSS expression returns the crossproduct of its set operands. The result is the set of tuples formed by concatenating the tuple value of each member of the left operand with the tuple value of each member of the right operand. Scalar set members are treated as tuples of length 1. The following statements demonstrate the CROSS operator:
proc optmodel;
set s1 = 1..2;
set<string> s2 = {'a', 'b'};
set<number, string> s3=s1 cross s2;
put 's3 is ' s3;
set<number, string, number> s4 = s3 cross 4..5;
put 's4 is ' s4;
This code produces the output in Figure 35.
Figure 35: CROSS Expression Output
s3 is {<1,'a'>,<1,'b'>,<2,'a'>,<2,'b'>}
s4 is {<1,'a',4>,<1,'a',5>,<1,'b',4>,<1,'b',5>,<2,'a',4>,<2,'a',5>,<2,'b',4>,<2,
'b',5>}
|
DIFF Expression
set-expression-1 DIFF set-expression-2
The DIFF operator returns a set that contains the set difference of the left and right operands. The result set contains values that are members of the left operand but not members of the right operand. The operands must have compatible set types. The following statements evaluate and print a set difference:
proc optmodel;
put ({1,3} diff {2,3}); /* outputs {1} */
Equality Expression
set-expression-1 = set-expression-2
set-expression-1 = set-expression-2
The equality comparison operators are extended to support the comparison of sets. Sets are considered equal if they have the same members. The following code demonstrates set comparison:
proc optmodel;
put (/a b/ = /a b/); /* true */
put (/a b/ = /a/); /* false */
put ({} = /a b/); /* false */
put (1..3 = {1,3,2}); /* true, ignores order */
IF-THEN/ELSE Expression
IF logic-expression THEN expression-2 ELSE expression-3
The IF-THEN/ELSE expression evaluates the logical expression logic-expression and returns the result of evaluating the second or third operand expression according to the logical test result. If the logic-expression is true (nonzero and nonmissing), then the result of evaluating expression-2 is returned. If the logic-expression is false (zero or missing), then the result of evaluating expression-3 is returned. The other subexpression that is not selected is not evaluated.
An ELSE clause is matched during parsing with the nearest IF-THEN clause that does not have a matching ELSE. The ELSE clause can be omitted for numeric expressions; the resulting IF-THEN is handled as if a default ELSE 0 clause were supplied.
Use the IF-THEN/ELSE expression to handle special cases in models. For example, an inventory model based on discrete time periods might require special handling for the first or last period. In the following example the initial inventory for the first period is assumed to be fixed:
proc optmodel;
number T;
var inv{1..T}, order{1..T};
number sell{1..T};
number inv0;
. . .
/* balance inventory flow */
con iflow{i in 1..T}:
inv[i] = order[i] - sell[i] +
if i=1 then inv0 else inv[i-1];
. . .
The IF-THEN/ELSE expression in the example models the initial inventory for a time period i. Usually the inventory value is the inventory at the end of the previous period, but for the first time period the inventory value is given by the inv0 parameter. The iflow constraints are linear because the IF-THEN/ELSE test subexpression does not depend on variables and the other subexpressions are linear.
IF-THEN/ELSE can be used as either a set expression or a scalar expression. The type of expression depends on the subexpression between the THEN and ELSE keywords. The type used affects the parsing of the subexpression that follows the ELSE keyword because the set form has a lower operator precedence. For example, the following two expressions are equivalent because the numeric IF-THEN/ELSE has a higher precedence than the range operator (..):
IF logic THEN 1 ELSE 2 .. 3
(IF logic THEN 1 ELSE 2) .. 3
But the set form of IF-THEN/ELSE has lower precedence than the range expression operator. So the following two expressions are equivalent:
IF logic THEN 1 .. 2 ELSE 3 .. 4
IF logic THEN (1 .. 2) ELSE (3 .. 4)
The IF-THEN and IF-THEN/ELSE operators always have higher precedence than the logic operators. So, for example, the following two expressions are equivalent:
IF logic THEN numeric1 < numeric2
(IF logic THEN numeric1) < numeric2
It is best to use parentheses when in doubt about precedence.
IN Expression
expression IN set-expression
expression NOT IN set-expression
The IN expression returns 1 if the value of the left operand is a member of the right operand set. Otherwise, the IN expression returns 0. The NOT IN operator logically negates the returned value. Unlike the DATA step, the right operand is an arbitrary set expression. The left operand can be a tuple expression. The following example demonstrates the IN and NOT IN operators:
proc optmodel;
set s = 1..10;
put (5 in s); /* outputs 1 */
put (-1 not in s); /* outputs 1 */
set<num, str> t = {<1,'a'>, <2,'b'>, <2,'c'>};
put (<2, 'b'> in t); /* outputs 1 */
put (<1, 'b'> in t); /* outputs 0 */
Index Set Expression
{ index-set }
The index set expression returns the set of members of an index set. This expression is distinguished from a set constructor (see the section Set Constructor Expression) because it contains a list of set expressions.
The following statements use an index set with a selection expression that excludes the value 3:
proc optmodel;
put ({i in 1..5 : i NE 3}); /* outputs {1,2,4,5} */
INTER Expression
set-expression-1 INTER set-expression-2
The INTER operator returns a set that contains the intersection of the left and right operands. This is the set that contains values that are members of both operand sets. The operands must have compatible set types.
The following statements evaluate and print a set intersection:
proc optmodel;
put ({1,3} inter {2,3}); /* outputs {3} */
INTER Aggregation Expression
INTER { index-set } set-expression
The INTER aggregation operator evaluates the set-expression for each member of the index set index-set. The result is the set that contains the intersection of the set of values that were returned by the set-expression for each member of the index set. An empty index set causes an expression evaluation error.
The following statements use the INTER aggregation operator to compute the value of {1,2,3,4} {2,3,4,5}
{3,4,5,6}:
proc optmodel;
put (inter{i in 1..3} i..i+3); /* outputs {3,4} */
MAX Aggregation Expression
MAX { index-set } expression
The MAX aggregation operator evaluates the scalar-valued expression expression for each member of the index set index-set. The result is the maximum of the values that are returned by the expression. Missing numeric values are handled with the SAS numeric sort order; a missing value is treated as smaller than any nonmissing numeric value. If the index set is empty and the expression is numeric, then the result is the negative number that has the largest absolute value representable on the machine. Expression evaluation produces an error if the index set is empty and the expression has a string value.
The following example produces the outputs 0.5 and *****:
proc optmodel;
put (max{i in 2..5} 1/i);
put (max{i in 2..5} repeat('*',i-1));
MIN Aggregation Expression
MIN { index-set } expression
The MIN aggregation operator evaluates the scalar-valued expression expression for each member of the index set index-set. The result is the minimum of the values that are returned by the expression. Missing numeric values are handled with the SAS numeric sort order; a missing value is treated as smaller than any nonmissing numeric value. If the index set is empty and the expression is numeric, then the result is the largest positive number representable on the machine. Expression evaluation produces an error if the index set is empty and the expression has a string value.
The following example produces the outputs 0.2 and **:
proc optmodel;
put (min{i in 2..5} 1/i);
put (min{i in 2..5} repeat('*',i-1));
Nil Constant Expression
_NIL_
The _NIL_ keyword evaluates to a nil value. For more information about nil values, see the section Nil Values.
OR Aggregation Expression
OR { index-set } logic-expression
The OR aggregation operator evaluates the logical expression logic-expression for each member of the index set index-set. The index set enumeration finishes early if the logic-expression evaluation produces a true value (nonzero and nonmissing). The result is 1 if a true value is found, or 0 otherwise. The following statements demonstrate both a true and a false result:
proc optmodel;
put (or{i in 1..5} i = 2); /* returns 1 */
put (or{i in 1..5} i = 7); /* returns 0 */
PROD Aggregation Expression
PROD { index-set } expression
The PROD aggregation operator evaluates the numeric expression expression for each member of the index set index-set. The result is the product of the values that are returned by the expression. This operator is analogous to the operator used in mathematical notation. If the index set is empty, then the result is 1.
The following example uses the PROD operator to evaluate a factorial:
proc optmodel;
number n = 5;
put (prod{i in 1..n} i); /* outputs 120 */
Range Expression
expression-1 .. expression-n BY expression
The range expression returns the set of numbers from the specified arithmetic progression. The sequence proceeds from the left operand value up to the right operand limit. The increment between numbers is 1 unless a different value is specified with a BY clause. If the increment is negative, then the progression is from the left operand down to the right operand limit. The result can be an empty set.
For compatibility with the DATA step iterative DO loop construct, the keyword TO can substitute for the range (..) operator.
The limit value is not included in the resulting set unless it belongs in the arithmetic progression. For example, the following range expression does not include 30:
proc optmodel;
put (10..30 by 7); /* outputs {10,17,24} */
The actual numbers that the range expression "f..l by i" produces are in the arithmetic sequence
where
and represents the relative machine precision. The limit is adjusted to avoid arithmetic roundoff errors.
PROC OPTMODEL represents the set specified by a range expression compactly when the value is stored in a parameter location, used as a set operand of an IN or NOT IN expression, used by an iterative DO loop, or used in an index set. For example, the following expression is evaluated efficiently:
999998.5 IN 1..1000000000
Set Constructor Expression
{ expression-1
, …expression-n
}
The set constructor expression returns the set of the expressions in the member list. Duplicated values are added to the set only once. A warning message is produced when duplicates are detected. The constructor expression consists of zero or more subexpressions of the same scalar type or of tuple expressions that match in length and in element types.
The following statements output a three-member set and warn about the duplicated value 2:
proc optmodel;
put ({1,2,3,2}); /* outputs {1,2,3} */
The following example produces a three-member set of tuples, using PROC OPTMODEL parameters and variables. The output is displayed in Figure 36.
proc optmodel;
number m = 3, n = 4;
var x{1..4} init 1;
string y = 'c';
put ({<'a', x[3]>, <'b', m>, <y, m/n>});
Figure 36: Set Constructor Expression Output
{<'a',1>,<'b',3>,<'c',0.75>}
|
Set Literal Expression
/ members /
The set literal expression provides compact specification of simple set values. It is equivalent in function to the set constructor expression but minimizes typing for sets that contain numeric and string constant values. The set members are specified by members, which are literal values. As with the set constructor expression, each member must have the same type.
The following statement specifies a simple numeric set:
/1 2.5 4/
The set contains the members 1, 2.5, and 4. A string set could be specified as follows:
/Miami 'San Francisco' Seattle 'Washington, D.C.'/
This set contains the strings ’Miami’, ’San Francisco’, ’Seattle’, and ’Washington, D.C.’. You can specify string values in set literals without quotation marks when the text follows the rules for a SAS name. Strings that begin with a digit or contain blanks or other special characters must be specified with quotation marks.
Specify tuple members of a set by enclosing the tuple elements within angle brackets (<elements>). The tuple elements can be specified with numeric and string literals. The following example includes the tuple elements <’New York’, 4.5> and <’Chicago’, -5.7>:
/<'New York' 4.5> <Chicago -5.7>/
SETOF Aggregation Expression
SETOF { index-set } expression
The SETOF aggregation operator evaluates the expression expression for each member of the index set index-set. The result is the set that is formed by collecting the values returned by the operand expression. The operand can be a tuple expression. For example, the following statements produce a set of tuples of numbers with their squared and cubed values:
proc optmodel;
put (setof{i in 1..3}<i, i*i, i**3>);
Figure 37 shows the displayed output.
Figure 37: SETOF Aggregation Expression Output
{<1,1,1>,<2,4,8>,<3,9,27>}
|
SLICE Expression
SLICE ( < element-1, …element-n > , set-expression )
The SLICE expression produces a new set by selecting members in the operand set that match a pattern tuple. The pattern tuple is specified by the element list in angle brackets. Each element in the pattern tuple must specify a numeric or string expression. The expressions are used to match the values of the corresponding elements in the operand set member tuples. You can also specify an element by using an asterisk (*). The sequence of element values that correspond to asterisk positions in each matching tuple is combined into a tuple of the result set. At least one asterisk element must be specified.
The following statements demonstrate the SLICE expression:
proc optmodel;
put (slice(<1,*>, {<1,3>, <1,0>, <3,1>}));
put (slice(<*,2,*>, {<1,2,3>, <2,4,3>, <2,2,5>}));
These statements produce the output in Figure 38.
Figure 38: SLICE Expression Output
{3,0}
{<1,3>,<2,5>}
|
For the first PUT statement, <1,*> matches set members <1,3> and <1,0> but not <3,1>. The second element of each matching set tuple, corresponding to the asterisk element, becomes the value of the resulting set member. In the second PUT statement, the values of the first and third elements of the operand set member tuple are combined into a two-position tuple in the result set.
The following statements use the SLICE expression to help compute the transitive closure of a set of tuples representing a relation by using Warshall’s algorithm. In these statements the set parameter dep represents a direct dependency relation.
proc optmodel;
set<str,str> dep = {<'B','A'>, <'C','B'>, <'D','C'>};
set<str,str> cl;
set<str> cn;
cl = dep;
cn = (setof{<i,j> in dep} i) inter (setof{<i,j> in dep} j);
for {node in cn}
cl = cl union (slice(<*,node>,cl) cross slice(<node,*>,cl));
put cl;
The local dummy parameter node in the FOR statement iterates over the set cn of possible intermediate nodes that can connect relations transitively. At the end of each FOR iteration, the set parameter cl contains all tuples from the original set in addition to all transitive tuples found in the current or previous iterations.
The output in Figure 39 includes the indirect and direct transitive dependencies from the set dep.
Figure 39: Warshall's Algorithm Output
{<'B','A'>,<'C','B'>,<'D','C'>,<'C','A'>,<'D','B'>,<'D','A'>}
|
A special form of index-set-item uses the SLICE expression implicitly. See the section More on Index Sets for details.
SUM Aggregation Expression
SUM { index-set } expression
The SUM aggregation operator evaluates the numeric expression expression for each member in the index set index-set. The result is the sum of the values that are returned by the expression. If the index set is empty, then the result is 0. This operator is analogous to the operator that is used in mathematical notation. The following statements demonstrate the use of the SUM aggregation operator:
proc optmodel;
put (sum {i in 1..10} i); /* outputs 55 */
SYMDIFF Expression
set-expression-1 SYMDIFF set-expression-2
The SYMDIFF expression returns the symmetric set difference of the left and right operands. The result set contains values that are members of either the left or right operand but are not members of both operands. The operands must have compatible set types.
The following example demonstrates a symmetric difference:
proc optmodel;
put ({1,3} symdiff {2,3}); /* outputs {1,2} */
Tuple Expression
< expression-1 , …expression-n
>
A tuple expression represents the value of a member in a set of tuples. Each scalar subexpression inside the angle brackets represents the value of a tuple element. This form is used only with IN, SETOF, set constructor, and relational expressions.
The following statements demonstrate the tuple expression:
proc optmodel;
put (<1,2,3> in setof{i in 1..2}<i,i+1,i+2>);
put ({<1,'a'>, <2,'b'>} cross {<3,'c'>, <4,'d'>});
The first PUT statement checks whether the tuple <1, 2, 3> is a member of a set of tuples. The second PUT statement outputs the crossproduct of two sets of tuples that are constructed by the set constructor.
These statements produce the output in Figure 40.
Figure 40: Tuple Expression Output
1
{<1,'a',3,'c'>,<1,'a',4,'d'>,<2,'b',3,'c'>,<2,'b',4,'d'>}
|
You can specify tuple expressions as operands of the relational operators. For the equality operators (= and =), tuples are equal if their corresponding elements are equal. Ordering operators (<, <=, >, and >=) compare tuples lexicographically. That is, the corresponding tuple elements are compared left to right, and the leftmost pair of unequal elements determines the result of the comparison.
The following code demonstrates tuple comparison:
proc optmodel;
put (<1,'a'> = <1,'a'>)=; /* true, all elements equal */
put (<1,'a'> = <1,'b'>)=; /* false, at least one element unequal */
put (<1,'a'> ~= <1,'b'>)=; /* true, at least one element unequal */
put (<1,2,5> < <1,3,4>)=; /* true, compared left to right, 2 < 3 */
These statements produce the output in Figure 41.
Figure 41: Tuple Comparison Output
<1,'a'> = <1,'a'>=1 <1,'a'> = <1,'b'>=0 <1,'a'> ~= <1,'b'>=1 <1,2,5> < <1,3,4>=1 |
UNION Expression
set-expression-1 UNION set-expression-2
The UNION expression returns the set union of the left and right operands. The result set contains values that are members of either the left or right operand. The operands must have compatible set types. The following example performs a set union:
proc optmodel;
put ({1,3} union {2,3}); /* outputs {1,3,2} */
UNION Aggregation Expression
UNION { index-set } set-expression
The UNION aggregation expression evaluates the set-expression for each member of the index set index-set. The result is the set union of the values that are returned by the set-expression. If the index set is empty, then the result is an empty set.
The following statements demonstrate a UNION aggregation. The output is the value of {1,2,3,4} {2,3,4,5}
{3,4,5,6}.
proc optmodel;
put (union{i in 1..3} i..i+3); /* outputs {1,2,3,4,5,6} */
WITHIN Expression
set-expression-1 WITHIN set-expression-2
set-expression NOT WITHIN set-expression
The WITHIN expression returns 1 if the left operand set is a subset of the right operand set and returns 0 otherwise. (That is, the operator returns true if every member of the left operand set is a member of the right operand set.) The NOT WITHIN form logically negates the result value. The following statements demonstrate the WITHIN and NOT WITHIN operators:
proc optmodel;
put ({1,3} within {2,3}); /* outputs 0 */
put ({1,3} not within {2,3}); /* outputs 1 */
put ({1,3} within {1,2,3}); /* outputs 1 */