How to delay the job's endDate when it crosses a holiday?

111 views
Skip to first unread message

Kent Zhang

unread,
Aug 18, 2022, 1:34:43 AM8/18/22
to OptaPlanner development
In the Project Job Scheduling example, if some dates are holidays, the resources are not used. So, if a job crosses a holiday, its end date should be delayed. For example, if a job that duration is 2 days, it normally starts on day 1 and ends on day 2. But if the 2nd day is a holiday, then it must be delayed by 1 day, ends on day 3. If its duration is 3 days, then the end date will have to be delayed from day 3 to day 4.
How to implement this scenario in the Project Job Schedule example? I have made the following attempts on the example, but they all failed.
1. In the updatePredecessorsDoneDate method of the PredecessorsDoneDateUpdatingVariableListener class. I check if the job crosses a holiday, the predecessorsDoneDate is deferred, but throws the UndoMove corruption exception.
2. In the getEndDate method of the Allocation class. I check if the job crosses a holiday, the endDate is deferred, but a VariableListener corruption exception is thrown.

So, how to implement this function? Can you give me some ideas?

Many thank you!

Osamu Kuniyasu

unread,
Aug 18, 2022, 7:04:14 PM8/18/22
to OptaPlanner development
Hi,

One idea was adding preprocessing and post processing as a wrapper.
the wrapper does below things.
1) transfer date(day and hour) to integer index(time index), which depends on time granularities.
     in my case it was 30 minutes unit. the time index skips from end of business hour to beginning of next business hour, including weekends and holidays.
     some constraints are for date of week, early morning, evening working. it uses a function to transfer from the time index to date.
2) solve by OptaPlanner.
3) transfer all time index to date at the solution output.

in my case, I did the preprocessing by Drools to create a PlanningSolution from input data.

hope it helps,
Osamu Kuniyasu

2022年8月18日木曜日 14:34:43 UTC+9 kent...@gmail.com:

Kent Zhang

unread,
Aug 19, 2022, 3:46:20 AM8/19/22
to OptaPlanner development
Thank you very much, Mr. Osamu Kuniyasu.

Do you mean to add preprocessing and post-processing for each allocation, and then use them to skip weekends or holidays when an allocation's endDate locates weekends or holidays?

Best regards,
Kent

Kent Zhang

unread,
Aug 19, 2022, 2:27:34 PM8/19/22
to OptaPlanner development
I have another idea, I don't know does it work or not.

In the real world, the duration of a job is fixed, but its actual duration depends on the resource to which it is assigned, due to the rest time doesn't work(if each resource has a different rest plan). So, there is a different actual duration when assigned to different resources for the same job.
Then should I let the actual duration as a shadow variable, and update it when the genuine planning variables - executionMode and delay changed?
In another word, when a job overlaps the rest time, delay its endDate by increasing its duration.

Osamu Kuniyasu

unread,
Aug 19, 2022, 7:35:23 PM8/19/22
to OptaPlanner development
Hi Kent,

Skipping holidays and weekends is done in preprocessing, not during the solving of OptaPlanner.
The data while OptaPlanner is solving are in the form of data that is solved only on business days, with no weekends or holidays.

In your case, that time granularity is one day.
Time Index : Date
1 : Aug 1st
2 : Aug 2nd
~omit~
5 : Aug 5th
6 : Aug 8th

The data in OptaPlanner uses Time Index. this transfer is done in preprocessing.
After get a best solution to use, transfer time index values to date for output solution.

Hope it helps,
Osamu Kuniyasu

2022年8月19日金曜日 16:46:20 UTC+9 kent...@gmail.com:

Osamu Kuniyasu

unread,
Aug 19, 2022, 8:27:55 PM8/19/22
to OptaPlanner development

We need a different control for the different blocks for each resource.
A block can be a vacation, an already scheduled training or assignment, a team meeting, machine maintenance, etc.
To represent these, a PlanningSolution is created with pre-set tasks and allocations that cannot be moved.
This is the same method used to find another solution from a solution that already exists.

However, this method does not allow assignments that overlap with blocks by default.
If you want to make the assignment overlap with blocks,
you need to extend the startDate or endDate in the solving...(a), and you also need to custom the daily load calculation...(b).

That customize of (a) is processing-heavy I think, as you need search overwrap blocks inside a PlanningSolution.

let me try to think of one customization of (a) and (b).
I would prepare a TreeMap by starDate of blocks, another TreeMap by endDate of blocks for each resources.
I assume that blocks does not increase or decrease while solving, so it is okay that facts (Resource) has blocks.
and your allocation's startDate and endDate includes a block's startDate or endDate, you delay allocation's startDate or endDate.
and you store a set of overwrap blocks in the allocation for customization of (b).
and ... In the customization of (b), ignore workload of a date in blocks' duration for the allocation.

