0
votes

I have a data set that looks like this (SAS 9.4):

data opt_test;
        input ID GRP $ x1 MIN MAX y z;
cards;
2 F 10 9 11 1.5 100
3 F 10 9 11 1.2 50
4 F 11 9 11 .9 20
8 G 5 4 6 1.2 300
9 G 6 4 6 .9 200
;
run;

I want to create a new variable x2 that maximizes a function based on x1, x2, y, and z.

I am having two main problems:

  1. The syntax on my proc optmodel has some errors that I have not been able to fix "Subscript 1 may not be a set" and constraint has incomplete declaration". UPDATE: I figured this part out.

  2. I need for the value of x2 to be the same for all members of the same GRP. So, id 2,3,4 would have same x2. ID 8 and 9 would have same x2.

Below is my attempt. This will ultimately be able to run with sevarl different GRP of varying numbers of ID.

Thanks in advance for any assistance.

proc optmodel;

set<num> ID;

var x2{ID} >= 0;
string GRP{ID};
number x1{ID}; 
number MIN{ID};
number MAX{ID}; 
number y{ID}; 
number z{ID}; 




max sales=sum{i in ID}(x2[i])*(1-(x2[i]-x1[i])*y[i]/x1[i])*z[i];

con floor_METRIC1{i in ID}: x2[i]>=MIN[i];
con ceiling_METRIC1{i in ID}: x2[i]<=MAX[i];

read data opt_test into
        ID=[ID]
        GRP
        x1 
        MIN 
        MAX 
        y 
        z 
        ;

solve;

print x2;
quit;
1

1 Answers

1
votes

If you want the value of x2 to be the same for all ids in the same group, then you only need one variable x2 per group. To keep track of which ids are in which group you could use an array of sets indexed by group:

set<num> ID;

string GRP{ID};
set GRPS                 = setof{i in ID} GRP[i];
set IDperGRP{gi in GRPS} = {i in ID: GRP[i] = gi};

When you use = (as opposed to init), you provide OPTMODEL with a function you don't need to update later. If you change any of the GRP or ID data, optmodel will recompute GRPS and IDperGRP as needed.

Now you can use the GRPS set and the IDperGRP array of sets to rewrite your objective to more closely match your business rule:

max sales = sum{gi in GRPS} sum{i in IDperGRP[gi]} 
            (x2[gi]) * (1-(x2[gi]-x1[i])*y[i]/x1[i]) * z[i];

Writing the expression this way makes it clearer (to me at least) that it can be simplified further as long as x1, y, and z are constants.

Adding the new sets also makes it clearer (to me at least) that the bounds from floor_METRIC1 and ceiling_METRIC1 can be combined to tighten the domain of x2. Since MIN and MAX are constants, you can move the constraints into direct variable bounds by adding >= and <= clauses to the declaration of x2. Since x2 will now depend on MIN and MAX, you will have to declare those before x2. IMHO that makes your intent clearer:

number MIN{ID};
number MAX{ID}; 
var x2{gi in GRPS} >= max{i in IDperGRP[gi]} MIN[i] 
                   <= min{i in IDperGRP[gi]} MAX[i] 
;