3
votes

I'm fairly new to proc optmodel and having trouble sorting out some syntax.

Here is my dataset.

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
1 H 21 18 22 1.2 300
7 H 20 18 22 .8 1000
;
run;

There are a few things going on here:

The IDs within a GRP must have the same x2, which is constrained by MIN and MAX. I now wish to further constrain the increase/decrease of x2 based on the value of y. If y<1, I do not want x2 to go below .95*x1. If y>1, I do not want x2 to exceed 1.05*x1. I've looked online and tried a few things to make this happen. Here is my latest attempt, cond_1 and cond_2 are the problems of interest, as everything else works:

proc optmodel;

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};

number x1{ID}; 
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] 
;
impvar x2byID{i in ID} = x2[GRP[i]];
number y{ID}; 
number z{ID}; 

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

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

con cond_1 {i in ID}: x2[i] >= 
        if y[i]<1 then .95*x1[i] else 0;
con cond_2 {i in ID}: x2[i] <= 
        if y[i]>=1 then 1.05*x1[i] else 99999999;

solve;

create data results from [ID]={ID} x2=x2byID GRP x1 MIN MAX y z;

print x2 maximize;
quit;
2
Yes, you cannot use conditionals in constraints. Is y strictly >=0 ?DomPazz
Yes, y is always greater than 0. Here is one of the sites I was referncing when trying to form the syntax support.sas.com/documentation/cdl/en/ormpug/59679/HTML/default/…pyll
One problem is that x2 is defined of the GRPS set (a string), not the ID set (a number). So '{i in ID} x2[i]' is not going resolve.DomPazz
Also, given X2 is defined over the group, and y is defined over the id, how do you want to handle the fact that y is on both sides of the conditional for each group? Are you simply looking for the most restrictive constraint on X2?DomPazz
I do not want to allow any given ID to exceed the constraints based on its specific y value, thereby restraining the whole group.pyll

2 Answers

3
votes

I would calculate the global max and min in a data step outside of PROC OPTMODEL and then set the values. Like so:

data opt_test;
set opt_test;
if y < 1 then
    min2 = .95*x1;
else
    min2 = 0;

if y>=1 then
    max2 = 1.05*x1;
else
    max2 = 9999999999;

Min_old = min;
max_old = max;

MIN = max(min,min2);
MAX = min(max,max2);
run;

But you have a problem with group G. Use expand to see it.

proc optmodel;

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};

number x1{ID}; 
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] 
;
impvar x2byID{i in ID} = x2[GRP[i]];
number y{ID}; 
number z{ID}; 

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

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

/*con cond_1 {i in ID}: x2[i] >= 
        if y[i]<1 then .95*x1[i] else 0;
con cond_2 {i in ID}: x2[i] <= 
        if y[i]>=1 then 1.05*x1[i] else 99999999;*/

expand;
solve;

create data results from [ID]={ID} x2=x2byID GRP x1 MIN MAX y z;

print x2 maximize;
quit;

You will see that X2[G] is infeasible:

Var x2[G] >= 5.7 <= 5.25

X2[G] starts in [4,6];

For ID=8, X=5 and Y=1.2. By your logic, this sets the max to 5.25 (5*1.2).

Now X2[G] in [4,5.25]

For ID=9, X=6 and Y=0.9. By your logic this sets the min to 5.7 (0.95*6).

X2[G] in [5.7,5.25] <-- BAD!

2
votes

The biggest problem with the model in the question is that the indexing of the var x2 is incorrect. You could fix that by referring to the group of the ID as such:

con cond_1 {i in ID}: x2[GRP[i]] >= 
        if y[i] < 1 then .95*x1[i] else 0;
con cond_2 {i in ID}: x2[GRP[i]] <= 
        if y[i]>=1 then 1.05*x1[i] else 99999999;

but a description of the constraint that reads closer to the business problem is to put a filter in the constraint definition itself:

con Cond_1v2 {i in ID: y[i] < 1} : x2[GRP[i]] >=  .95 * x1[i]; 
con Cond_2v2 {i in ID: Y[i] >= 1}: x2[GRP[i]] <= 1.05 * x1[i];

In either case, the problem becomes infeasible because of the constraint Cond2v2, as you can see using expand (as @DomPazz pointed out), and in particular the expand / iis option, which will print the conflicting constraints when it can determine them:

solve with nlp / iis=on;
expand / iis;