ResourceUnavailable constraint specification
Closed this issue · 3 comments
I seem to have a problem specifying when a specific resource is unavailable. I am looking to crate a model in which specified resources are not working weekends for example.
When I specify that Albert is unavailable with:
ps.ResourceUnavailable(Albert, [(1, 4), (6, 9)]) # Try a specific named worker
I still have Albert scheduled to work on tasked in those time periods.
Any suggestions?
#!/usr/bin/env python
# coding: utf-8
import processscheduler as ps
from datetime import timedelta, datetime
# Create the scheduling problem
# The total horizon is not known
agile_problem = ps.SchedulingProblem('agile-priorities')
# ,delta_time=timedelta(days=1))
# nb_fullstack = 1
# nb_backend = 1
# nb_frontend = 1
# nb_testers = 2
#
# Team with skills
# fullstack = [ps.Worker('Full%i' % (i + 1)) for i in range(nb_fullstack)]
# backend = [ps.Worker('Back%i' % (i + 1)) for i in range(nb_backend)]
# frontend = [ps.Worker('Front%i' % (i + 1)) for i in range(nb_frontend)]
# tester = [ps.Worker('Tester%i' % (i + 1)) for i in range(nb_testers)]
# Alternative try with named workers
Albert = ps.Worker('Albert') # Fullstack
Bob = ps.Worker('Bob') # Backend
Fred = ps.Worker('Fred') # Frontend
Ted = ps.Worker('Ted') # Tester
Tony = ps.Worker('Tony') # Tester
ps.ResourceUnavailable(Albert, [(1, 4), (6, 9)]) # Try a specific named worker
# Create tasks and assign resources
# One period is mapped to one day.
kick_off_epic_1 = ps.FixedDurationTask('KickOffEpic_1', duration=1)
kick_off_epic_1.add_required_resource(ps.SelectWorkers([Albert, Bob, Fred, Ted]))
work_item_1 = ps.FixedDurationTask('WorkItem_1', duration=3)
work_item_1.add_required_resource(Albert)
work_item_2 = ps.FixedDurationTask('WorkItem_2', duration=2)
work_item_2.add_required_resource(Bob)
work_item_3 = ps.FixedDurationTask('WorkItem_3', duration=2)
# the testing activity can be processed by the Tester1 OR Tester2
work_item_3.add_required_resource(ps.SelectWorkers([Ted, Tony],
nb_workers_to_select=1,
kind='exact'))
work_item_4 = ps.FixedDurationTask('WorkItem_4', duration=2)
work_item_4.add_required_resource(Albert)
release_epic_1 = ps.FixedDurationTask('ReleaseEpic_1', duration=1)
release_epic_1.add_required_resource(Tony)
ps.TaskStartAt(kick_off_epic_1, 0)
ps.TaskEndAt(release_epic_1, agile_problem.horizon)
# Task precedences
ps.TaskPrecedence(kick_off_epic_1, work_item_1)
ps.TaskPrecedence(work_item_1, work_item_2)
ps.TaskPrecedence(work_item_2, work_item_3)
ps.TaskPrecedence(work_item_3,work_item_4)
ps.TaskPrecedence(work_item_4,release_epic_1)
# First solution, plot the schedule
solver = ps.SchedulingSolver(agile_problem)
solution_1 = solver.solve()
solution_1.render_gantt_matplotlib(fig_size=(10, 5), render_mode='Resource')
Hey @rnwolf!
I ran into the same issue and found out what was going on when I exchanged the line
ps.ResourceUnavailable(Albert, [(1, 4), (6, 9)]) # Try a specific named worker
with
ps.WorkLoad(Albert, {(1, 4) : 0, (6, 9): 0}) # Try a specific named worker
since the docs mention that ps.ResourceUnavailable
is a special case of ps.WorkLoad
.
Now I got the following error:
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Cell In[128], line 29
26 Ted = ps.Worker('Ted') # Tester
27 Tony = ps.Worker('Tony') # Tester
---> 29 ps.WorkLoad(Albert, {(1, 4) : 0, (6, 9): 0}) # Try a specific named worker
31 # Create tasks and assign resources
32 # One period is mapped to one day.
34 kick_off_epic_1 = ps.FixedDurationTask('KickOffEpic_1', duration=1)
File ~/.mambaforge/envs/pm/lib/python3.11/site-packages/processscheduler/resource_constraint.py:124, in WorkLoad.__init__(self, resource, dict_time_intervals_and_bound, kind, optional)
121 durations.append(dur)
123 if not durations:
--> 124 raise AssertionError(
125 "The resource is not assigned to any task. WorkLoad constraint meaningless."
126 )
128 # workload constraint depends on the kind
129 if kind == "exact":
AssertionError: The resource is not assigned to any task. WorkLoad constraint meaningless.
So I moved the line which adds the resource constraint after the assignment of the resource (Albert
) to tasks:
...
# Alternative try with named workers
Albert = ps.Worker('Albert') # Fullstack
Bob = ps.Worker('Bob') # Backend
Fred = ps.Worker('Fred') # Frontend
Ted = ps.Worker('Ted') # Tester
Tony = ps.Worker('Tony') # Tester
-# moved resource constraint from here...
-ps.ResourceUnavailable(Albert, [(1, 4), (6, 9)]) # Try a specific named worker
# Create tasks and assign resources
# One period is mapped to one day.
kick_off_epic_1 = ps.FixedDurationTask('KickOffEpic_1', duration=1)
kick_off_epic_1.add_required_resource(ps.SelectWorkers([Albert, Bob, Fred, Ted]))
work_item_1 = ps.FixedDurationTask('WorkItem_1', duration=3)
work_item_1.add_required_resource(Albert)
work_item_2 = ps.FixedDurationTask('WorkItem_2', duration=2)
work_item_2.add_required_resource(Bob)
work_item_3 = ps.FixedDurationTask('WorkItem_3', duration=2)
# the testing activity can be processed by the Tester1 OR Tester2
work_item_3.add_required_resource(ps.SelectWorkers([Ted, Tony],
nb_workers_to_select=1,
kind='exact'))
work_item_4 = ps.FixedDurationTask('WorkItem_4', duration=2)
work_item_4.add_required_resource(Albert)
release_epic_1 = ps.FixedDurationTask('ReleaseEpic_1', duration=1)
release_epic_1.add_required_resource(Tony)
+# ... to here.
+
+# Add resource constraints after resources have been assigned to tasks
+ps.ResourceUnavailable(Albert, [(1, 4), (6, 9)]) # Try a specific named worker
+
ps.TaskStartAt(kick_off_epic_1, 0)
ps.TaskEndAt(release_epic_1, agile_problem.horizon)
...
Fully working example
import processscheduler as ps
from datetime import timedelta, datetime
# Create the scheduling problem
# The total horizon is not known
agile_problem = ps.SchedulingProblem('agile-priorities')
# ,delta_time=timedelta(days=1))
# nb_fullstack = 1
# nb_backend = 1
# nb_frontend = 1
# nb_testers = 2
#
# Team with skills
# fullstack = [ps.Worker('Full%i' % (i + 1)) for i in range(nb_fullstack)]
# backend = [ps.Worker('Back%i' % (i + 1)) for i in range(nb_backend)]
# frontend = [ps.Worker('Front%i' % (i + 1)) for i in range(nb_frontend)]
# tester = [ps.Worker('Tester%i' % (i + 1)) for i in range(nb_testers)]
# Alternative try with named workers
Albert = ps.Worker('Albert') # Fullstack
Bob = ps.Worker('Bob') # Backend
Fred = ps.Worker('Fred') # Frontend
Ted = ps.Worker('Ted') # Tester
Tony = ps.Worker('Tony') # Tester
# Create tasks and assign resources
# One period is mapped to one day.
kick_off_epic_1 = ps.FixedDurationTask('KickOffEpic_1', duration=1)
kick_off_epic_1.add_required_resource(ps.SelectWorkers([Albert, Bob, Fred, Ted]))
work_item_1 = ps.FixedDurationTask('WorkItem_1', duration=3)
work_item_1.add_required_resource(Albert)
work_item_2 = ps.FixedDurationTask('WorkItem_2', duration=2)
work_item_2.add_required_resource(Bob)
work_item_3 = ps.FixedDurationTask('WorkItem_3', duration=2)
# the testing activity can be processed by the Tester1 OR Tester2
work_item_3.add_required_resource(ps.SelectWorkers([Ted, Tony],
nb_workers_to_select=1,
kind='exact'))
work_item_4 = ps.FixedDurationTask('WorkItem_4', duration=2)
work_item_4.add_required_resource(Albert)
release_epic_1 = ps.FixedDurationTask('ReleaseEpic_1', duration=1)
release_epic_1.add_required_resource(Tony)
# Add resource constraints after resources have been assigned to tasks
ps.ResourceUnavailable(Albert, [(1, 4), (6, 9)]) # Try a specific named worker
ps.TaskStartAt(kick_off_epic_1, 0)
ps.TaskEndAt(release_epic_1, agile_problem.horizon)
# Task precedences
ps.TaskPrecedence(kick_off_epic_1, work_item_1)
ps.TaskPrecedence(work_item_1, work_item_2)
ps.TaskPrecedence(work_item_2, work_item_3)
ps.TaskPrecedence(work_item_3,work_item_4)
ps.TaskPrecedence(work_item_4,release_epic_1)
# First solution, plot the schedule
solver = ps.SchedulingSolver(agile_problem)
solution_1 = solver.solve()
solution_1.render_gantt_matplotlib(fig_size=(10, 5), render_mode='Resource')
So, the issue here was that there was no indication to the user that the order of task assignment and adding resource constraints matter in the case of ps.ResourceUnavailable
.
@tpaviot Thanks for this amazing package!
I had already a very quick look at the class definition, but was not a able to directly see how to add an assertion there.
Could you please have a look?
Related issue: #110
Brilliant I will experiment some more.
Thank you guys for your feedback.