BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.

I have below data:

id $ cost profit_1 profit_2 score;
A -1 10 11 500
B -0.6 10 5 700
C -0.75 10 10.5 700
D -0.75 10.5 10 600
E -0.65 12 11 300
F -0.65 12 13 350;
Following code does the work when considering profits only:

proc optmodel;
set <str> ITEMS;
num cost {ITEMS};
set CASES = 1..2;
num profit {ITEMS, CASES};
read data have into ITEMS=[id] cost {j in CASES} <profit[id,j]=col('profit_'||j)>;
var X {ITEMS, CASES} binary;
max TotalProfit = sum {i in ITEMS, j in CASES} profit[i,j] * X[i,j];
con ChooseOne {i in ITEMS}: sum {j in CASES} X[i,j] <= 1;
con Budget: sum {i in ITEMS, j in CASES} cost[i] * X[i,j] <= 1.5;
solve;
print X;
quit;

but I want to add another constraint on score.
Something like SCORE must be greater than 500, i tried inserting following -
num score {ITEMS}

then in read data statement- SCORE[id] = col('SCORE')

And finally the constraint- con MinScore SCORE{i in ITEMS}: SCORE[id]>=500

but this is leading to infeasible solution because of cases where SCORE >=500.

Can anyone help in how to do this and what am I doing wrong?

 

1 ACCEPTED SOLUTION

Accepted Solutions
RobPratt
SAS Super FREQ

Here are four ways, in increasing order of efficiency.

 

1. Impose explicit constraint:

/* if X[i,j] = 1 then score[i] >= 500 */
con MinScore {i in ITEMS, j in CASES}: score[i] >= 500 * X[i,j];

2. Fix variables to 0:

/* if score[i] < 500 then X[i,j] = 0 */
for {i in ITEMS: score[i] < 500} for {j in CASES} fix X[i,j] = 0;

3. Modify index set after reading data:

read data have into ITEMS=[id] cost score {j in CASES} <profit[id,j]=col('profit_'||j)>;
ITEMS = {i in ITEMS: score[i] >= 500};

4. Use WHERE= in READ DATA:

read data have(where=(score >= 500)) into ITEMS=[id] cost score {j in CASES} <profit[id,j]=col('profit_'||j)>;

See also this doc example.

View solution in original post

4 REPLIES 4
RobPratt
SAS Super FREQ

Here are four ways, in increasing order of efficiency.

 

1. Impose explicit constraint:

/* if X[i,j] = 1 then score[i] >= 500 */
con MinScore {i in ITEMS, j in CASES}: score[i] >= 500 * X[i,j];

2. Fix variables to 0:

/* if score[i] < 500 then X[i,j] = 0 */
for {i in ITEMS: score[i] < 500} for {j in CASES} fix X[i,j] = 0;

3. Modify index set after reading data:

read data have into ITEMS=[id] cost score {j in CASES} <profit[id,j]=col('profit_'||j)>;
ITEMS = {i in ITEMS: score[i] >= 500};

4. Use WHERE= in READ DATA:

read data have(where=(score >= 500)) into ITEMS=[id] cost score {j in CASES} <profit[id,j]=col('profit_'||j)>;

See also this doc example.

thepushkarsingh
Quartz | Level 8
Thanks a lot for your response. I was able to do 3 and 4, but it was subsetting my observations in output, so wasn't inclined. Finally I found 2 but was quite sure that this can be done in CON statement. Thanks again for 1. Very grateful for what I am learning from you.
RobPratt
SAS Super FREQ

Glad to help.  By the way, because of the ChooseOne constraint, you can tighten the MinScore constraint as follows:

con MinScore {i in ITEMS}: score[i] >= 500 * sum {j in CASES} X[i,j];

In either case, the MILP presolver will force X[i,j] to 0 if score[i] < 500.

hackathon24-white-horiz.png

The 2025 SAS Hackathon Kicks Off on June 11!

Watch the live Hackathon Kickoff to get all the essential information about the SAS Hackathon—including how to join, how to participate, and expert tips for success.

YouTube LinkedIn

Discussion stats
  • 4 replies
  • 1396 views
  • 2 likes
  • 2 in conversation