This example is an advanced version of a previous example, Vacation Days. The full specification can be found here: Vacation Days Advanced. In this challenge, we need to calculate vacation days for employees, based on their age and years of service and some more information. In total, there are seven rules:

Vacation Days

1. Every employee receives at least 22 vacation days.

2. Employees younger than 18 or at least 60 years, or employees with at least 30 years of service can receive extra 5 days.

3. Employees with at least 30 years of service and also employees of age 60 or more, can receive extra 3 days, on top of possible additional days already given.

4. If an employee has at least 15 but less than 30 years of service, extra 2 days can be given. These 2 days can also be provided for employees of age 45 or more.

5. A college student is eligible to 1 extra vacation day.

6. If an employee is a veteran, 2 extra days can be given.

7. The total number of vacation days cannot exceed 29.

The seventh rule of the specification is what makes this challenge so interesting. At first, it would seem intuitive to place a soft maximum on the vacation days: if an employee exceeds 29 days, give them 29 days. However, in Jacob Feldman’s OpenRules implementation he points out that this would mean we can simply subtract days from some types and only give partial number of eligible days. If we assume that this is not allowed, an employee would not want to make use of every type of vacation days for which they are eligible, but only the ones that get their total closest to 29. In this small example this does not matter too much, but in bigger systems with more mutually exclusive rules this could make a difference.

In this implementation, we will deal with two types of vacation rules: the ones for which an employee is eligible, and the ones that they ‘apply’, e.g. actually use. In other words, they will not have to use all vacation days for which they are eligible. We will try to pick the used days in such a way that a maximum number is achieved for every employee.

To fill out our glossary, we start by adding types for the string-based domains, i.e., the employees and the different rules. For the example, we create three dummy employees: Huey, Dewey and Louie.

Type
Name Type Values
Employee String Huey, Dewey, Louie
Rule String r2, r3, r4, r5, r6

Next, we create functions to map every employee to their age, service years and total vacation days.

Function
Name Type
age of Employee Int
service years of Employee Age
vacation days of Employee Int

In order to express whether an employee is a veteran or student, we add 2 relations. We also add a relation which keeps track of the eligible rules, and the ‘applied’ rules.

Relation
Name
Employee is Student
Employee is Veteran
Employee is eligible for Rule
Employee applies Rule

Now that our glossary is complete, we can start creating decision and constraint tables. We start by implementing a table for every rule, to express when an exmployee is eligible.

Define the eligibility for rule 2
A Employee age of Employee service years of Employee Employee is eligible for r2
1 - < 18 - Yes
2 - ≥ 60 - Yes
3 - [18, 60) ≥ 30 Yes

Define the eligibility for rule 3
A Employee age of Employee service years of Employee Employee is eligible for r3
1 - - ≥ 30 Yes
2 - < 60 < 30 Yes

Define the eligibility for rule 4
A Employee age of Employee service years of Employee Employee is eligible for r4
1 - - [15, 30) Yes
2 - ≥ 45 - Yes

Define the eligibility for rule 5
A Employee Employee is Student Employee is eligible for r5
1 - Yes Yes

Define the eligibility for rule 6
A Employee Employee is Veteran Employee is eligible for r6
1 - Yes Yes

Now that we can decide which employee is eligible for what rules, we need a way to calculate the number of days. We do this by creating a `C+` table, in which we sum the number of days per rule based on which rules are applied.

Calculate vacation days based on which rules are applied.
C+ Employee Employee applies r2 Employee applies r3 Employee applies r4 Employee applies r5 Employee applies r6 vacation days of Employee
1 - - - - - - 22
2 - Yes - - - - 5
3 - - - - - - 3
4 - No - Yes - - 2
5 - - - - Yes - 1
6 - - - - - Yes 1

Just stating that rules can be applied is of course not enough. We need to make sure that only rules for which an employee is eligible can be applied. In order to do this, we create a constraint table. Note how it states that a rule can only be applied if the employee is eligible, but that it does not state the opposite. In other words, not every eligible rule needs to be applied.

Only apply rule if eligible.
E* Employee Rule Employee applies Rule Employee is eligible for Rule
1 - - Yes Yes

Of course, we also need to add a constraint that no employee can have more than 29 vacation days.

Max days.
E* Employee vacation days of Employee
1 - ≤ 29

There we have it! We are now able to calculate vacation days for employees. However, just this calculation alone does not do much yet. We need to be able to look for the solution in which the employees have a maximum number of vacation days. To do this, we sum all the vacation days of employees and maximize it. This is a bit unfortunate, but because we added quantification we have to maximize for all employees. If we did not use quantification, we could run the model once for every employee. In this case the employees do not affect each other, so solving the problem for all of them at the same time creates no implications.

Total days.
C+ Employee Total Days
1 - vacation days of Employee

Goal
Maximize Total Days

To now check the amount of vacation days our employees get, we can insert a data table containing their information, and run the solver.

Employee Info
D Employee age of Employee service years of Employee Employee is Veteran Employee is Student
1 Huey 17 1 Yes No
2 Dewey 70 31 No Yes
3 Louie 35 16 No No

This gives us the following solution:

```Model 1
==========
vacation_days_of_Employee := {Huey->31, Dewey->30, Louie->27, Donald->26}.
age_of_Employee := {Huey->17, Dewey->70, Louie->35, Donald->46}.
service_years_of_Employee := {Huey->1, Dewey->31, Louie->16, Donald->10}.
Employee_eligible_for_Rule := {(Huey,r2), (Huey,r3), (Huey,r5), (Dewey,r2), (Dewey,r3), (Dewey,r4), (Louie,r3), (Louie,r4), (Donald,r3), (Donald,r4), (Donald,r6)}.
Employee_is_Student := {Huey}.
Employee_is_Veteran := {Donald}.
Employee_applies_Rule := {(Huey,r2), (Huey,r3), (Huey,r5), (Dewey,r2), (Dewey,r3), (Louie,r3), (Louie,r4), (Donald,r3), (Donald,r6)}.

Elapsed Time:
0.955
```