# Resource Constrained Scheduling Problem (RCSP)
# Schedules activities to maximize net present value of cash flows
# Subject to resource capacity constraints and activity durations
# Demonstrates Excel data loading and parameterized set functions
model rcsp
set I = load_activities() # Activities to schedule
set T = load_time_periods() # Time periods
set R = load_resources() # Resources
set delta(I, T) = load_delta() # Time periods when activity i is active if started at t
const d = load_durations(), forall i in I # Duration of activity i
const u = load_resource_limits(), forall r in R # Upper limit of resource r
const v = load_resource_values(), forall i in I, forall r in R # Resource r consumed by activity i
const dc = load_discounted_cf(), forall i in I, forall t in T # Discounted cash flow if activity i starts at t
bin x, forall i in I, forall t in T # 1 if activity i starts at time t
# Objective: Maximize net present value of cash flows
max sum_{t in T}{sum_{i in I}{dc_{i,t} * x_{i,t}}}
# Each activity must start exactly once
constr sum_{t in T}{x_{i,t}} = 1, forall i in I
# Resource capacity constraint at each time period
constr sum_{i in I}{sum_{k in delta(i, t)}{v_{i,r} * x_{i,k}}} <= u_{r}, forall t in T, forall r in R
end
import elytica
import pandas as pd
import json
# Excel file with three sheets: activities, resources, consumption
xls = "scheduling.xlsx"
# Load data from Excel
activity_sheet = json.loads((pd.read_excel(xls, sheet_name="activities")).to_json(orient='records'))
resource_sheet = json.loads((pd.read_excel(xls, sheet_name="resources")).to_json(orient='records'))
consumption_sheet = json.loads((pd.read_excel(xls, sheet_name="consumption")).to_json(orient='records'))
# Process activity data
activities = [i["activity_id"] for i in activity_sheet]
durations = {i["activity_id"]: i["duration"] for i in activity_sheet}
cash_flows = {i["activity_id"]: i["cash_flow"] for i in activity_sheet}
# Generate time periods based on total duration
time_periods = list(range(1, sum(durations.values())))
# Process resource data
resources = [r["resource_id"] for r in resource_sheet]
resource_limits = {r["resource_id"]: r["upper_limit"] for r in resource_sheet}
# Process consumption data
resource_values = {
i["activity_id"]: {
r["resource_id"]: r["value"]
for r in consumption_sheet
if r["activity_id"] == i["activity_id"]
}
for i in consumption_sheet
}
def load_activities():
"""Return list of activity IDs"""
return activities
def load_time_periods():
"""Return list of time periods"""
return time_periods
def load_resources():
"""Return list of resource IDs"""
return resources
def load_durations():
"""Return duration for each activity"""
return durations
def load_resource_limits():
"""Return capacity limit for each resource"""
return resource_limits
def load_resource_values():
"""Return resource consumption for each activity and resource"""
return resource_values
def load_discounted_cf():
"""Calculate discounted cash flow for each activity at each start time (10% discount rate)"""
return {
i: {t: cash_flows[i] / (1.1)**t for t in time_periods}
for i in activities
}
def load_delta():
"""
Return time periods when activity i is active if started at time t
delta(i,t) = [t, t+1, ..., t+d[i]-1]
"""
return {
i: {
t: [k for k in time_periods if k >= t and k <= t + durations[i] - 1]
for t in time_periods
}
for i in activities
}
def main():
elytica.init_model("rcsp")
elytica.run_model("rcsp")
I = elytica.get_model_set("rcsp", "I")
T = elytica.get_model_set("rcsp", "T")
print("\n" + "="*70)
print("RESOURCE CONSTRAINED SCHEDULING SOLUTION")
print("="*70)
# Collect schedule
schedule = []
print("\nActivity Schedule:")
print("-"*70)
for i in I:
for t in T:
vname = f"x{i},{t}"
val = elytica.get_variable_value("rcsp", vname)
if val > 0.5:
duration = durations[i]
end_time = t + duration - 1
schedule.append({
"activity": i,
"start_time": t,
"end_time": end_time,
"duration": duration
})
print(f" Activity {i:3d}: Start={t:3d}, End={end_time:3d}, Duration={duration:2d}")
# Sort schedule by start time
schedule.sort(key=lambda x: x["start_time"])
# Get objective value (NPV)
obj_value = elytica.get_best_primal_bound("rcsp")
print("-"*70)
print(f"Net Present Value (NPV): {obj_value:.2f}")
print("="*70)
# Prepare results for output
results = {
"objective_value": obj_value,
"schedule": schedule
}
elytica.write_results(json.dumps(results, indent=2))
return 0
# Expected Excel file format (scheduling.xlsx):
#
# Sheet "activities":
# | activity_id | duration | cash_flow |
# |-------------|----------|-----------|
# | 1 | 3 | 1000 |
# | 2 | 2 | 500 |
# | ... | ... | ... |
#
# Sheet "resources":
# | resource_id | upper_limit |
# |-------------|-------------|
# | 1 | 10 |
# | 2 | 8 |
# | ... | ... |
#
# Sheet "consumption":
# | activity_id | resource_id | value |
# |-------------|-------------|-------|
# | 1 | 1 | 5 |
# | 1 | 2 | 3 |
# | 2 | 1 | 2 |
# | ... | ... | ... |
Read this through MCP: read with path: "examples/rcsp", or resource hlpl://examples/rcsp.