# Exemple resolu : RCPSP avec ressources non-renouvelables
@dataclass
class TaskNR:
"""Tache RCPSP avec ressources renouvelables et non-renouvelables."""
id: int
duration: int
predecessors: List[int]
renewable_needs: Dict[int, int]
non_renewable_needs: Dict[int, int]
def solve_rcpsp_with_nonrenewable(
tasks: List[TaskNR],
renewable_capacities: Dict[int, int],
non_renewable_budgets: Dict[int, int],
horizon: int,
) -> Dict:
"""
Resout RCPSP avec:
- ressources renouvelables (contrainte cumulative)
- ressources non-renouvelables (budget global sur tout le projet)
"""
model = cp_model.CpModel()
starts = {}
ends = {}
intervals = {}
for task in tasks:
starts[task.id] = model.NewIntVar(0, horizon, f"start_{task.id}")
ends[task.id] = model.NewIntVar(0, horizon, f"end_{task.id}")
intervals[task.id] = model.NewIntervalVar(
starts[task.id], task.duration, ends[task.id], f"interval_{task.id}"
)
# Precedences
for task in tasks:
for pred_id in task.predecessors:
model.Add(starts[task.id] >= ends[pred_id])
# Ressources renouvelables
for res_id, capacity in renewable_capacities.items():
task_intervals = []
demands = []
for task in tasks:
demand = task.renewable_needs.get(res_id, 0)
if demand > 0:
task_intervals.append(intervals[task.id])
demands.append(demand)
if demands:
model.AddCumulative(task_intervals, demands, capacity)
# Ressources non-renouvelables (budget total du projet)
for res_id, budget in non_renewable_budgets.items():
total_consumption = sum(task.non_renewable_needs.get(res_id, 0) for task in tasks)
model.Add(total_consumption <= budget)
makespan = model.NewIntVar(0, horizon, "makespan_rcpsp_nr")
model.AddMaxEquality(makespan, [ends[task.id] for task in tasks])
model.Minimize(makespan)
solver = cp_model.CpSolver()
solver.parameters.max_time_in_seconds = 30
status = solver.Solve(model)
if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):
schedule = []
for task in tasks:
schedule.append({
"task": task.id,
"start": solver.Value(starts[task.id]),
"end": solver.Value(ends[task.id]),
"duration": task.duration,
})
non_renewable_usage = {
r: sum(task.non_renewable_needs.get(r, 0) for task in tasks)
for r in non_renewable_budgets
}
return {
"status": "OPTIMAL" if status == cp_model.OPTIMAL else "FEASIBLE",
"makespan": solver.Value(makespan),
"schedule": schedule,
"non_renewable_usage": non_renewable_usage,
}
return {"status": "INFEASIBLE", "makespan": None, "schedule": []}
tasks_nr = [
TaskNR(0, 2, [], {0: 2, 1: 1}, {0: 3}),
TaskNR(1, 3, [0], {0: 1, 1: 2}, {0: 2}),
TaskNR(2, 4, [0], {0: 3}, {0: 4}),
TaskNR(3, 2, [1], {1: 2}, {0: 1}),
TaskNR(4, 3, [2], {0: 1, 1: 1}, {0: 2}),
TaskNR(5, 1, [3, 4], {0: 2}, {0: 1}),
]
renewable_capacities = {0: 4, 1: 3}
non_renewable_budgets = {0: 14}
rcpsp_nr_result = solve_rcpsp_with_nonrenewable(
tasks_nr, renewable_capacities, non_renewable_budgets, horizon=25
)
print("\nExercice 2 - RCPSP non-renouvelables")
print(f"Status: {rcpsp_nr_result['status']}")
print(f"Makespan: {rcpsp_nr_result['makespan']}")
print(f"Usage non-renouvelable: {rcpsp_nr_result.get('non_renewable_usage', {})}")