Hello. I recently posted a request for assistance in giving preference to certain resources/variables when optimizing.
Proc Optmodel MILP Scheduling: Pausing resource reallocation and giving preference to allocation
@RobPratt `s solution was very informative, particularly this bit that illustrates how to create sets within sets:
con DesiredConstraint1 {<PROD_ID, slot, day> in PROD_SLOT_DAY, <(PROD_ID), slot2, day2> in PROD_SLOT_DAY: dayToNum[day2] in dayToNum[day]+1..dayToNum[day]+N}:
I have another issue with set manipulation(?). I am using MILP to maximize revenue based on the allocation of product IDs to a set of time slots over a set of days.
var PROD_ASSIGN{PROD_SLOT_DAY}
a tuple of <string PROD_ID, num SLOT, string DAY>
I would like constrain allocation of the primary variable PROD_ID based on a secondary numeric feature, call it "FTR", such that:
So, for example, let PROD_ID={...,"ABC", "DEF"...}, slot={...9,10,...}, and day={...,"Wednesday", "Thursday",...}. Assume that both ABC and DEF both have identical FTR=10.
I have used the following code in a separate constraint to prevent a given PROD_ID from being assigned to more than 2 consecutive slots (indexed by "start" here):
PROD_ASSIGN[prod_id, start, day] + PROD_ASSIGN[prod_id, start+1, day] + PROD_ASSIGN[prod_id, start+2, day] <= 2
I am just constraining the number of times a given PROD_ID can appear in set of consecutive slots.
This is rather straight-forward sum as PROD_ID is a component of the PROD_ASSIGN optimization variable.
In my example this time though, I would like to constrain the number of times that a secondary feature, which is not a component of PROD_ASSIGN, appears vertically (i.e. in consecutive slots within a given day) and horizontally (i.e. in a given slot across consecutive days).
Any suggestions of how I can do this?
In the attached "Build Input Data" code, I have created a data set called SECOND_FTR_PRODS that contains a numeric FTR code for each of the corresponding PROD_IDs. My sample optimization code is included in "Optimize Schedule". I am not married to the idea of a numeric FTR. If string is more convenient/easy-to-solve, I can convert numeric values to string as needed.
con Constraint1 {<PROD_ID, slot, day> in PROD_SLOT_DAY, <PROD_ID2, slot2, (day)> in PROD_SLOT_DAY: FTR[PROD_ID] = FTR[PROD_ID2] and slot2 = slot+1}:
PROD_ASSIGN[PROD_ID, slot, day] + PROD_ASSIGN[PROD_ID2, slot2, day] <= 1;
con Constraint2 {<PROD_ID, slot, day> in PROD_SLOT_DAY, <PROD_ID2, (slot), day2> in PROD_SLOT_DAY: FTR[PROD_ID] = FTR[PROD_ID2] and dayToNum[day2] = dayToNum[day]+1}:
PROD_ASSIGN[PROD_ID, slot, day] + PROD_ASSIGN[PROD_ID2, slot, day2] <= 1;
con Constraint1 {<PROD_ID, slot, day> in PROD_SLOT_DAY, <PROD_ID2, slot2, (day)> in PROD_SLOT_DAY: FTR[PROD_ID] = FTR[PROD_ID2] and slot2 = slot+1}:
PROD_ASSIGN[PROD_ID, slot, day] + PROD_ASSIGN[PROD_ID2, slot2, day] <= 1;
con Constraint2 {<PROD_ID, slot, day> in PROD_SLOT_DAY, <PROD_ID2, (slot), day2> in PROD_SLOT_DAY: FTR[PROD_ID] = FTR[PROD_ID2] and dayToNum[day2] = dayToNum[day]+1}:
PROD_ASSIGN[PROD_ID, slot, day] + PROD_ASSIGN[PROD_ID2, slot, day2] <= 1;
@RobPratt Thank you for your reply.
Based on your previous solution, I assume your current solution would also need a lookup array like this:
/*Note: I converted input numeric FTR to string FTR_c: strip(put(FTR, best3.)) */
set <string> FTR_chars;
read data SECOND_FTR_PRODS into FTR_chars = [FTR_c];
num FTR {FTR_chars};
num ftr_cnt init 0;
for {d in FTR_chars} do;
ftr_cnt = ftr_cnt + 1;
FTR[d] = ftr_cnt;
end;
print FTR;
This however generates an error, ERROR: The array subscript 'FTR[AAA]' is invalid at line 185 column 103. , when this code is called as 'AAA' is a PROD_ID value:
FTR[PROD_ID] = FTR[PROD_ID2]
This equality takes the string PROD_ID as input rather than the FTR_c values that were used to create the FTR array.
I could recreate the array using PROD_ID rather than FTR_C like:
num FTR {PROD_IDs}; /*PROD_IDs = set of string PROD_ID values defined in the attached SAS code*/
num ftr_cnt init 0;
for {d in PROD_IDs} do;
ftr_cnt = ftr_cnt + 1;
FTR[d] = ftr_cnt;
end;
print FTR;
but this just assigns an integer to each PROD_ID value. It doesn't incorporate the FTR/FTR_c values from the SECOND_FTR_PRODS table, which is what I am trying to accomplish this time.
I think I need some kind of lookup array/matrix/? that takes PROD_ID as an input and outputs the corresponding FTR/FTR_c value.
Any thoughts on how I might create that?
For the code I suggested, the following suffices:
num FTR {PROD_IDs};
read data SECOND_FTR_PRODS into [PROD_ID] FTR;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 16. Read more here about why you should contribute and what is in it for you!
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.