These docs are for v1.1. Click to read the latest docs for v2.0.

Shift Creation

Introduction

The Creation Solver can be used to assign shifts based on a given demand. Additional constraints can be placed on the problem like limiting inter-shift-start-times, accounting for limited resources used by certain shifts and/or requiring certain skills. Flexible work is executed as late as possible and user provided preferences and costs can be used by the solver.

Input-Output

The input of the creation solver consists of two parts:

  • The possible shifts and their properties
  • The demands that must be fulfilled by those shifts

The demands can be provided in two different ways, in fixed or flexible format. These cannot be mixed. Based on which type of demand is provided, the solver will run in fixed or flexible mode, more explanation on this later in the guide.

The output of the solver contains the selected shifts along with the amount of each shift that the solver assigned. The output is the same for fixed and flexible mode.

Input

Shift

PropertyTypeDescription
definitionArray of PatternA list of Patterns defining at what times this shift takes place.
skillstringspecifies the employee type that will execute the shift. Should match with a Demand skill field.
minintegerMinimum number of people that have to be assigned to this shift.
maxintegerMaximum number of people that can be assigned to this shift.
resourceRequirementsArray of Resource RequirementThe different resources and the number of each resource needed to execute this shift for one person (eg. 1 hammer).
costintegerThe cost of assigning a single person to this shift.
preferenceintegerA number that behaves like the cost of a shift. Used to specify a preference between two shifts that have the same cost.

Pattern

PropertyTypeDescription
fromstring (iso DateTime)Start time of the shift pattern.
tostring (iso DateTime)End time of the shift pattern.
breaksArray of BreakA list of breaks that take place between the from and to time.

Break

PropertyTypeDescription
startstring (iso DateTime)Start time of this break.
endstring (iso DateTime)End time of this break.

Resource Requirement

Used to specify that a shift requires a certain resource during the times when the shift takes place.
For example, a shift with skill 'worker' needs 1 hammer.

PropertyTypeDescription
resourceNamestringThe name of the required resource. A resource with this name also needs to be present in the resourceAvaliability list.
valueintegerThe amount of this resource that are needed when assigning 1 person to the shifts that holds this Resource Requirement.

Resource Availability

Used to specify which resources are available at all times and how many there are available of each resource.
An example would be that there are 5 hammers available.

PropertyTypeDescription
resourceNamestringThe name of the available resource.
maxintegerHow many units of the available resource can be used simultaneously.

Demand

The demands are used to specify when shifts of each skill are needed. When using fixed mode only the demand field needs to be specified. When using flexible mode only the min and max fields of the demands need to be specified. A solver instance can only contain fixed or flexible demands, not both.

PropertyTypeDescription
fromstring (iso DateTime)Start time of the Demand block.
tostring (iso DateTime)End time of the Demand block.
skillstringThe skill that is required to fulfil this demand.
demandintegerThe number of people needed during this Demand block. (when using fixed mode)
minintegerThe minimum total number of minutes that need to have been assigned to this skill by the end of this Demand block (when using flexible mode).
maxintegerThe maximum total number of minutes that can be been assigned to this skill by the end of this Demand block (when using flexible mode).

Example

In the example of Single Day Shift Creation we will be testing a simple demand curve against 2 patterns from 9am to 5pm and 10am to 2pm.

