data ACTIVITYDATA;
input _ACTIVITY_ $ _SUCCESSOR_ $ _DURATION_ _TRG_AREA_RESOURCE_ $ _TRG_AREA_QTY_ _CTLEVEL_ $ _PRI_ ;
if _ACTIVITY_ = 'CALGVACA' then do;
_ALIGNTYPE_ = 'seq';
_ALIGNDATE_ = 2;
end;
datalines;
AA1 AA3 12 CANADA 1 CT1 23
AA2 AA3 20 UK 1 CT2 12
AA3 AB2 30 CANADA 1 CT1 10
AA4 AB3 30 CANADA 1 CT3 25
AB2 AB3 20 CANADA 1 CT1 8
AB3 . 10 UK 1 CT2 12
AB4 AB3 51 LONDON 1 CT4 21
CALGVACA . 1 CALGARY 1 . .
;
/* Convert this to Hold only Training Areas. This will allow the code to select only Trg Area */
data TRG_AREA_DATA;
input _TRG_AREA_ $ _TRG_AREA_CAPACITY_ _TRG_AREA_POOL_ $ _TRG_AREA_SUBQTY_;
datalines;
LONDON 1 UK 1
EDINBURGH 1 UK 1
VANCOVER 1 CANADA 1
CALGARY 1 CANADA 1
LONDON 1 CANADA 1
;
data DAILY_RES_REQUIREMENTS;
input _ACTIVITY_ $ _DAILY_RESOURCE_ $ QTY;
datalines;
AA1 SG 1
AA2 IG 1
AA2 WG 1
AA2 SG 1
AA3 GG 1
AA3 SG 1
AA4 SG 1
AB2 IG 1
AB2 SG 1
AB3 IG 1
AB4 GG 1
AA1 WR 23
AA1 CR 12
AA2 CR 10
AA3 BD 34
AA4 WR 21
AB2 BD 32
AB4 CR 10
AA4 CR 6
;
data DAILY_RES_CAPACITY;
input _DAILY_RESOURCE_ $ DAILY_CAP;
datalines;
SG 1
IG 1
WG 1
GG 1
WR 25
CR 12
BD 35
;
proc optmodel;
set <str,str> ACTIVITY_SUCCESSOR;
read data ACTIVITYDATA into ACTIVITY_SUCCESSOR=[_ACTIVITY_ _SUCCESSOR_];
set ACTIVITIES = (union {<i,j> in ACTIVITY_SUCCESSOR} {i,j}) diff {''};
num duration {ACTIVITIES} init 0;
str trg_area_resource {ACTIVITIES};
num trg_area_quantity {ACTIVITIES} init 1;
str aligntype {ACTIVITIES};
num aligndate {ACTIVITIES};
set <str> Daily_Resource;
set <str,str> ACTIVITY_DAILY_RESOURCES;
num Daily_Res_Req {ACTIVITIES, Daily_Resource} ;
num Daily_Res_Cap {Daily_Resource};
str ct_level {ACTIVITIES};
num pri {ACTIVITIES};
/* READ ACTIVITY AND LOCTATION REQUIREMENTS DATA */
read data ACTIVITYDATA into [_ACTIVITY_]
duration=_DURATION_ trg_area_resource=_TRG_AREA_RESOURCE_ trg_area_quantity=_TRG_AREA_QTY_ aligntype=_ALIGNTYPE_ aligndate=_ALIGNDATE_ ct_level = _CTLEVEL_ pri = _PRI_;
num maxperiod = sum {a in ACTIVITIES} duration[a] - 1;
set PERIODS = 0..maxperiod;
set ACTIVITIES_PERIODS = {a in ACTIVITIES, p in PERIODS: p+duration[a]-1 in PERIODS};
var IsStart {ACTIVITIES_PERIODS} binary;
var StartDate{ACTIVITIES} integer >= 0 <= maxperiod;
impvar FinishDate{a in ACTIVITIES} = StartDate[a] + duration[a];
con StartOnce {a in ACTIVITIES}:
sum {<(a),p> in ACTIVITIES_PERIODS} IsStart[a,p] = 1;
impvar Start {a in ACTIVITIES} = sum {<(a),p> in ACTIVITIES_PERIODS} p * IsStart[a,p];
impvar Finish {a in ACTIVITIES} = Start[a] + duration[a];
con Precedence {<a,s> in ACTIVITY_SUCCESSOR: s ne ''}:
Finish[a] <= Start[s];
con Alignment {a in ACTIVITIES: aligntype[a] = 'seq'}:
Start[a] = aligndate[a];
var MaxFinish integer >= max {a in ACTIVITIES} duration[a];
/*min Makespan = max{a in ACTIVITIES} Finish[a];*/
min Makespan = MaxFinish;
con MaxFinishCon {a in ACTIVITIES}:
MaxFinish >= Finish[a];
/* LOCATION INFORMATION */
set <str,str> TRG_AREA_POOL;
num trg_area_subquantity {TRG_AREA_POOL} init 1;
/* READ LOCATION DATA AND CAPACITIES */
read data TRG_AREA_DATA into TRG_AREA_POOL=[_TRG_AREA_ _TRG_AREA_POOL_] trg_area_subquantity=_TRG_AREA_SUBQTY_;
set <str> TRG_AREA_RESOURCES;
num capacity {TRG_AREA_RESOURCES};
read data TRG_AREA_DATA into TRG_AREA_RESOURCES=[_TRG_AREA_] capacity=_TRG_AREA_CAPACITY_;
TRG_AREA_POOL = TRG_AREA_POOL union setof {r in TRG_AREA_RESOURCES} <r,r>;
print trg_area_subquantity;
set TA_RESOURCES_ACTIVITIES = {r in TRG_AREA_RESOURCES, a in ACTIVITIES: <r,trg_area_resource[a]> in TRG_AREA_POOL};
/* SELECTS THE RESOURCE(S) TO BE ALLOCATED TO THE ACTIVITY */
var IsRes {TA_RESOURCES_ACTIVITIES} binary;
/*var IsDailyRes {ACTIVITY_DAILY_RESOURCES} binary;*/
/* CONSTRAINS ONLY LOCATION TO AN ACTIVITY */
con OneResourcePerActivity {a in ACTIVITIES}:
sum {<r,trg_area_resource[a]> in TRG_AREA_POOL} IsRes[r,a] = 1;
var IsResPeriod {TA_RESOURCES_ACTIVITIES, PERIODS} binary;
con IsResPeriodImpliesIsRes {<r,a> in TA_RESOURCES_ACTIVITIES, p in PERIODS}:
IsResPeriod[r,a,p] <= IsRes[r,a];
con IsStartImpliesResource {<a,p> in ACTIVITIES_PERIODS, p2 in p..p+duration[a]-1}:
trg_area_quantity[a] * IsStart[a,p] <= sum {<r,trg_area_resource[a]> in TRG_AREA_POOL} trg_area_subquantity[r,trg_area_resource[a]] * IsResPeriod[r,a,p2];
con ResourceCapacity {r in TRG_AREA_RESOURCES, p in PERIODS}:
sum {<(r),a> in TA_RESOURCES_ACTIVITIES} IsResPeriod[r,a,p] <= capacity[r];
/* READ DAILY RESOURCE REQUIREMENTS */
read data DAILY_RES_REQUIREMENTS into Daily_Resource = [_DAILY_RESOURCE_];
put Daily_Resource;
read data DAILY_RES_REQUIREMENTS into ACTIVITY_DAILY_RESOURCES=[_ACTIVITY_ _DAILY_RESOURCE_];
put ACTIVITY_DAILY_RESOURCES;
num Is_Act_Daily_Res{ACTIVITY_DAILY_RESOURCES} init 1;
print Is_Act_Daily_Res;
read data DAILY_RES_REQUIREMENTS into [_ACTIVITY_ _DAILY_RESOURCE_] Daily_Res_Req=QTY;
/* READS DAILY RESOURCE CAPACITY */
read data DAILY_RES_CAPACITY into [_DAILY_RESOURCE_] Daily_Res_Cap=DAILY_CAP;
print Daily_Res_Cap;
var IsDailyResPeriod{ACTIVITY_DAILY_RESOURCES, PERIODS} binary;
/* Checks that Trg Area is not allocated to a period with requirement with resource */
/*expand IsStartImpliesResource;*/
con IsDailyResPeriodImpliesIsRes {<a,r> in ACTIVITY_DAILY_RESOURCES, p in PERIODS}:
IsDailyResPeriod[a,r,p] <= Is_Act_Daily_Res[a,r];
con IsStartImpliesDailyResource {<a,p> in ACTIVITIES_PERIODS, p2 in p..p+duration[a]-1,<(a), r> in ACTIVITY_DAILY_RESOURCES }:
Daily_Res_Req[a,r]*IsStart[a,p] <= Daily_Res_Req[a,r]*IsDailyResPeriod[a,r,p2];
/*expand IsStartImpliesDailyResource;*/
con DailyResourceCapacity {r in Daily_Resource, p in PERIODS}:
sum {<a,(r)> in ACTIVITY_DAILY_RESOURCES} IsDailyResPeriod[a,r,p]*Daily_Res_Req[a,r] <= Daily_Res_Cap[r];
/* Trying to remove the need for Var IsDailyResPeriod
I am trying to reduce the number of variables to try and improve its peformance
This part is currently included in the final problem as I cannoit get to work.
*/
impvar Busy {<a,r> in ACTIVITY_DAILY_RESOURCES, <(a),p> in ACTIVITIES_PERIODS, p2 in p..p+duration[a]: IsStart[(a),p].sol>0.5} = Daily_Res_Req[a,r];
/* con busy2 {r in Daily_Resource, p in PERIODS}: sum {a in ACTIVITIES, p2 in Periods} Busy[a,r,p2,p] <= Daily_Res_Cap[r];
impvar busy1{<a,r> in ACTIVITY_DAILY_RESOURCES, p in PERIODS : p in StartDate[a]..FinishDate[a]} = 1; */
solve;
quit; Rob, in the code above I have additional resources which are always required if a activity is scheduled. In an effort to improve performance I have tried to reduce the need for IsResPeriod Var but have failed. Below is amended code to uses only one source of activities and resources. I tried to get the program to select a resource from each pool that has a resource requirement. I would be grateful for your suggestions on the best approach and advice to resolve the problems. as I said previously, the end problem will be approx 2400 activities with approx 10 resources. data ACTIVITYDATA;
input _ACTIVITY_ $ _SUCCESSOR_ $ _DURATION_ _TRG_AREA_RESOURCE_ $ _TRG_AREA_QTY_ _CTLEVEL_ $ _PRI_ ;
if _ACTIVITY_ = 'CALGVACA' then do;
_ALIGNTYPE_ = 'seq';
_ALIGNDATE_ = 2;
end;
datalines;
AA1 AA3 12 CANADA 1 CT1 23
AA2 AA3 20 UK 1 CT2 12
AA3 AB2 30 CANADA 1 CT1 10
AA4 AB3 30 CANADA 1 CT3 25
AB2 AB3 20 CANADA 1 CT1 8
AB3 . 10 UK 1 CT2 12
AB4 AB3 51 LONDON 1 CT4 21
CALGVACA . 1 CALGARY 1 . .
AA1 AA3 12 SG 1 CT1 23
AA2 AA3 20 IG 1 CT2 12
AA2 AA3 20 WG 1 CT2 12
AA2 AA3 20 SG 1 CT2 12
AA3 AB2 30 GG 1 CT1 10
AA3 AB2 30 SG 1 CT1 10
AA4 AB3 30 SG 1 CT3 25
AB2 AB3 30 IG 1 CT1 8
AB2 AB3 30 SG 1 CT1 8
AB3 . 10 IG 1 CT2 12
AB4 AB3 51 GG 1 CT4 21
AA1 AA3 12 WR 23 CT1 23
AA1 AA3 12 CR 12 CT1 23
AA2 AA3 20 CR 10 CT2 12
AA3 AB2 30 BD 34 CT3 10
AA4 AB3 30 WR 21 CT3 25
AB2 AB3 20 BD 32 CT1 8
AB4 AB3 51 CR 10 CT4 21
AA4 AB3 30 CR 6 CT3 25
;
/* Convert this to Hold only Training Areas. This will allow the code to select only Trg Area */
data TRG_AREA_DATA;
input _TRG_AREA_ $ _TRG_AREA_CAPACITY_ _TRG_AREA_POOL_ $ _TRG_AREA_SUBQTY_;
datalines;
LONDON 1 UK 1
EDINBURGH 1 UK 1
VANCOVER 1 CANADA 1
CALGARY 1 CANADA 1
LONDON 1 CANADA 1
SG 1 SG 1
IG 1 IG 1
WG 1 WG 1
GG 1 GG 1
WR 25 WR 25
CR 12 CR 12
BD 35 BD 35
;
proc optmodel;
set <str,str> ACTIVITY_SUCCESSOR;
read data ACTIVITYDATA into ACTIVITY_SUCCESSOR=[_ACTIVITY_ _SUCCESSOR_];
set ACTIVITIES = (union {<i,j> in ACTIVITY_SUCCESSOR} {i,j}) diff {''};
num duration {ACTIVITIES} init 0;
str trg_area_resource {ACTIVITIES};
num trg_area_quantity {ACTIVITIES} init 1;
str aligntype {ACTIVITIES};
num aligndate {ACTIVITIES};
set <str> Daily_Resource;
set <str,str> ACTIVITY_DAILY_RESOURCES;
num Daily_Res_Req {ACTIVITIES, Daily_Resource} ;
num Daily_Res_Cap {Daily_Resource};
str ct_level {ACTIVITIES};
num pri {ACTIVITIES};
/* READ ACTIVITY AND LOCTATION REQUIREMENTS DATA */
read data ACTIVITYDATA into [_ACTIVITY_]
duration=_DURATION_ trg_area_resource=_TRG_AREA_RESOURCE_ trg_area_quantity=_TRG_AREA_QTY_ aligntype=_ALIGNTYPE_ aligndate=_ALIGNDATE_ ct_level = _CTLEVEL_ pri = _PRI_;
num maxperiod = sum {a in ACTIVITIES} duration[a] - 1;
set PERIODS = 0..maxperiod;
set ACTIVITIES_PERIODS = {a in ACTIVITIES, p in PERIODS: p+duration[a]-1 in PERIODS};
var IsStart {ACTIVITIES_PERIODS} binary;
var StartDate{ACTIVITIES} integer >= 0 <= maxperiod;
impvar FinishDate{a in ACTIVITIES} = StartDate[a] + duration[a];
con StartOnce {a in ACTIVITIES}:
sum {<(a),p> in ACTIVITIES_PERIODS} IsStart[a,p] = 1;
impvar Start {a in ACTIVITIES} = sum {<(a),p> in ACTIVITIES_PERIODS} p * IsStart[a,p];
impvar Finish {a in ACTIVITIES} = Start[a] + duration[a];
con Precedence {<a,s> in ACTIVITY_SUCCESSOR: s ne ''}:
Finish[a] <= Start[s];
con Alignment {a in ACTIVITIES: aligntype[a] = 'seq'}:
Start[a] = aligndate[a];
var MaxFinish integer >= max {a in ACTIVITIES} duration[a];
/*min Makespan = max{a in ACTIVITIES} Finish[a];*/
min Makespan = MaxFinish;
con MaxFinishCon {a in ACTIVITIES}:
MaxFinish >= Finish[a];
/* LOCATION INFORMATION */
set <str,str> TRG_AREA_POOL;
num trg_area_subquantity {TRG_AREA_POOL} init 1;
/* READ LOCATION DATA AND CAPACITIES */
read data TRG_AREA_DATA into TRG_AREA_POOL=[_TRG_AREA_ _TRG_AREA_POOL_] trg_area_subquantity=_TRG_AREA_SUBQTY_;
set <str> TRG_AREA_RESOURCES;
num capacity {TRG_AREA_RESOURCES};
read data TRG_AREA_DATA into TRG_AREA_RESOURCES=[_TRG_AREA_] capacity=_TRG_AREA_CAPACITY_;
put TRG_AREA_RESOURCES;
TRG_AREA_POOL = TRG_AREA_POOL union setof {r in TRG_AREA_RESOURCES} <r,r>;
put TRG_AREA_POOL;
print trg_area_subquantity;
set TA_RESOURCES_ACTIVITIES = {r in TRG_AREA_RESOURCES, a in ACTIVITIES: <r,trg_area_resource[a]> in TRG_AREA_POOL};
put TA_RESOURCES_ACTIVITIES;
/* SELECTS THE RESOURCE(S) TO BE ALLOCATED TO THE ACTIVITY */
var IsRes {TA_RESOURCES_ACTIVITIES} binary;
/*var IsDailyRes {ACTIVITY_DAILY_RESOURCES} binary;*/
/* CONSTRAINS ONLY LOCATION TO AN ACTIVITY */
con OneResourcePerActivity {a in ACTIVITIES}:
sum {<r,trg_area_resource[a]> in TRG_AREA_POOL} IsRes[r,a] = 1;
var IsResPeriod {TA_RESOURCES_ACTIVITIES, PERIODS} binary;
con IsResPeriodImpliesIsRes {<r,a> in TA_RESOURCES_ACTIVITIES, p in PERIODS}:
IsResPeriod[r,a,p] <= IsRes[r,a];
con IsStartImpliesResource {<a,p> in ACTIVITIES_PERIODS, p2 in p..p+duration[a]-1}:
trg_area_quantity[a] * IsStart[a,p] <= sum {<r,trg_area_resource[a]> in TRG_AREA_POOL} trg_area_subquantity[r,trg_area_resource[a]] * IsResPeriod[r,a,p2];
con ResourceCapacity {r in TRG_AREA_RESOURCES, p in PERIODS}:
sum {<(r),a> in TA_RESOURCES_ACTIVITIES} IsResPeriod[r,a,p] <= capacity[r];
solve;
quit;
... View more