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;
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.