Hi Rob
I am building some layers on top of this model . The model referenced in the link below, will get the cheapest way to ship from Point A to Point B respecting the weight and volume of boxes available for that lane. It would factor in the costs and make the minimal cost decision. You had helped me to tune the constraints. Now the new problem I am trying to solve is this. I am not sure if I have formulated it correct. I am looking for suggestions and I can change my model to behave the way I wanted it to behave. The one thing that I can right away think is to cut the number of possibilities by grouping similar charge buckets with same rate basis. Eg. Charge type A, charge type B all have a Volume based calculation. So i can just club all those and put a one aggregated number so that the problem size is small and solve time is faster.
Anyways, here is the problem I want to solve: This is a ocean movement in this example. There are some origins (Point A, Shanghai in our example) and destinations (Point B, Atlanta in our example) generally. In my example, there is only one origin and one destination. For this origin port, there are many vendors in Shanghai area. There are two different Method of Loading (CFS and CYS). The logic is that any vendor can send to CFS or CYS. There is a node called T1 which is just the day number of an year and T2 which is the week number of that year. Specific rules to use CFS and CYS are below.
For using CYS: This is like each vendor taking a full container to their factory and loading all their stuff in it and sending to CYS from where it goes to Atlanta. This will be daily, meaning, each vendor, at the end of that day, if he can send all his goods by himself in this CYS, he can send. Next day he can take another container(s) and sends to Atlanta via this CYS option. In other words, if a vendor goes to CYS method, then only his stuff is there in the container and no other vendors' stuff gets contaminated in his container. It is like an exclusive/dedicated container only for that vendor and only for that day's load.
For CFS: If a vendor is not able to send via CYS daily (because it may be expensive to ship so little on a day) , then he has an option of consolidating his goods with other such vendors, and so, many such vendors go to a CFS, which is like a consolidation center. However at this CFS, the goods are consolidated for 1 week. At the end of week 1, all the vendors stuff are consolidated at CFS and sent finally to Atlanta. In other words, we will just consolidate across a week (T2) for CFS . For CYS, consolidation is at T1 level (day) and for CFS it is weekly (T2 level).
I am trying to decide, if a vendor can send all his stuff to CYS daily or if it is not feasible , he can send to CFS to be consolidated with other vendors for the week and then sent. I want to see for a given week, which vendor should send via CYS (daily) and which vendors go to CFS (weekly), obviously those who cannot do CYS their only other option is to get consolidated at CFS and send.
Here is my code. The CFSSpecific constraint is the one that are not right but CYSSpecific may be right, i am not sure. Appreciate your support. Have attached data as well. Am I missing something else? The rates are the same for a lane for a given ratetype and MOL. But I have it set up for all the combinations of dates and weeks in the data. Not sure if it is efficient way as well.
proc optmodel;
set <str> ORG;
read data CASUSER.Unique_ORG into ORG = [ORG];
set <str> DES;
read data CASUSER.Unique_DES into DES = [DES];
set <str> BOX;
read data CASUSER.Unique_BOX into BOX = [BOX];
set <str> MOL;
read data CASUSER.Unique_MOL into MOL = [MOL];
set <str> T1;
read data CASUSER.Unique_T1 into T1 = [T1];
set <str> T2;
read data CASUSER.Unique_T2 into T2 = [T2];
/*Read ISN Wt and Volume for a given ISN* - START */
set <str> ISN;
str Org_ISN {ISN};
str Des_ISN {ISN};
num Volume_ISN {ISN} init 0;
num Weight_ISN {ISN} init 0;
read data CASUSER.InputData_AssetMix into ISN=[ISN] Org_ISN=ORG Des_ISN=DES Volume_ISN=Volume Weight_ISN=Weight;
/*Read ISN Wt and Volume for a given ISN* - END*/
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - START */
num Volume_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Volume_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
num Weight_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Weight_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
read data CASUSER.BOXSPECS into [ORG DES BOX MOL T1 T2]
Volume_Capacity=Volume_Capacity
Weight_Capacity=Weight_Capacity
Volume_Min=Volume_Min
Weight_Min=Weight_Min;
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - END */
/* A Binary variable that is 0 or 1 for a given asset for a given lane, 0 indicated not available, 1- available */
num Is_BoxAvalable_for_a_lane {ORG,DES,BOX,MOL,T1,T2};
read data CASUSER.BOXSpecs into [ORG Des Box Mol T1 T2] Is_BoxAvalable_for_a_lane = BoxAvailability;
/* Decision Variable and Associated Impvars - START */
var BoxesNeeded {ISN,BOX,MOL,T1,T2} >= 0 integer;
var Proportion {ISN,BOX,MOL,T1,T2} >= 0 <= 1;
impvar VolumeInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Volume_ISN[i] * Proportion[i,b,m,d,w];
impvar WeightInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Weight_ISN[i] * Proportion[i,b,m,d,w];
NUM BigM;
READ DATA CASUSER.BigM INTO BigM=Volume;
/* var Is_MOL {MOL} binary; */
var Is_ISN_MOL {ISN,MOL} binary;
/* Decision Variable and Associated Impvars - END */
/* Define Rates and Implicit Variables - START */
set <str,str,str,str,str,str> PerBox_Based_Rate_NoZeroes;
num PerBox_Based_Rate {PerBox_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerBox" and Ratetype='Linehaul' and Rate>0))
into PerBox_Based_Rate_NoZeroes=[ORG DES BOX MOL T1 T2] PerBox_Based_Rate=Rate;
impvar PerBox_Based_Costs = sum {i in ISN, b in BOX,m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerBox_Based_Rate_NoZeroes}
PerBox_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w];
set <str,str,str,str,str,str> PerVol_Based_Rate_NoZeroes_Carrier_BOL;
num PerVol_Based_Rate_Carrier_BOL {PerVol_Based_Rate_NoZeroes_Carrier_BOL};
read data CASUSER.BOXRATE
(where=(RateBasis="PerVolUOM" and Ratetype='Carrier_BOL' and Rate>0))
into PerVol_Based_Rate_NoZeroes_Carrier_BOL=[ORG DES MOL BOX T1 T2] PerVol_Based_Rate_Carrier_BOL=Rate;
impvar PerVol_Based_Costs_Carrier_BOL = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerVol_Based_Rate_NoZeroes_Carrier_BOL}
PerVol_Based_Rate_Carrier_BOL[Org_ISN[i],Des_ISN[i],b,m,d,w] * VolumeInsideBox[i,b,m,d,w];
set <str,str,str,str,str,str> PerShp_Based_Rate_NoZeroes_Entry;
num PerShp_Based_Rate_Entry {PerShp_Based_Rate_NoZeroes_Entry};
read data CASUSER.BOXRATE
(where=(RateBasis="PerShipment" and Ratetype='Export_Declaration' and Rate>0))
into PerShp_Based_Rate_NoZeroes_Entry=[ORG DES BOX MOL T1 T2] PerShp_Based_Rate_Entry=Rate;
impvar PerShp_Based_Costs_Entry = sum {i in ISN, b in BOX,m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerShp_Based_Rate_NoZeroes_Entry}
PerShp_Based_Rate_Entry[Org_ISN[i],Des_ISN[i],b,m,d,w];
/* impvar BoxesNeededatMOL{m in MOL}=sum{i in ISN,b in BOX} BoxesNeeded [i,b,m]; */
/*Define Implicit Variables - END */
Min TotalCost = PerBox_Based_Costs + PerVol_Based_Costs_Carrier_BOL+PerShp_Based_Costs_Entry;
/* Constraints - START */
for {i in ISN, b in BOX,m in MOL,d in T1,w in T2: Is_BoxAvalable_for_a_lane[Org_ISN[i],Des_ISN[i],b,m,d,w] = 0}
fix BoxesNeeded[i,b,m,d,w] = 0;
con SumProportionToOne {i in ISN}:
sum {b in BOX,m in MOL,d in T1,w in T2} Proportion[i,b,m,d,w] = 1;
con OneMOL{i in ISN}:
sum{M in MOL} Is_ISN_MOL[i,m] = 1;
con CYSSpecific {m in {'CYS'},i in ISN}:
sum {b in BOX,d in T1,w in T2} VolumeInsideBox[i,b,'CYS',d,w]= Volume_ISN[i]*Is_ISN_MOL[i,'CYS'];
con CFSSpecific {m in {'CFS'},w in T2}:
sum {i in ISN, b in BOX, d in T1} VolumeInsideBox[i,b,'CFS',d,w]= sum{i in ISN} Volume_ISN[i]*Is_ISN_MOL[i,'CFS'];
expand CFSSpecific; /* I want to sum volume of all ISNs in a given week only across all box types but I know it is not right */
/* The following constraints are for determining type and number of boxes and the associated wt and volume constraints */
con Volume_Constraint {i in ISN, b in BOX, m in MOL}:
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m] * BoxesNeeded[i,b,m] >= VolumeInsideBox[i,b,m];
con Volume_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL}:
Volume_Min[Org_ISN[i],Des_ISN[i],b,m] * BoxesNeeded[i,b,m] <= VolumeInsideBox[i,b,m];
con Weight_Constraint {i in ISN, b in BOX, m in MOL}:
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m] * BoxesNeeded[i,b,m] >= WeightInsideBox[i,b,m];
con Weight_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL}:
Weight_Min[Org_ISN[i],Des_ISN[i],b,m] * BoxesNeeded[i,b,m] <= WeightInsideBox[i,b,m];
/* Constraints - END */
solve with milp / decomp=(method=concomp);
a network diagram just in case
Your READ DATA statements refer to tables that I do not see in the attachment. Please attach all required tables, ideally as separate data sets rather than as sheets in an Excel file.
Hi Rob
Ok. I have attached here 4 files (a) BoxRate (b) BoxSpecs (c) InputDataAssetMix and (d) ISN_T1_T2_Final. These are small excel files. I do not know how to send SAS Datasets as such. Out of the four files above, I am not using ISN_T1_T2_Final yet but I have created it may be for future. The other three I am using definitely. Here is my updated code below. Please use this one and also the input files as these are the right ones. The problem that I am grappling with is that if for CYS, each ISN will be evaluated if it makes sense to go via CYS on a day. Else, if it is not, then, all the remaining ISNs for that particular week will be consolidated and sent to CFS. For this consolidated CFS load, we will need to find the asset mix. I was thinking of some ideas one of which was to create a set of ISN, MOL, Box, T1 and T2 that are possible and then use this and which is why we have this ISN_T1_T2_Final. But I did not go far after just creating the table. Anyway, can you please help me how to solve this problem? Thanks always Rob.
proc optmodel;
set <str> ORG;
read data CASUSER.Unique_ORG into ORG = [ORG];
set <str> DES;
read data CASUSER.Unique_DES into DES = [DES];
set <str> BOX;
read data CASUSER.Unique_BOX into BOX = [BOX];
set <str> MOL;
read data CASUSER.Unique_MOL into MOL = [MOL];
set <str> T1;
read data CASUSER.Unique_T1 into T1 = [T1];
set <str> T2;
read data CASUSER.Unique_T2 into T2 = [T2];
/*Read ISN Wt and Volume for a given ISN* - START */
set <str> ISN;
str Org_ISN {ISN};
str Des_ISN {ISN};
num Volume_ISN {ISN} init 0;
num Weight_ISN {ISN} init 0;
read data CASUSER.InputData_AssetMix into ISN=[ISN] Org_ISN=ORG Des_ISN=DES Volume_ISN=Volume Weight_ISN=Weight;
/*Read ISN Wt and Volume for a given ISN* - END*/
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - START */
num Volume_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Volume_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
num Weight_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Weight_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
read data CASUSER.BOXSPECS into [ORG DES BOX MOL T1 T2]
Volume_Capacity=Volume_Capacity
Weight_Capacity=Weight_Capacity
Volume_Min=Volume_Min
Weight_Min=Weight_Min;
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - END */
/* A Binary variable that is 0 or 1 for a given asset for a given lane, 0 indicated not available, 1- available */
num Is_BoxAvalable_for_a_lane {ORG,DES,BOX,MOL,T1,T2};
read data CASUSER.BOXSpecs into [ORG Des Box Mol T1 T2] Is_BoxAvalable_for_a_lane = BoxAvailability;
/* Decision Variable and Associated Impvars - START */
var BoxesNeeded {ISN,BOX,MOL,T1,T2} >= 0 integer;
var Proportion {ISN,BOX,MOL,T1,T2} >= 0 <= 1;
impvar VolumeInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Volume_ISN[i] * Proportion[i,b,m,d,w];
impvar WeightInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Weight_ISN[i] * Proportion[i,b,m,d,w];
NUM BigM;
READ DATA CASUSER.BigM INTO BigM=Volume;
var Is_ISN_MOL {ISN,MOL} binary;
/* Decision Variable and Associated Impvars - END */
/*Define Rates and Implicit Variables - START */
set <str,str,str,str,str,str> PerBox_Based_Rate_NoZeroes;
num PerBox_Based_Rate {PerBox_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerBox" and Ratetype='Linehaul' and Rate>0))
into PerBox_Based_Rate_NoZeroes=[ORG DES BOX MOL T1 T2] PerBox_Based_Rate=Rate;
impvar PerBox_Based_Costs = sum {i in ISN, b in BOX,m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerBox_Based_Rate_NoZeroes}
PerBox_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w];
set <str,str,str,str,str,str> PerVol_Based_Rate_NoZeroes;
num PerVol_Based_Rate {PerVol_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerVolUOM" and Ratetype='Carrier_BOL' and Rate>0))
into PerVol_Based_Rate_NoZeroes=[ORG DES MOL BOX T1 T2] PerVol_Based_Rate=Rate;
impvar PerVol_Based_Costs = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerVol_Based_Rate_NoZeroes}
PerVol_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * VolumeInsideBox[i,b,m,d,w];
set <str,str,str,str,str,str> PerShp_Based_Rate_NoZeroes_Entry;
num PerShp_Based_Rate_Entry {PerShp_Based_Rate_NoZeroes_Entry};
read data CASUSER.BOXRATE
(where=(RateBasis="PerShipment" and Ratetype='Export_Declaration' and Rate>0))
into PerShp_Based_Rate_NoZeroes_Entry=[ORG DES MOL BOX T1 T2] PerShp_Based_Rate_Entry=Rate;
impvar PerShp_Based_Costs_Entry = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerShp_Based_Rate_NoZeroes_Entry}
PerShp_Based_Rate_Entry[Org_ISN[i],Des_ISN[i],b,m,d,w];
/*Define Implicit Variables - END */
Min TotalCost = PerBox_Based_Costs + PerVol_Based_Costs+PerShp_Based_Costs_Entry;
/* Constraints - to chose box type and number*/
for {i in ISN, b in BOX,m in MOL,d in T1,w in T2: Is_BoxAvalable_for_a_lane[Org_ISN[i],Des_ISN[i],b,m,d,w] = 0}
fix BoxesNeeded[i,b,m,d,w] = 0;
con SumProportionToOne {i in ISN}:
sum {b in BOX,m in MOL,d in T1,w in T2} Proportion[i,b,m,d,w] = 1;
con Volume_Constraint {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] >= VolumeInsideBox[i,b,m,d,w];
con Volume_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Volume_Min[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] <= VolumeInsideBox[i,b,m,d,w];
con Weight_Constraint {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] >= WeightInsideBox[i,b,m,d,w];
con Weight_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Weight_Min[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] <= WeightInsideBox[i,b,m,d,w];
/*End of Constraints relating to chosing box type and number*/
/* Constraint to enforce other stuff*/
con OneMOL{i in ISN}:
sum{M in MOL} Is_ISN_MOL[i,m] = 1;
con CYSSpecific {m in {'CYS'},d in T1,w in T2,i in ISN}:
sum {b in BOX} VolumeInsideBox[i,b,'CYS',d,w]= Volume_ISN[i]*Is_ISN_MOL[i,'CYS'];
expand CYSSpecific;
con CFSSpecific {m in {'CFS'},d in T1}:
sum {i in ISN, b in BOX,w in T2} VolumeInsideBox_T1[i,b,'CFS',d,w]= sum{,d in T1} Volume_T1[d]*Is_MOL_T1[d,'CFS'];
solve with MILP / decomp varsel=ryanfoster presolver=basic;
solve with milp;
Thank you for this update, but the Unique_* and BigM tables are still missing.
Rob
Sorry. Here are the tables. I am not using BigM table anyways as of now in the constraints.
here is the rest of the two files, unique_T1 and unique_T2
@Santha, for attaching data sets, you may find it easier to use @SASJedi's data-to-data-step macro to convert a SAS data set into the appropriate DATA step/datalines code.
The macro creates a text file containing the DATA step/datalines code that you can then Ctrl/A, Ctrl/C, and Ctrl/V into a SAS program (or into the "Insert SAS Code" icon on the communities page).
You can read more about it and download the macro here
Example below and attached of the SASHELP.AIR data set, but you can run it for any data set.
%data2datastep(AIR,SASHELP,,C:\...\MYAIR.txt,144);
@ChanceTGardener thank you. Appreciate your support
Rob - can you let me know if you need any other input tables or something?
Thanks for your support as always.
@RobPratt - I know the syntax is not even right for the constraint "CFSSpecific". I was trying to work more on the constraint "CYSSpecific" but I was not successful. I have this latest code that I was trying but in vain. I am now taking a pause and wait for recommendations from you to see if I can structure the model more efficiently, let alone the logic and syntax.
proc optmodel;
set <str> ORG;
read data CASUSER.Unique_ORG into ORG = [ORG];
set <str> DES;
read data CASUSER.Unique_DES into DES = [DES];
set <str> BOX;
read data CASUSER.Unique_BOX into BOX = [BOX];
set <str> MOL;
read data CASUSER.Unique_MOL into MOL = [MOL];
set <str> T1;
read data CASUSER.Unique_T1 into T1 = [T1];
set <str> T2;
read data CASUSER.Unique_T2 into T2 = [T2];
/*Read ISN Wt and Volume for a given ISN* - START */
set <str> ISN;
str Org_ISN {ISN};
str Des_ISN {ISN};
num Volume_ISN {ISN} init 0; num Volume_T1{T1} init 0;
num Weight_ISN {ISN} init 0;
read data CASUSER.InputData_AssetMix into ISN=[ISN] Org_ISN=ORG Des_ISN=DES Volume_ISN=Volume Weight_ISN=Weight;
/*Read ISN Wt and Volume for a given ISN* - END*/
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - START */
num Volume_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Volume_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
num Weight_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Weight_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
read data CASUSER.BOXSPECS into [ORG DES BOX MOL T1 T2]
Volume_Capacity=Volume_Capacity
Weight_Capacity=Weight_Capacity
Volume_Min=Volume_Min
Weight_Min=Weight_Min;
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - END */
/* A Binary variable that is 0 or 1 for a given asset for a given lane, 0 indicated not available, 1- available */
num Is_BoxAvalable_for_a_lane {ORG,DES,BOX,MOL,T1,T2};
read data CASUSER.BOXSpecs into [ORG Des Box Mol T1 T2] Is_BoxAvalable_for_a_lane = BoxAvailability;
num Flag {ISN,BOX,MOL,T1,T2};
read data CASUSER.ISN_T1_T2_FINAL into [ISN BOX MOL T1 T2]Flag=Flag;
set LegitCombo_ISN = {i in ISN, b in BOX, m in MOL, d in T1, w in T2: Flag[i,b,m,d,w]=1};
/* Decision Variable and Associated Impvars - START */
var BoxesNeeded {LegitCombo_ISN} >= 0 integer;
var Proportion {LegitCombo_ISN} >= 0 <= 1;
impvar VolumeInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Volume_ISN[i] * Proportion[i,b,m,d,w];
impvar WeightInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Weight_ISN[i] * Proportion[i,b,m,d,w];
impvar VolumeInsideBox_T1 {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Volume_T1[d] * Proportion[i,b,m,d,w];
NUM BigM;
READ DATA CASUSER.BigM INTO BigM=Volume;
var Is_ISN_MOL {ISN,MOL} binary;
var Is_MOL_T1 {MOL,T1} binary;
/* Decision Variable and Associated Impvars - END */
/*Define Rates and Implicit Variables - START */
set <str,str,str,str,str,str> PerBox_Based_Rate_NoZeroes;
num PerBox_Based_Rate {PerBox_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerBox" and Ratetype='Linehaul' and Rate>0))
into PerBox_Based_Rate_NoZeroes=[ORG DES BOX MOL T1 T2] PerBox_Based_Rate=Rate;
impvar PerBox_Based_Costs = sum {i in ISN, b in BOX,m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerBox_Based_Rate_NoZeroes}
PerBox_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w];
set <str,str,str,str,str,str> PerVol_Based_Rate_NoZeroes;
num PerVol_Based_Rate {PerVol_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerVolUOM" and Ratetype='Carrier_BOL' and Rate>0))
into PerVol_Based_Rate_NoZeroes=[ORG DES MOL BOX T1 T2] PerVol_Based_Rate=Rate;
impvar PerVol_Based_Costs = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerVol_Based_Rate_NoZeroes}
PerVol_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * VolumeInsideBox[i,b,m,d,w];
set <str,str,str,str,str,str> PerShp_Based_Rate_NoZeroes_Entry;
num PerShp_Based_Rate_Entry {PerShp_Based_Rate_NoZeroes_Entry};
read data CASUSER.BOXRATE
(where=(RateBasis="PerShipment" and Ratetype='Export_Declaration' and Rate>0))
into PerShp_Based_Rate_NoZeroes_Entry=[ORG DES MOL BOX T1 T2] PerShp_Based_Rate_Entry=Rate;
impvar PerShp_Based_Costs_Entry = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerShp_Based_Rate_NoZeroes_Entry}
PerShp_Based_Rate_Entry[Org_ISN[i],Des_ISN[i],b,m,d,w];
/*Define Implicit Variables - END */
Min TotalCost = PerBox_Based_Costs + PerVol_Based_Costs+PerShp_Based_Costs_Entry;
/* Constraints - to chose box type and number*/
for {i in ISN, b in BOX,m in MOL,d in T1,w in T2: Is_BoxAvalable_for_a_lane[Org_ISN[i],Des_ISN[i],b,m,d,w] = 0}
fix BoxesNeeded[i,b,m,d,w] = 0;
con SumProportionToOne {i in ISN}:
sum {b in BOX,m in MOL,d in T1,w in T2} Proportion[i,b,m,d,w] = 1;
con Volume_Constraint {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] >= VolumeInsideBox[i,b,m,d,w];
con Volume_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Volume_Min[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] <= VolumeInsideBox[i,b,m,d,w];
con Weight_Constraint {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] >= WeightInsideBox[i,b,m,d,w];
con Weight_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Weight_Min[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] <= WeightInsideBox[i,b,m,d,w];
/*End of Constraints relating to chosing box type and number*/
/* Constraint to enforce other stuff*/
con OneMOL{i in ISN}:
sum{M in MOL} Is_ISN_MOL[i,m] = 1;
con CYSSpecific {m in {'CYS'}, i in ISN,d in T1}:
sum {<(i),b,(m),(d),w> in LegitCombo_ISN} VolumeInsideBox[i,b,'CYS',d,w]= Volume_ISN[i]*Is_ISN_MOL[i,'CYS'];
expand CYSSpecific;
/*con CFSSpecific {m in {'CFS'},d in T1}:
sum {i in ISN, b in BOX,w in T2} VolumeInsideBox_T1[i,b,'CFS',d,w]= sum{d in T1} Volume_T1[d]*Is_MOL_T1[d,'CFS'];
solve with MILP / decomp varsel=ryanfoster presolver=basic;
Thank you for attaching the additional data. I will not have a chance to look at this again until tomorrow afternoon. Although you addressed the question to me, anybody in the community is welcome to respond.
Oh ok Rob.
Thank you. I shall wait.
Rob I think I may be closer to the solution . Here is my latest code below.
If you look at the constraints Con CYSSpecific and Con CFSSpecific, there are two flavor of it , one for weight and one for volume.
However, like I said in my description earlier, what I wanted was for a given Day (which is T1), if a given shipment (which is ISN here) can be sent to DC, then thats fine. If it is not, then it gets consolidated for that week. with other ISNs and goes to CFS .
The model solves but it gives a warning WARNING: The Decomposition algorithm was not run because the automated method was unable to find block-angular form. Also, in the create data step, I am not able to see any observations for "OptMix_RESULTS_Shipment" and "OptMix_RESULTS_Volume". I can see "OptMix_RESULTS_Box" though.
Thanks in advance.
proc optmodel;
set <str> ORG;
read data CASUSER.Unique_ORG into ORG = [ORG];
set <str> DES;
read data CASUSER.Unique_DES into DES = [DES];
set <str> BOX;
read data CASUSER.Unique_BOX into BOX = [BOX];
set <str> MOL;
read data CASUSER.Unique_MOL into MOL = [MOL];
set <str> T1;
read data CASUSER.Unique_T1 into T1 = [T1];
set <str> T2;
read data CASUSER.Unique_T2 into T2 = [T2];
/*Read ISN Wt and Volume for a given ISN* - START */
set <str> ISN;
str Org_ISN {ISN};
str Des_ISN {ISN};
str T1_ISN {ISN};
str T2_ISN {ISN};
num Volume_ISN {ISN} init 0;
num Weight_ISN {ISN} init 0;
read data CASUSER.InputData_AssetMix into ISN=[ISN] Org_ISN=ORG Des_ISN=DES Volume_ISN=Volume Weight_ISN=Weight T1_ISN=T1 T2_ISN=T2;
/*Read ISN Wt and Volume for a given ISN* - END*/
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - START */
num Volume_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Volume_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
num Weight_Capacity {ORG,DES,BOX,MOL,T1,T2} init 0; num Weight_Min {ORG,DES,BOX,MOL,T1,T2} init 0;
read data CASUSER.BOXSPECS into [ORG DES BOX MOL T1 T2]
Volume_Capacity=Volume_Capacity
Weight_Capacity=Weight_Capacity
Volume_Min=Volume_Min
Weight_Min=Weight_Min;
/*Read Wt and Volume Capacity for a given ORG, DES, BOX - END */
/* A Binary variable that is 0 or 1 for a given asset for a given lane, 0 indicated not available, 1- available */
num Is_BoxAvalable_for_a_lane {ORG,DES,BOX,MOL,T1,T2};
read data CASUSER.BOXSpecs into [ORG Des Box Mol T1 T2] Is_BoxAvalable_for_a_lane = BoxAvailability;
/* Decision Variable and Associated Impvars - START */
var BoxesNeeded {ISN,BOX,MOL,T1,T2} >= 0 integer;
var Proportion {ISN,BOX,MOL,T1,T2} >= 0 <= 1;
impvar VolumeInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Volume_ISN[i] * Proportion[i,b,m,d,w];
impvar WeightInsideBox {i in ISN, b in BOX,m in MOL,d in T1,w in T2} = Weight_ISN[i] * Proportion[i,b,m,d,w];
NUM BigM;
READ DATA CASUSER.BigM INTO BigM=Volume;
var Is_ISN_MOL {ISN,MOL} binary;
/* Decision Variable and Associated Impvars - END */
/*Define Rates and Implicit Variables - START */
set <str,str,str,str,str,str> PerBox_Based_Rate_NoZeroes;
num PerBox_Based_Rate {PerBox_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerBox" and Ratetype='Linehaul' and Rate>0))
into PerBox_Based_Rate_NoZeroes=[ORG DES BOX MOL T1 T2] PerBox_Based_Rate=Rate;
impvar PerBox_Based_Costs = sum {i in ISN, b in BOX,m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerBox_Based_Rate_NoZeroes}
PerBox_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w];
set <str,str,str,str,str,str> PerVol_Based_Rate_NoZeroes;
num PerVol_Based_Rate {PerVol_Based_Rate_NoZeroes};
read data CASUSER.BOXRATE
(where=(RateBasis="PerVolUOM" and Ratetype='Carrier_BOL' and Rate>0))
into PerVol_Based_Rate_NoZeroes=[ORG DES MOL BOX T1 T2] PerVol_Based_Rate=Rate;
impvar PerVol_Based_Costs = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerVol_Based_Rate_NoZeroes}
PerVol_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w] * VolumeInsideBox[i,b,m,d,w];
set <str,str,str,str,str,str> PerShp_Based_Rate_NoZeroes_Entry;
num PerShp_Based_Rate_Entry {PerShp_Based_Rate_NoZeroes_Entry};
read data CASUSER.BOXRATE
(where=(RateBasis="PerShipment" and Ratetype='Export_Declaration' and Rate>0))
into PerShp_Based_Rate_NoZeroes_Entry=[ORG DES MOL BOX T1 T2] PerShp_Based_Rate_Entry=Rate;
impvar PerShp_Based_Costs_Entry = sum {i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerShp_Based_Rate_NoZeroes_Entry}
PerShp_Based_Rate_Entry[Org_ISN[i],Des_ISN[i],b,m,d,w];
/*Define Implicit Variables - END */
print PerShp_Based_Rate_Entry;
Min TotalCost = PerBox_Based_Costs + PerVol_Based_Costs+PerShp_Based_Costs_Entry;
/* Constraints - to chose box type and number*/
for {i in ISN, b in BOX,m in MOL,d in T1,w in T2: Is_BoxAvalable_for_a_lane[Org_ISN[i],Des_ISN[i],b,m,d,w] = 0}
fix BoxesNeeded[i,b,m,d,w] = 0;
con SumProportionToOne {i in ISN}:
sum {b in BOX,m in MOL,d in T1,w in T2} Proportion[i,b,m,d,w] = 1;
con Volume_Constraint {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] >= VolumeInsideBox[i,b,m,d,w];
con Volume_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Volume_Min[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] <= VolumeInsideBox[i,b,m,d,w];
con Weight_Constraint {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] >= WeightInsideBox[i,b,m,d,w];
con Weight_Constraint_MinThreshold {i in ISN, b in BOX, m in MOL,d in T1,w in T2}:
Weight_Min[Org_ISN[i],Des_ISN[i],b,m,d,w] * BoxesNeeded[i,b,m,d,w] <= WeightInsideBox[i,b,m,d,w];
/*End of Constraints relating to chosing box type and number*/
/* Constraint to enforce other stuff*/
con OneMOL{i in ISN}:
sum{M in MOL} Is_ISN_MOL[i,m] = 1;
/* CYS Specific Constraints - */
con CYSSpecific_Volume {i in ISN,m in {'CYS'}}:
sum{b in BOX} VolumeInsideBox[i,b,m,T1_ISN[i],T2_ISN[i]]=Volume_ISN[i]*Is_ISN_MOL[i,'CYS'];
con CYSSpecific_Weight {i in ISN,m in {'CYS'}}:
sum{b in BOX} WeightInsideBox[i,b,m,T1_ISN[i],T2_ISN[i]]=Weight_ISN[i]*Is_ISN_MOL[i,'CYS'];
/* CFS Specific Constraints*/
con CFSSpecific_Volume {w in T2, m in {'CFS'}}:
sum{i in ISN, b in BOX} VolumeInsideBox[i,b,m,T1_ISN[i],T2_ISN[i]]=sum{i in ISN}Volume_ISN[i]*Is_ISN_MOL[i,'CFS'];
con CFSSpecific_Weight {w in T2, m in {'CFS'}}:
sum{i in ISN, b in BOX} WeightInsideBox[i,b,m,T1_ISN[i],T2_ISN[i]]=sum{i in ISN}Weight_ISN[i]*Is_ISN_MOL[i,'CFS'];
solve with milp / decomp=(method=concomp);
create data OptMix_RESULTS_Volume (where=(Proportion > 0)) from
[ISN=i BOX=b MOL=m T1=d T2=w]={i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerVol_Based_Rate_NoZeroes}
Org=ORG_ISN[i]
Des=DES_ISN[i]
CustomerName='a'
ScenarioName='b'
ShipmentVolume=Volume_ISN[i]
ShipmentWeight=Weight_ISN[i]
BoxesNeeded
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w]
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w]
VolumeThresholdMinimum=Volume_Min[Org_ISN[i],Des_ISN[i],b,m,d,w]
WeightThresholdMinimum=Weight_Min[Org_ISN[i],Des_ISN[i],b,m,d,w]
Costs=(PerVol_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w]* VolumeInsideBox[i,b,m,d,w])
DimsType='Vol'
Proportion=Proportion[i,b,m,d,w]
VolumeInsideBox
WeightInsideBox
;
create data OptMix_RESULTS_Box(where=(Proportion > 0)) from
[ISN=i BOX=b MOL=m T1=d T2=w]={i in ISN, b in BOX,m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerBox_Based_Rate_NoZeroes}
Org=ORG_ISN[i]
Des=DES_ISN[i]
CustomerName='a'
ScenarioName='b'
ShipmentVolume=Volume_ISN[i]
ShipmentWeight=Weight_ISN[i]
BoxesNeeded
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w]
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w]
VolumeThresholdMinimum=Volume_Min[Org_ISN[i],Des_ISN[i],b,m,d,w]
WeightThresholdMinimum=Weight_Min[Org_ISN[i],Des_ISN[i],b,m,d,w]
Costs=(PerBox_Based_Rate[Org_ISN[i],Des_ISN[i],b,m,d,w]* BoxesNeeded[i,b,m,d,w])
DimsType='Box'
Proportion=Proportion[i,b,m,d,w]
VolumeInsideBox
WeightInsideBox
;
create data OptMix_RESULTS_Shipment(where=(Proportion > 0)) from
[ISN=i BOX=b MOL=m T1=d T2=w]={i in ISN, b in BOX, m in MOL,d in T1,w in T2: <Org_ISN[i],Des_ISN[i],b,m,d,w> in PerShp_Based_Rate_NoZeroes_Entry}
Org=ORG_ISN[i]
Des=DES_ISN[i]
CustomerName='a'
ScenarioName='b'
ShipmentVolume=Volume_ISN[i]
ShipmentWeight=Weight_ISN[i]
BoxesNeeded
Volume_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w]
Weight_Capacity[Org_ISN[i],Des_ISN[i],b,m,d,w]
VolumeThresholdMinimum=Volume_Min[Org_ISN[i],Des_ISN[i],b,m,d,w]
WeightThresholdMinimum=Weight_Min[Org_ISN[i],Des_ISN[i],b,m,d,w]
Costs=(PerShp_Based_Rate_Entry[Org_ISN[i],Des_ISN[i],b,m,d,w])
DimsType='SHP'
Proportion=Proportion[i,b,m,d,w]
VolumeInsideBox
WeightInsideBox
;
This Warning often appears for option "decomp=concomp" as you use it. You should have special structure of constraints and data for this option.
https://support.sas.com/documentation/onlinedoc/or/143/decomp.pdf#page=6&zoom=100,96,457
I would recommend you to use "auto". That's more valuable in regular problems.
If talk about your goals to create logic for CYSSpecific and CFSSpecific, you can add some binary variables to creating if-else expressions.
Thas most interesting job in MILP.
It can looks like that:
Binary x, If a>b then x=0, else x=1. This approach use BigM trick.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.