The OPTMODEL procedure can call functions and subroutines that are compiled by the FCMP procedure. You can use FCMP functions wherever a function expression is allowed in PROC OPTMODEL. Use the CALL statement to call FCMP subroutines. The following example defines a function in the FCMP procedure and calls it within PROC OPTMODEL:
proc fcmp outlib=work.funcs.test; /* arithmetic geometric mean */ function agm(a0, b0); a=a0; b = b0; if a<=0 or b<=0 then return(0); do until( a - b < a/1e12 ); a1 = 0.5*a + 0.5*b; b1 = sqrt(a*b); a = a1; b = b1; end; return(a); endsub; run; /* libraries must be specified with the CMPLIB option */ option cmplib=work.funcs; proc optmodel; print (agm(1,2)); /* find x where agm(1,x) == 23 */ var x init 1; num c = 23; min z=(agm(1,x)-c)^2; solve; print x;
FCMP subroutines can return data by updating OPTMODEL numeric and string parameters, which are passed as arguments in a CALL statement. These arguments are declared using the OUTARGS statement in the PROC FCMP subroutine definition. The OPTMODEL argument must be specified with an identifier expression . The following code shows a simple example of output arguments. The maximum length of output strings from OUTARGS arguments is restricted to the argument length before the call, as described in the section CALL Statement.
proc fcmp outlib=work.funcs.test; subroutine do_sqr(x, sq, text $); outargs sq, text; sq = x*x; text = 'This is an example of output arguments'; endsub; run; option cmplib=work.funcs; proc optmodel; string s init repeat(' ', 79); /* reserve 80 bytes */ number n; call do_sqr(7, n, s); print s n;
This code produces the output in FigureĀ 5.67.
You can pass OPTMODEL arrays to FCMP functions and subroutines that accept matrix arguments. The array must match the type and dimensions of the FCMP argument declaration. The argument in the OPTMODEL CALL statement must be specified using the following syntax:
array-name . suffix
The following code passes a constant matrix to an FCMP function. The array coeff
contains the coefficients of a polynomial, which in this case defines a simple quadratic formula, .
proc fcmp outlib=work.funcs.test; function evalpoly(x, coeff[*]); z = 0; do i = dim1(coeff) to 1 by -1; z = z * x + coeff[i]; end; return (z); endsub; run; option cmplib=work.funcs; proc optmodel; num coeff{1..3} = [1, -2, 1]; var x; min z=evalpoly(x, coeff); solve; print x;
An array that is used as a matrix argument must be indexed like an FCMP matrix. In other words, the array index set
must be specified as the crossproduct of one or more range expressions
(such as 1..N
) where the lower bound and step size are 1. Set parameters that are used for indexing must contain a crossproduct of ranges,
but the element order is not important. The following code shows some examples of suitable and unsuitable array declarations:
proc fcmp outlib=work.funcs.test; subroutine mattest(x[*]); put x[1]=; endsub; subroutine mattest2(x[*,*]); put x[1,1]=; endsub; run; option cmplib=work.funcs; proc optmodel; /* the following arrays can be used as matrices */ num N init 3; num mat1{1..N} init 0; call mattest(mat1); /* OK */ set S1 = 1..5; num mat2{S1} init 0; call mattest(mat2); /* OK */ set S2 = {S1,S1}; num mat3{S2} init 0; call mattest2(mat3); /* OK */ num mat4{S1 cross S1} init 0; call mattest2(mat4); /* OK */ num L init 1; num mat5{L..N} init 0; call mattest(mat5); /* OK */ set S3 init S1; num mat6{S3} init 0; call mattest(mat6); /* OK */ /* some errors are detected at execution time */ S3 = 2..5; call mattest(mat6); /* ERROR: lower bound not 1 */ S3 = {1, 3, 4, 5}; call mattest(mat6); /* ERROR: missing index 2 */ L = 0; call mattest(mat5); /* ERROR: lower bound not 1 */ /* the following arrays cannot be used as matrices */ num arr1{1..10 by 3}; /* step size is not 1 */ call mattest(arr1); /* ERROR */ num arr2{i in 1..N, j in 1..N: j >= i}; /* selection expression used */ call mattest2(arr2); /* ERROR */ num arr3{i in 1..N, j in 1..i}; /* index dependency on 'i' */ call mattest2(arr3); /* ERROR */
Not all PROC FCMP functionality is compatible with PROC OPTMODEL; in particular, the following FCMP functions are not supported and should not be called within your FCMP function definitions: READ_ARRAY, WRITE_ARRAY, RUN_MACRO, and RUN_SASFILE. In many cases, OPTMODEL capabilities can replace these functions. Matrix arguments can be used in place of the READ_ARRAY function by using the READ DATA statement to load the matrix in PROC OPTMODEL. Similarly, you can replace the WRITE_ARRAY function in an FCMP subroutine by copying the matrix to an output argument and using the OPTMODEL procedure to write the matrix. You can use the SUBMIT statement in place of the RUN_MACRO and RUN_SASFILE functions.
The SAS CMPLIB= system option specifies where to look for previously compiled functions and subroutines. For more information about the CMPLIB= system option, see SAS System Options: Reference. FCMP functions can be used in distributed mode with the NLP multistart solver. The needed PROC FCMP compiled routines are automatically packaged and distributed. For more information about the multistart solver, see ChapterĀ 10: The Nonlinear Programming Solver, in this book.
Note: Distributed mode requires SAS High-Performance Optimization.
PROC OPTMODEL uses derivatives values that are provided by FCMP when they are available. FCMP cannot provide derivatives with respect to array arguments, so PROC OPTMODEL must use finite differences to compute these derivatives. Also, if the CMPOPT= SAS system option specifies the FUNCDIFFERENCING value, then PROC OPTMODEL uses its own finite differencing for FCMP functions.