I'm not sure if these customizations meet the time requirements allowed for a solving.
but we can't help it if the delay for blocks is absolutely necessary.

Hope it helps,
Osamu Kuniyasu


2022年8月20日土曜日 3:27:34 UTC+9 kent...@gmail.com:

kent...@gmail.com

unread,
Aug 20, 2022, 1:17:01 AM8/20/22
to Osamu Kuniyasu, OptaPlanner development
Great! Thank you, Osamn.

I generate the available date blocks in preprocessing, filter out the unusable dates(such as weekends) to get a date series with only working days, and index these days  into a continuous date-index list, so that the jobs are  assigned to the date-index (only contains available date). And then when I get
the planning result, in post-processing, convert the date-index into date. 
Is that correct?


---Original---
From: "Osamu Kuniyasu"<okun...@redhat.com>
Date: Sat, Aug 20, 2022 07:35 AM
To: "OptaPlanner development"<optapla...@googlegroups.com>;
Subject: [optaplanner-dev] Re: How to delay the job's endDate when it crosses a holiday?

Hi Kent,

Skipping holidays and weekends is done in preprocessing, not during the solving of OptaPlanner.
The data while OptaPlanner is solving are in the form of data that is solved only on business days, with no weekends or holidays.

In your case, that time granularity is one day.
Time Index : Date
1 : Aug 1st
2 : Aug 2nd
~omit~
5 : Aug 5th
6 : Aug 8th

The data in OptaPlanner uses Time Index. this transfer is done in preprocessing.
After get a best solution to use, transfer time index values to date for output solution.

Hope it helps,
Osamu Kuniyasu

2022年8月19日金曜日 16:46:20 UTC+9 kent...@gmail.com:
Thank you very much, Mr. Osamu Kuniyasu.


Do you mean to add preprocessing and post-processing for each allocation, and then use them to skip weekends or holidays when an allocation's endDate locates weekends or holidays?

Best regards,
Kent

On Friday, August 19, 2022 at 7:04:14 AM UTC+8 Osamu Kuniyasu wrote:
Hi,

One idea was adding preprocessing and post processing as a wrapper.
the wrapper does below things.
1) transfer date(day and hour) to integer index(time index), which depends on time granularities.
     in my case it was 30 minutes unit. the time index skips from end of business hour to beginning of next business hour, including weekends and holidays.
     some constraints are for date of week, early morning, evening working. it uses a function to transfer from the time index to date.
2) solve by OptaPlanner.
3) transfer all time index to date at the solution output.

in my case, I did the preprocessing by Drools to create a PlanningSolution from input data.

hope it helps,
Osamu Kuniyasu

2022年8月18日木曜日 14:34:43 UTC+9 kent...@gmail.com:
In the Project Job Scheduling example, if some dates are holidays, the resources are not used. So, if a job crosses a holiday, its end date should be delayed. For example, if a job that duration is 2 days, it normally starts on day 1 and ends on day 2. But if the 2nd day is a holiday, then it must be delayed by 1 day, ends on day 3. If its duration is 3 days, then the end date will have to be delayed from day 3 to day 4.
How to implement this scenario in the Project Job Schedule example? I have made the following attempts on the example, but they all failed.
1. In the updatePredecessorsDoneDate method of the PredecessorsDoneDateUpdatingVariableListener class. I check if the job crosses a holiday, the predecessorsDoneDate is deferred, but throws the UndoMove corruption exception.
2. In the getEndDate method of the Allocation class. I check if the job crosses a holiday, the endDate is deferred, but a VariableListener corruption exception is thrown.

So, how to implement this function? Can you give me some ideas?

Many thank you!

--
You received this message because you are subscribed to a topic in the Google Groups "OptaPlanner development" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/optaplanner-dev/mjITvDLU5RU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to optaplanner-d...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/optaplanner-dev/ee5ce0e0-dcc3-4b6e-bf10-b1be0add5eb4n%40googlegroups.com.

Osamu Kuniyasu

unread,
Aug 20, 2022, 7:42:29 PM8/20/22
to OptaPlanner development
Hi Kent,

Yes, that's right. Good luck.

Osamu Kuniyasu

2022年8月20日土曜日 14:17:01 UTC+9 kent...@gmail.com:

kent...@gmail.com

unread,
Aug 21, 2022, 1:29:03 AM8/21/22
to Osamu Kuniyasu, OptaPlanner development

OK, thank you so much, Osamu.

I think I understand your idea, I'll try it out. 
en~~ there are many details that need thinking about and designing in it.
And thanks again!

