timnon / pyschedule

pyschedule - resource scheduling in python
Apache License 2.0
295 stars 61 forks source link

PERIODS - bug with non-consecutive slots #67

Closed DatalyticUk closed 5 years ago

DatalyticUk commented 6 years ago

I was hoping to use periods for setting "offshift" calendar patterns.

resource1, resource2 = S.Resource('R1'), S.Resource('R2',periods=[1,4,5])

task1 = S.Task('task1',3)
task2 = S.Task('task2',5)

task1 += resource1
task2 += resource2

INFO: execution time for solving mip (sec) = 0.02400493621826172
INFO: objective = 1.0
[(task1, R1, 0, 3), (task2, R2, 1, 6)]

I was expecting either task2 to fill available periods, until time criteria has been met or to start at a point where there is enough available slots in a row to schedule the task.

In this case and others I have tested the task starts at the first available period (1) and still continues on 2 and 3 ignoring that fact they are not available.

Is this the expected outcome?

Thanks

timnon commented 6 years ago

This is the expected outcome, the periods-parameter specifies the periods when tasks are allowed to start. So it is allowed to reach into periods which are not in the periods-parameter. Maybe this is a little misleading, it probably makes more sense to completely block such periods. Let me change this in future versions.

For the time being, i would use blocker-tasks to implement times when no tasks is allowed to be scheduled. Or use a capacity constraint like:

from pyschedule import Scenario, Task, Resource, solvers, plotters, alt

horizon = 50
S = Scenario('Scenario',horizon=horizon)

resource1, resource2 = S.Resource('R1'), S.Resource('R2')#,periods=[1,4,5])

task1 = S.Task('task1',length=3)
task2 = S.Task('task2',length=3)

task1 += resource1
task2 += resource2

# bound the sum of the lengths of all tasks in interval 2..4 to 0, hence no task is allowed
S += resource2['length'][2:5] <= 0

if solvers.mip.solve(S, msg=1):
    plotters.matplotlib.plot(S, fig_size=(10, 5))
else:
    print('no solution exists')

tmp

!! Please update to the newest version 0.2.30

DatalyticUk commented 6 years ago

Thanks for the reply. My main problem is that in a manufacturing environment it is common for a task to be started before a shift ends and continued after the next shift starts. Bounding the sum of the lengths still has the same underlying issue, in this case it just pushes the task to start later if no room to complete before. Each machine can have a different shift pattern so they can't be inserted after computing positions.

I did play around with splitting the task into groups of single time unit slots (as I don't know where a task will go beforehand I can't split it into sizes which fit perfectly) which does provide the correct solution (is a small bug on task names btw if you have task groups with names 'T1' and 'T2' once you get over 10 tasks names clash).

The smallest unit I can feasibly use would be 1 minute per unit. I only tested a few task groups with 100 - 1000 single unit tasks, the performance was incredibly slow (it took minutes). With some clients they have 200+ machines and 4000+tasks, so this can't work.

Any suggestions appreciated.

kindlyfire commented 6 years ago

I'd be against making all periods that are not in periods blocked slots. I rely on the fact that periods contains the periods at which the task can start.

Example: task 1 and 2 of length 2. Set periods to [0, 2] => they nicely align. If you were to change the system, I'd have to make periods [0, 1, 2, 3], meaning a task could be scheduled starting at 1, which isn't the result I'm looking for. This is what half an exam schedule looks like (everything needs to align):

out

timnon commented 5 years ago

Yes, lets keep the periods the starting periods of jobs, otherwise it gets to mind-boggling. @DatalyticUk If you go down to a minute granularity, i think your problem will get too big the be solvable anyway,