I am setting up a model in SAS based on an excel solver problem.
in SAS I've set this up with Proc Optmodel but I can't seem to calculate the function in the objective statement that's analogous to ABS( SUMPRODUCT(var1..10,var11..20) - szone) to work... The goal is for each row, the difference between each szone and sumproduct should be minimal.
data WORK.sMatrix;
Infile datalines delimiter='#';
input s szone var7 var8 var9 var10 var11 var12;
datalines;
824801#3#9#4#6#6#6#6
696455#3#6#6#6#6#6#3
920946#1#6#1#6#6#6#6
;
Run;
proc optmodel;
set SSET;
set JSET = 1..6;
num a {SSET, JSET};
num szone {SSET};
read data sMatrix into SSET=[s] {j in JSET} <a[s,j] = col('Var'||(j+6))> szone;
print a szone;
var X {SSET, JSET} binary;
min Objective = (sum {s in SSET, j in JSET} a[s,j]*X[s,j] - szone) / (sum {s in SSET} szone[s]);
con OnceChoice {s in SSET}:
sum {j in JSET} X[s,j] = 1;
solve;
print X;
quit;
The LINEARIZE option to automatically linearize is available only in SAS Optimization (SAS Viya) and not SAS/OR (SAS 9), but you can manually linearize as follows:
var Abs {SSET};
con AbsCon1 {s in SSET}:
Abs[s] >= sum {j in JSET} a[s,j]*X[s,j] - szone[s];
con AbsCon2 {s in SSET}:
Abs[s] >= -sum {j in JSET} a[s,j]*X[s,j] + szone[s];
min Objective = (sum {s in SSET} Abs[s]) / (sum {s in SSET} szone[s]);
I think you want this instead:
min Objective = (sum {s in SSET} abs(sum {j in JSET} a[s,j]*X[s,j] - szone[s])) / (sum {s in SSET} szone[s]);
The ABS introduces nonlinearity, but in this case you can use the LINEARIZE option in SAS Viya to automatically linearize the problem:
solve linearize;
After the solve, you can then print the two parts of the numerator:
print {s in SSET} (sum {j in JSET} a[s,j]*X[s,j]) szone;
The resulting optimal solution has objective value 1/7, and the two PRINT statements yield:
Hi Rob. thanks! the suggestion for szone[s] errored. but I think I got this to work:
min Objective = (sum {s in SSET, j in JSET} a[s,j]*X[s,j]) - (sum{s in SSET} szone[s]) / (sum {s in SSET} szone[s]);
You're right the problem is non-linear with ABS, so I also tried with
min Objective = ((sum {s in SSET, j in JSET} a[s,j]*X[s,j]) - (sum{s in SSET} szone[s]))^2 / (sum {s in SSET} szone[s]);
and SAS didn't like it.
I then tried the linearize option but it errored. the following code throws an error:
data WORK.sMatrix; Infile datalines delimiter='#'; input s szone var7 var8 var9 var10 var11 var12; datalines; 824801#3#9#4#6#6#6#6 696455#3#6#6#6#6#6#3 920946#1#6#1#6#6#6#6 ; Run; proc optmodel; ods output PrintTable#3=expt3; set SSET; set JSET = 1..6; num a {SSET, JSET}; num szone {SSET}; read data sMatrix into SSET=[s] {j in JSET} <a[s,j]=col('Var'||(j+6))> szone; print a szone; var X {SSET, JSET} binary; constraint OnceChoice {s in SSET}: sum {j in JSET} X[s,j] = 1; min Objective = abs((sum {s in SSET, j in JSET} a[s,j]*X[s,j]) - (sum{s in SSET} szone[s])) / (sum {s in SSET} szone[s]); solve linearize; print X; quit;
ERROR 22-322: Syntax error, expecting one of the following: /, OBJECTIVE, WITH.
ERROR 76-322: Syntax error, statement will be ignored.
I'm using SAS EG 8.2 (8.2.0.1201) (64 bit)
Thanks Rob!
The LINEARIZE option to automatically linearize is available only in SAS Optimization (SAS Viya) and not SAS/OR (SAS 9), but you can manually linearize as follows:
var Abs {SSET};
con AbsCon1 {s in SSET}:
Abs[s] >= sum {j in JSET} a[s,j]*X[s,j] - szone[s];
con AbsCon2 {s in SSET}:
Abs[s] >= -sum {j in JSET} a[s,j]*X[s,j] + szone[s];
min Objective = (sum {s in SSET} Abs[s]) / (sum {s in SSET} szone[s]);
Relaxing integrality on X is a reasonable approach to try for cases when you cannot linearize, but it is not guaranteed to yield anything close to an integer solution, so you were a bit lucky here. Also, the MIN and MAX operators introduce additional nonlinearity that you should avoid. To relax integrality, the simplest change is instead to use the RELAXINT option:
solve relaxint;
Alternatively, you can change the declaration of X:
var X {SSET, JSET} >= 0 <= 1;
Another way is to set the .LB and .UB suffixes:
var {SSET, JSET};
for {s in SSET, j in JSET} do;
X[s,j].lb = 0;
X[s,j].ub = 1;
end;
Yet another way is to impose explicit constraints:
/* two constraints */
con XlowerBound {s in SSET, j in JSET}:
X[s,j] >= 0;
con XupperBound {s in SSET, j in JSET}:
X[s,j] <= 1;
/* one range constraint */
con XBound {s in SSET, j in JSET}:
0 <= X[s,j] <= 1;
The presolver will replace such constraints with bounds, anyway, so I recommend just using RELAXINT here.
Regarding manual linearization, you can find numerous examples in this book. Look for "linearization" in the Subject Index in the pdf.
The new automatic linearization techniques are discussed here.
The two columns labeled AbsCon1 and AbsCon2 in your table should always have opposite signs. When you correct the table, I think you will see that Abs[s] >= 0. You can also deduce that by adding the two constraints: everything on the right-hand side cancels in pairs, yielding 2*Abs[s] >= 0, equivalently, Abs[s] >= 0.
You're right. My mistake. Thanks so much Rob.
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.
Find more tutorials on the SAS Users YouTube channel.