pyschedule icon indicating copy to clipboard operation
pyschedule copied to clipboard

Variable length task?

Open glibersat opened this issue 7 years ago • 9 comments

Hi,

First of all, thanks for this wonderful piece of code! I've managed to modelize almost all my needs except one case. Is there a way to either:

  • have a variable task that can be dynamically extended to the beginning of its following?
  • block a resource until the next task is scheduled?

To understand my scenario, here's the use case:

  • we brew (1 day)
  • we ferment (~15 days)
  • and we bottle condition as soon as we're available

[Brew/R=brewhouse] <= [Ferment/R=Tank1] <..[variable length task/R=Tank1]..< [Bottle condition/R=Tank1+Capper]

The problem is that sometimes, we are busy or it's sunday and we want to wait a few days before bottle conditioning. How would you achieve that? Thank you!

glibersat avatar Sep 09 '18 17:09 glibersat

Hi, nice to hear that pyschedule is used to brew beer!

Regarding your problem, you could use some capacity constraint like setting:

Ferment.block = 1 Bottle.block = -1 For t in range(horizon): Tank1['block'][:t] <= 1

This would ensure that some bottle operation needs to take place before a new ferment is started.

You probably also want to ensure that the fermenting and bottleling are happening at the same tank for one beer. This works like Bottle >= Ferment*Tank1

If you have a concrete example, i could try to fix it, i am always looking for real-life examples to showcast.

timnon avatar Sep 10 '18 10:09 timnon

Thank you for you quick answer!

I've tried using the block strategy but I still experience 2 fermentations being scheduled one after the other then two bottling jobs. Weird!

I already had added a "same tank" constraint, that works quite well! But I did that using: bottling += fermentation * fermenters_all do you think that's correct?

Give me a few days to clean up my mess and I'll send you a link to the code. I'll be quite happy if you can help me fix the latest "bugs" and of course, you can use it as a showcast :)

Cheers!

glibersat avatar Sep 11 '18 20:09 glibersat

Maybe you should also constrain the number of bottle operations in a row by using Tank1['block'][:t] >= -1 ? But this should more be a problem the other way round, happy to take a look

timnon avatar Sep 11 '18 20:09 timnon

I've created a gist for you to read my (very dirty) code. Sorry for the quality, it's really a poc: https://gist.github.com/glibersat/c5a487178a7e6b82102aaa1a6066dd09

I hope it's clear enough, feel free to ask for informations where it's not understandable.

Thank you very much!

glibersat avatar Sep 11 '18 20:09 glibersat

Unfortunately, the constraint is still not working. See attached picture: multiple fermentations take place in tanks before bottling/transfer order (example: Tank "funky" and fermentations of "Fut Papayou" and "Yoga"). capture d ecran de 2018-09-11 23-05-20

glibersat avatar Sep 11 '18 21:09 glibersat

Had a quick look on my phone and spotted the following syntactic problem. You need to write

S += fermenter['block'][:t] <= 1

Otherwise, the constraint is created but not added to the scenario. Please check if that gives some progress. I will be able to run your code next sunday.

timnon avatar Sep 12 '18 15:09 timnon

My bad, I didn't know I had to add it. Now it works, in the expected order, thank you! Took about 50minutes but the result is good :) I'll now start cleaning a little bit and try to implement extra things after that.

glibersat avatar Sep 13 '18 07:09 glibersat

Excellent. If you have performance problems, check the option a “group“ interchangeable tasks, if you have any. I will have a look.

timnon avatar Sep 13 '18 07:09 timnon

Had a look at the gist above, some comments:

  • you can define chambre_garde = S.Resource('chambre_garde', size=7) to group these resources, this drops the computation time.
  • your can set e.g.
plot_color = ColorHash(name).hex
brewday = S.Task('{0}_brewday'.format(name),plot_color=plot_color)

to color all tasks in one batch with the same color. You can then plot without text using plotters.matplotlib.plot(S,show_task_labels=False,fig_size=(10,5)) to get better visuals right away.

  • for testing, it is quite handy to use a ratio_gap like solvers.mip.solve(S, kind="CBC", time_limit=3000, ratio_gap=1.2, msg=1). This will e.g. stop the computation as soon as a solution is found, which is provably only 20% off an optimal one

Looks like an awesome project, happy to see some newer version if available

timnon avatar Sep 23 '18 20:09 timnon