{
  "solver": "CREATE",
  "demands": [
    {
      "from": "2021-10-04T08:15:00",
      "to": "2021-10-04T11:30:00",
      "skill": "mechanic",
      "demand": 3
    },
    {
      "from": "2021-10-04T11:30:00",
      "to": "2021-10-04T15:15:00",
      "skill": "mechanic",
      "demand": 6
    }
  ],
  "resourceAvailabilities": [
    {
      "resourceName": "screwdriver",
      "max": 10
    }
  ],
  "patterns": [
    {
      "name": "MON:9-5",
      "skill": "mechanic",
      "min": 0,
      "max": 10,
      "cost": 80,
      "preference": 10,
      "resourceRequirements": [
        {
          "resourceName": "screwdriver",
          "value": 1
        }
      ],
      "definition": [
        {
          "from": "2021-10-04T09:00:00",
          "to": "2021-10-04T17:00:00",
          "breaks": [
            {
              "start": "2021-10-04T12:00:00",
              "end": "2021-10-04T13:00:00"
            }
          ]
        }
      ]
    },
    {
      "name": "FLEX:10-14",
      "skill": "mechanic",
      "min": 0,
      "max": 10,
      "cost": 45,
      "preference": 0,
      "resourceRequirements": [
        {
          "resourceName": "screwdriver",
          "value": 1
        }
      ],
      "definition": [
        {
          "from": "2021-10-04T10:00:00",
          "to": "2021-10-04T14:00:00"
        }
      ]
    }
  ]
}
{
  "solver": "CREATE",
  "demands": [
    {
      "from": "2021-10-04T08:15:00",
      "to": "2021-10-04T11:30:00",
      "skill": "mechanic",
      "min": 0,
      "max": 180
    },
    {
      "from": "2021-10-04T11:30:00",
      "to": "2021-10-04T15:15:00",
      "skill": "mechanic",
      "min": 120,
      "max": 480
    }
  ],
  "resourceAvailabilities": [
    {
      "resourceName": "screwdriver",
      "max": 10
    }
  ],
  "patterns": [
    {
      "name": "MON:9-5",
      "skill": "mechanic",
      "min": 0,
      "max": 10,
      "cost": 80,
      "preference": 10,
      "resourceRequirements": [
        {
          "resourceName": "screwdriver",
          "value": 1
        }
      ],
      "definition": [
        {
          "from": "2021-10-04T09:00:00",
          "to": "2021-10-04T17:00:00",
          "breaks": [
            {
              "start": "2021-10-04T12:00:00",
              "end": "2021-10-04T13:00:00"
            }
          ]
        }
      ]
    },
    {
      "name": "FLEX:10-14",
      "skill": "mechanic",
      "min": 0,
      "max": 10,
      "cost": 45,
      "preference": 0,
      "resourceRequirements": [
        {
          "resourceName": "screwdriver",
          "value": 1
        }
      ],
      "definition": [
        {
          "from": "2021-10-04T10:00:00",
          "to": "2021-10-04T14:00:00"
        }
      ]
    }
  ]
}
{
  "solver": "CREATE",
  "resourceAvailabilities": [
    {"resourceName": "screwdriver", "max": 10}
  ],
  "weights": {
    "totalDemandWeight": {
      "initScore": 0,
      "hardScore": 1,
      "mediumScore": 0,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "resourceAvailabilityWeight": {
      "initScore": 0,
      "hardScore": 1,
      "mediumScore": 0,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "underDemandWeight": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 10,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "overDemandWeight": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 1,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "underDemandWeightCumulative": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 100,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "timeWastedWeightCumulative": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 50,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "asLateAsPossible": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 1,
      "softScore": 0,
      "feasible": true,
      "solutionInitialized": true
    },
    "costWeight": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 0,
      "softScore": 18,
      "feasible": true,
      "solutionInitialized": true
    },
    "preferredShifts": {
      "initScore": 0,
      "hardScore": 0,
      "mediumScore": 0,
      "softScore": 1,
      "feasible": true,
      "solutionInitialized": true
    }
  },
  "patterns": [
    {
      "name": "MON:9-5",
      "skill": "mechanic",
      "min": 0,
      "max": 10,
      "cost": 80,
      "preference": 10,
      "resourceRequirements": [
        {
          "resourceName": "screwdriver",
          "value": 1
        }
      ],
      "definition": [
        {
          "from": "2021-10-04T09:00:00",
          "to": "2021-10-04T17:00:00",
          "breaks": [
            {
              "start": "2021-10-04T12:00:00",
              "end": "2021-10-04T13:00:00"
            }
          ]
        }
      ]
    },
    {
      "name": "FLEX:10-14",
      "skill": "mechanic",
      "min": 0,
      "max": 10,
      "cost": 45,
      "preference": 0,
      "resourceRequirements": [
        {
          "resourceName": "screwdriver",
          "value": 1
        }
      ],
      "definition": [
        {
          "from": "2021-10-04T10:00:00",
          "to": "2021-10-04T14:00:00"
        }
      ]
    }
  ],
  "demands": [
    {
      "from": "2021-10-04T08:15:00",
      "to": "2021-10-04T11:30:00",
      "skill": "mechanic",
      "min": 0,
      "max": 180
    },
    {
      "from": "2021-10-04T11:30:00",
      "to": "2021-10-04T15:15:00",
      "skill": "mechanic",
      "min": 120,
      "max": 480
    }
  ]
}