Best regards!
---Original---
From: "Osamu Kuniyasu"<okun...@redhat.com>
Date: Sun, Aug 21, 2022 07:42 AM
To: "OptaPlanner development"<optapla...@googlegroups.com>;
Subject: Re: [optaplanner-dev] Re: How to delay the job's endDate when it crosses a holiday?

Kent Zhang

unread,
Aug 22, 2022, 10:38:08 PM8/22/22
to Osamu Kuniyasu, OptaPlanner development
Hi Osamu,

I created a time index that skips the weekends and holidays. But there are different business days for each resource, so, their indexes look like the following:

For Resource-1, its time index and date mapping:

Time Index: Date
1 : Aug 1st
2 : Aug 2nd
~omit~
5 : Aug 5th
6 : Aug 8th

But for Resource-2, its  maintenance date is Aug 7th and Aug 8th, so the mapping looks like this:
Time Inded:Date


1 : Aug 1st
2 : Aug 2nd
~omit~
5 : Aug 6th
6 : Aug 9th

while updating predecessorDoneDate in class PredecessorsDoneDateUpdatingVariableListener, to derive an allocation's predecssorDoneDate, I need to use the time index map to find the date. but when a move that the executionMode of the allocation or the predecessor allocation is null, I could not get the resource, so I could not find their date to derive predecessorDoneDate.

is there a similar case in your situation? If yes, how do you get the predecessorDoneDate when an executionMode of an allocation is null?

Thank you in advance


Kent
Best regards!
--
With kind regards,
Kent Zhang

Kent Zhang

unread,
Aug 22, 2022, 11:03:00 PM8/22/22
to Osamu Kuniyasu, OptaPlanner development
I mean, because the time index of each resource is different when I find the predecessorDoneDate, it needs to convert to date, to find which predecessor has a later ending date. However, when the excutionMode of an allocation is null(in the CH phase), it is impossible to know which resource it gets, without the time index of a resource, could not know which predecessor allocation ends later.

Richard Bourner

unread,
Aug 22, 2022, 11:37:49 PM8/22/22
to Kent Zhang, optapla...@googlegroups.com, Osamu Kuniyasu
Ken,

One idea would be for you to build a list of indexes that are common for all resources, and use constraints to prevent some indexes to be used by specific resources.
Example:

Time Index: Date
    1 : Aug 1
    2 : Aug 2
    ~omit~
    5 : Aug 5
    6 : Aug 6
    7 : Aug 7
    8 : Aug 8
    9 : Aug 9
    etc.
With a constraint that prevents some assignment's combinations, such as Resource-1 to indexes 6 & 7, and Resources-2 to indexes 7 & 8. The mapping between indexes and maintenance would be part of the pre-processing.
Something like this:

Time Index: Date
    1 : Aug 1
    2 : Aug 2
    3 : Aug 5
    4 : Aug 6
    5 : Aug 7
    6 : Aug 8
    7 : Aug 9
    etc.
And Resource-1 cannot be assigned to indexes 4 and 5, and Resources-2 to indexes 5 & 6.


BTW there is no real need to skip indexes 3 and 4 during pre-processing, since no resource would use them as long as you keep the same logic during post-processing.
Also, if Aug 7 is forbidden for all resources, you could also skip the corresponding index altogether.


HTH
Richard
You received this message because you are subscribed to the Google Groups "OptaPlanner development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to optaplanner-d...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/optaplanner-dev/CADNPm8JAaczR5VY5cDpyP5w-CmXbE4amGWhQswN%2B24APDCR-Wg%40mail.gmail.com.

Kent Zhang

unread,
Aug 23, 2022, 12:46:35 AM8/23/22
to Richard Bourner, OptaPlanner development, Osamu Kuniyasu
Thank you for your idea, Richard.

Do you mean that all resources have the same index list and map to the same date list? Skip those days when all resources are unavailable (such as weekends), but not skip the day that some resources are available, and some unavailable days (such as only one or two resources in all are maintained). 
For the unavailable index of a resource, use a constraint to prevent the work assigned to it?


Kent

Richard Bourner

unread,
Aug 23, 2022, 10:47:18 AM8/23/22
to Kent Zhang, OptaPlanner development, Osamu Kuniyasu
Yes. 
The solver does not even have to deal with dates, only with the indexe. The mapping between both being done within the pre- and post-processing.
And then you can have your constraints penalize the forbidden assignments (eg 1 rule for for maintenance days per resource, etc.).

Kent Zhang

unread,
Aug 23, 2022, 3:08:53 PM8/23/22
to OptaPlanner development
I'm afraid this approach doesn't work well for my situation.
Because there are many kinds of resources that vary greatly in time, and many jobs are so long (more than 1 day).
The following chart shows that resource 1 and resource 2 have a large correlation between their working and non-working hours.

In the following chart, the job-1, its duration is 48 hours, if assigned to resource 1, it will be completed on day 3, but placed on resource 2, it will be completed on day 4.