Output

The solver returns a json in which the original instance can be found. The output is given in the solution field and contains a list of Created Shift.

Created Shift

Used to represent the output. Each entry corresponds with a pattern that was given as input.

PropertyTypeDescription
patternstringThe name of the corresponding pattern that was given as input.
skillstringThe skill of the corresponding pattern.
valueintegerThe amount of people the solver chose to assign to this shift.
2592

Output: supply of shifts vs demand

Objective

The solver will try to find the best values to assign to each shift. In order to do this, constraints are used to determine how good a solution is. These constraints represent different numbers used to score the solution. These constraints are prioritised by being either HARD, MEDIUM or SOFT. All improvements in HARD constraints trump any deterioration in MEDIUM or SOFT constraints. All improvements in MEDIUM constraints trump any deterioration in SOFT constraints.

Constraints

In this table is a summary of all constraints and what they mean. The weight and priority of each constraint can be adjusted using the weights field in a solver request. An example of this can be found on this page in 'Single Day Creation With Weights'. This is an advanced feature, the results of tweaking these weights is often hard to predict.

NameLevelDescription
Total DemandHARDThe total required demand must be met (only relevant in fixed mode).
Resource AvailabilityHARDOnly the maximum number of available resources for every resource type can be used.
Min Time Between Shift StartHARDTwo shifts with different start times cannot have start times closer to each other than the minimum allowed time specified (optionally) by the user.
Under demandMEDIUMFor every demand time block, the supply that is less than the demand must be minimised.
Over demandMEDIUMFor every demand time block, the supply that is more than the demand must be minimised.
As Late As PossibleMEDIUMOnly relevant in flexible mode. When planning shifts, try to plan shifts as late as possible while respecting the Over Demand and Under Demand constraints.
CostsSOFTCost based minimisation of the shifts.
PreferenceSOFTPreference based maximisation of the shifts.

Fixed Mode and Flexible Mode

The solver can run in two modes: Fixed Mode and Flexible Mode.

Fixed Mode

In Fixed Mode the solver has no choice about when work needs to be done. In this mode, the input specifies how many people we need at which point in time for what skill.

When a demand block that starts at 9am and ends at 10am has a demand value of 3, that means that 3 people are required for that skill between 9am and 10am.

Flexible Mode

In Flexible Mode the solver has some freedom over when certain work can be done. In this mode, the input specifies when volumes of work can be done and when they have to be completed.

When a demand block that starts at 9am and ends at 10am has a min value of 60 and a max value of 120, that means that a minimum of 60 minutes of work needs to have been done by 10am and that the maximum amount of work that can be done by 10am is 120 minutes.

examples of Flexible Mode

Let's say that nobody has worked before 9am, meaning that the total amount of work done by the start of this demand block is 0. We now assign 1 person to a shift that starts at 9am and ends at 10am. This means that during this hour, 60 minutes of work will be done (since 1 person is assigned for 1 hour). That means that the actual volume of work done by the end of this demand block will be 60 minutes. This is okay since it lies between our given min and max for the demand block.

Let's say that instead of 1 person, 2 people are assigned during this interval. The total amount of work that will be done by the end of this interval is now 120 minutes. This is also fine since it falls within our given min and max for this demand block.

Will the solver choose 1 or 2 people?

In this simple example, the solver will choose to assign only 1 person. We want to schedule work as late as possible so we don't want to do 120 minutes of work when only 60 minutes are needed, even if there are already 120 minutes of work available.

If this demand block was part of a larger instance, then the solver might have preferred to place 2 people here. One reason might be that later in the day, a lot of additional work needs to be completed (which means that the min field has a high value). In that case we might have chosen to place 2 people here because else we end up being too late with other work down the road.