1.png

Kent Zhang

unread,
Aug 23, 2022, 3:37:05 PM8/23/22
to OptaPlanner development
I am trying to split a task into multiple jobs with a shorter duration, e.g. split a job whose duration is 48 hours into 48 jobs with a duration is 1 hour,  such that any of the smaller jobs can be assigned separately, and then use a constraint to ensure that these 48 jobs are assigned to the same resources. I know this would greatly increase the number of planning variables and lead to larger problem size.

Also, I saw a post on StackOverflow that was similar to this situation, and @Geoffrey provided a suggestion, but I don't quite understand the approach.
https://stackoverflow.com/questions/21205981/optaplanner-project-job-scheduling-weekends-and-holidays

Hello Geoffrey, could you please give me some advice? Thank you in advance!

Osamu Kuniyasu

unread,
Aug 23, 2022, 6:51:47 PM8/23/22
to OptaPlanner development

I forgot to mention about splitting jobs.
I applied this way instead of the customization of delay startDate or endDate in the variableListener which mentioned below response.

0) decide time granularity as big as possible
1) make time index by the granularity and skip common breaks, which depends on your problem such as weekends, holidays, early morning, lunch breaks,  and late night, and so on. as Richard said, time index is common to all resources.
2) make preassigned jobs in preprocessing for individual (not common) breaks.
3) split jobs by the granularity (in preprocessing)

In your figure, it seems the time granularity would be 8 hours to 4 hours?
as you mentioned, small time granularity increase the problem size.

Hope it helps,
Osamu Kuniyasu

2022年8月20日土曜日 9:27:55 UTC+9 Osamu Kuniyasu:

Kent Zhang

unread,
Aug 23, 2022, 9:32:27 PM8/23/22
to OptaPlanner development
Thank you so much for your mention, Osamu.

Let me summarize it.
1. Create a common index list for all resources that skips the common non-working hour, such as the weekends and holidays e.c.
2. Split jobs by granularity, the granularity is the minimum continuous working time.
3. For the individual breaks of some resources, make preassigned dummy jobs by @PlanningPin.
4. Build a constraint to prevent the jobs that split from the same job be assigned to different resources, ff all the time a job that before split must be assigned to one resource.
Is that right?

Best regards!
Kent


--
You received this message because you are subscribed to the Google Groups "OptaPlanner development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to optaplanner-d...@googlegroups.com.

Osamu Kuniyasu

unread,
Aug 25, 2022, 6:40:59 AM8/25/22
to OptaPlanner development

Hi Kent,

Yes, that’s correct.

[4] is penalty constraint.

After it works with a small test data and you face a challenge at scale, valueSelection would work nicely to avoid different resources for divided jobs.

Best Regards,
Osamu Kuniyasu

2022年8月24日水曜日 10:32:27 UTC+9 kent...@gmail.com:

Kent Zhang

unread,
Aug 25, 2022, 1:38:28 PM8/25/22
to OptaPlanner development
Much appreciated! Osamu.
Thank you, especially for your patience and selfless sharing.

I have implemented the first 3 items of work as we discussed, but in step 4 - making sure the divided jobs that split from an original job, must be assigned to the same resource. I tried the hard constraint, but this is not feasible because the problem size is so large.
I also tried the valueSelection that you suggested, but the divided jobs are still assigned to various resources.

Here's what I did.
The divided jobs from an original job are joined into a predecessor and successor relationship and use a field named "groupId" to identify that they are from the same original job.
And I created a SelectionFilter class, and in the accept method, when a divided job has a predecessor from the same original job, and if they get different resources, the accept method returns false. But it doesn't work. the divided jobs from the same original job are still assigned to various resources.
Could you please share more about your solution to this?

Thank you again!

Best regard!
Kent


You received this message because you are subscribed to a topic in the Google Groups "OptaPlanner development" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/optaplanner-dev/mjITvDLU5RU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to optaplanner-d...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/optaplanner-dev/f04b3f26-a566-4b8a-8929-ec380802e087n%40googlegroups.com.

Osamu Kuniyasu

unread,
Aug 25, 2022, 9:42:33 PM8/25/22
to OptaPlanner development

Hi Kent,

You are doing good path.

Construction Heuristics (CH) selects variable which resource is not yet assigned.
So, your implementation of checking different resources are not for CH, I think.

Best Regards,
Osamu Kuniyasu

2022年8月26日金曜日 2:38:28 UTC+9 kent...@gmail.com:

Kent Zhang

unread,
Aug 26, 2022, 2:24:52 AM8/26/22
to OptaPlanner development
OK, I'll try it again carefully.
Thank you very much! Osamu.



Best regards!
Kent

Reply all
Reply to author
Forward
0 new messages