Skip to content

Nurse Scheduling API Reference

Data

Data model for NurseScheduling use case.

NurseSchedulingData

Bases: UcData

Data for the Nurse Scheduling Problem use case.

This class encapsulates all necessary information to define and solve a Nurse Scheduling problem instance. The unified formulation supports both 2-shift and 3-shift systems. Given a set of nurses and shifts, the problem creates rotating rosters that satisfy workforce requirements while respecting nurse preferences and day-off requests.

Attributes:

Name Type Description
name Literal['nurse_scheduling']

A constant identifier for this data type, always set to "nurse_scheduling". Used for registration and type identification in the use case registry.

n_nurses int

Number of nurses available for scheduling.

n_shifts int

Number of shifts in the schedule.

workforce NumPyArray

Required workforce W(d) for each shift d. 1D float array of length n_shifts.

effort NumPyArray

Effort level E(n) for each nurse n. 1D float array of length n_nurses.

max_shifts NumPyArray

Maximum number of (weighted) shifts F(n) each nurse can work. 1D int array of length n_nurses.

shift_weight NumPyArray

Weight for each shift indicating how much it "counts" toward a nurse's maximum shift allowance (used in constraint C3). A weight of 2.0 means the shift counts double — e.g., a night shift with weight 2.0 uses two of a nurse's allowed shifts. 1D float array of length n_shifts.

day_off_priority NumPyArray

Priority matrix g(n,d) for day-off requests. 2D float array of shape (n_nurses, n_shifts). 0 = no request, higher = stronger request.

Examples:

Create a simple nurse scheduling instance:

>>> data = NurseSchedulingData(
...     n_nurses=3,
...     n_shifts=6,
...     workforce=np.array([2.0, 2.0, 2.0, 2.0, 2.0, 2.0]),
...     effort=np.array([1.0, 1.0, 1.0]),
...     max_shifts=np.array([3, 3, 3]),
...     shift_weight=np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0]),
...     day_off_priority=np.zeros((3, 6)),
... )

plot(*, ax: Axes | None = None) -> Axes

Plot workforce requirements and shift weights.

Shows a bar chart of workforce requirements per shift with shift weights overlaid as a line.

Parameters:

Name Type Description Default
ax Axes | None

Matplotlib axes to draw on. Creates a new figure if None.

None

Returns:

Type Description
Axes

The axes with the plot.

to_string() -> str

Print the data.

Returns:

Type Description
str

String representation of the data.

from_values(n_nurses: int, n_shifts: int, workforce: np.ndarray | list[float], effort: np.ndarray | list[float], max_shifts: np.ndarray | list[int], shift_weight: np.ndarray | list[float], day_off_priority: np.ndarray | list[list[float]]) -> NurseSchedulingData staticmethod

Create a NurseSchedulingData instance from explicit values.

Parameters:

Name Type Description Default
n_nurses int

Number of nurses available for scheduling.

required
n_shifts int

Number of shifts in the schedule.

required
workforce ndarray | list[float]

Required workforce W(d) for each shift d. 1D array of length n_shifts.

required
effort ndarray | list[float]

Effort level E(n) for each nurse n. 1D array of length n_nurses.

required
max_shifts ndarray | list[int]

Maximum number of (weighted) shifts F(n) each nurse can work. 1D array of length n_nurses.

required
shift_weight ndarray | list[float]

Weight for each shift. 1D array of length n_shifts.

required
day_off_priority ndarray | list[list[float]]

Priority matrix g(n,d) for day-off requests. 2D array of shape (n_nurses, n_shifts).

required

Returns:

Type Description
NurseSchedulingData

A NurseSchedulingData instance with the given values.

generate_random(n_nurses: int = 10, n_shifts: int = 14, shift_system: str = 'two_shift', seed: int | None = None) -> NurseSchedulingData staticmethod

Generate a random Nurse Scheduling instance.

Parameters:

Name Type Description Default
n_nurses int

Number of nurses, by default 10.

10
n_shifts int

Number of shifts, by default 14.

14
shift_system str

Either "two_shift" or "three_shift", by default "two_shift". Controls how shift weights are generated.

'two_shift'
seed int | None

Random seed for reproducibility, by default None.

None

Returns:

Type Description
NurseSchedulingData

A randomly generated Nurse Scheduling data instance.

Formulation

Formulation for NurseScheduling use case.

NurseSchedulingFormulation

Bases: UcFormulation[NurseSchedulingData, NurseSchedulingSolution]

Constraint-based formulation for Nurse Scheduling Problem.

This formulation uses LunaModel's constraint system to create rotating rosters that satisfy workforce requirements while respecting nurse preferences and day-off requests. Supports both 2-shift and 3-shift systems through unified shift weight parameters.

Mathematical Formulation
Given:
    - N nurses with effort levels E(n) and max shifts F(n)
    - D shifts with workforce requirements W(d) and weights
    - Day-off priority matrix g(n,d)

Decision Variables:
    q[n,d] in {0,1} for each nurse n and shift d
    q[n,d] = 1 if nurse n is assigned to shift d

Objective:
    minimize sum_{n,d} g(n,d) * q[n,d]
    (minimize scheduling nurses on their day-off requests)

Constraints:
    1. No consecutive shifts:
       q[n,d] + q[n,d+1] <= 1 for each nurse n, shift d < D-1
    2. Workforce requirement:
       sum_n E(n)*q[n,d] >= W(d) for each shift d
    3. Max shift count (weighted):
       sum_d shift_weight(d)*q[n,d] <= F(n) for each nurse n
References
  • Nurse Scheduling Problem: https://en.wikipedia.org/wiki/Nurse_scheduling_problem

to_string(data: NurseSchedulingData) -> str staticmethod

Print the formulation.

Parameters:

Name Type Description Default
data NurseSchedulingData

The problem data.

required

Returns:

Type Description
str

String representation of the formulation.

formulate(data: NurseSchedulingData) -> Model staticmethod

Formulate the Nurse Scheduling Problem using constraint-based approach.

Creates a constraint-based formulation using binary decision variables for nurse-shift assignments with workforce, consecutive shift, and max shift count constraints.

Parameters:

Name Type Description Default
data NurseSchedulingData

The Nurse Scheduling instance data.

required

Returns:

Type Description
Model

A LunaModel ready to be solved.

Raises:

Type Description
EmptyDataError

If there are no nurses or no shifts.

InvalidProblemStructureError

If array dimensions do not match the declared sizes.

interpret(solution: Solution, data: NurseSchedulingData) -> NurseSchedulingSolution staticmethod

Extract solution from solver result.

Extracts the nurse-shift assignments from the solver solution and computes solution metrics including validity checks.

Parameters:

Name Type Description Default
solution Solution

The solver solution containing variable assignments.

required
data NurseSchedulingData

The original Nurse Scheduling instance data.

required

Returns:

Type Description
NurseSchedulingSolution

A structured solution object with schedule and metrics.

Raises:

Type Description
NoSolutionFoundError

If the solver did not find a solution.

Solution

Solution model for NurseScheduling use case.

NurseSchedulingSolution

Bases: UcSolution

Solution for the Nurse Scheduling Problem.

Attributes:

Name Type Description
name Literal['nurse_scheduling']

Identifier for this solution type.

schedule dict[int, list[int]]

Maps nurse index to list of assigned shift indices.

n_shifts_assigned int

Total number of nurse-shift assignments.

is_valid bool

Whether constraints are satisfied (no consecutive shifts, workforce met).

plot(data: NurseSchedulingData | None = None, *, ax: Axes | None = None) -> Axes

Plot the nurse schedule as a grid.

Shows a heatmap where rows are nurses and columns are shifts. Assigned shifts are highlighted in color.

Parameters:

Name Type Description Default
data NurseSchedulingData | None

Problem data. When provided, workforce coverage info is shown.

None
ax Axes | None

Matplotlib axes to draw on. Creates a new figure if None.

None

Returns:

Type Description
Axes

The axes with the plot.

to_string() -> str

Print the solution.

Returns:

Type Description
str

String representation of the solution.

Instance

Instance model for NurseScheduling use case.

NurseSchedulingInstance

Bases: UcInstance[NurseSchedulingData, NurseSchedulingFormulation, NurseSchedulingSolution]

Instance combining data and formulation for NurseScheduling.

Collection

Collection of NurseScheduling instances.

NurseSchedulingCollection

Bases: UcInstanceCollection[NurseSchedulingInstance]

Collection of Nurse Scheduling Problem instances.

This collection provides methods to generate benchmark instances with various characteristics for testing and evaluation, including random, two-shift, and three-shift system instances.

from_random(min_num_nurses: int | None = None, max_num_nurses: int | None = None, num_instances: int = 1, *, sizes: Sequence[int] | None = None, n_shifts: int = 14, shift_system: str = 'two_shift', seed: int | None = None) -> NurseSchedulingCollection classmethod

Generate random nurse scheduling instances.

Generates instances with varying nurse counts and random parameters.

Parameters:

Name Type Description Default
min_num_nurses int | None

Minimum number of nurses per instance.

None
max_num_nurses int | None

Maximum number of nurses per instance.

None
num_instances int

Number of instances per size, by default 1.

1
n_shifts int

Number of shifts per instance, by default 14.

14
shift_system str

Either "two_shift" or "three_shift", by default "two_shift".

'two_shift'
seed int | None

Random seed for reproducibility, by default None.

None
sizes Sequence[int] | None

Explicit sizes to generate, e.g. [10, 50, 100], instead of a range. Mutually exclusive with min_num_nurses/max_num_nurses, by default None.

None

Returns:

Type Description
NurseSchedulingCollection

Collection containing generated instances.

Examples:

>>> collection = NurseSchedulingCollection.from_random(
...     min_num_nurses=5,
...     max_num_nurses=10,
...     num_instances=2,
...     seed=42,
... )

from_two_shift(min_num_nurses: int | None = None, max_num_nurses: int | None = None, num_instances: int = 1, *, sizes: Sequence[int] | None = None, n_shifts: int = 14, seed: int | None = None) -> NurseSchedulingCollection classmethod

Generate 2-shift system nurse scheduling instances.

Shift weights are based on h_1(n)*h_2(d) pattern where alternating shifts have different weights (e.g., day/night).

Parameters:

Name Type Description Default
min_num_nurses int | None

Minimum number of nurses per instance.

None
max_num_nurses int | None

Maximum number of nurses per instance.

None
num_instances int

Number of instances per size, by default 1.

1
n_shifts int

Number of shifts per instance, by default 14.

14
seed int | None

Random seed for reproducibility, by default None.

None
sizes Sequence[int] | None

Explicit sizes to generate, e.g. [10, 50, 100], instead of a range. Mutually exclusive with min_num_nurses/max_num_nurses, by default None.

None

Returns:

Type Description
NurseSchedulingCollection

Collection containing 2-shift system instances.

Examples:

>>> collection = NurseSchedulingCollection.from_two_shift(
...     min_num_nurses=5,
...     max_num_nurses=10,
...     seed=42,
... )

from_three_shift(min_num_nurses: int | None = None, max_num_nurses: int | None = None, num_instances: int = 1, *, sizes: Sequence[int] | None = None, n_shifts: int = 21, seed: int | None = None) -> NurseSchedulingCollection classmethod

Generate 3-shift system nurse scheduling instances.

Shift weights are based on day_type*shift_type pattern: - shift_type: daytime=1.0, early_night=1.5, late_night=2.0 - day_type: weekday=1.0, weekend=2.0

Parameters:

Name Type Description Default
min_num_nurses int | None

Minimum number of nurses per instance.

None
max_num_nurses int | None

Maximum number of nurses per instance.

None
num_instances int

Number of instances per size, by default 1.

1
n_shifts int

Number of shifts per instance, by default 21 (7 days * 3 shifts).

21
seed int | None

Random seed for reproducibility, by default None.

None
sizes Sequence[int] | None

Explicit sizes to generate, e.g. [10, 50, 100], instead of a range. Mutually exclusive with min_num_nurses/max_num_nurses, by default None.

None

Returns:

Type Description
NurseSchedulingCollection

Collection containing 3-shift system instances.

Examples:

>>> collection = NurseSchedulingCollection.from_three_shift(
...     min_num_nurses=8,
...     max_num_nurses=15,
...     seed=42,
... )

filter_infeasible(max_runtime: float = 3600, *, quiet: bool = True) -> list[bool]

Drop the instances of this collection that have no feasible solution.

Every instance is formulated and handed to SCIP, which stops as soon as it finds the first feasible solution. An instance is removed from the collection when SCIP proves the model infeasible, when no solution turns up within max_runtime, or when formulating it fails altogether. This keeps randomly generated instances from breaking a downstream pipeline.

Parameters:

Name Type Description Default
max_runtime float

SCIP time limit per instance in seconds. Must be positive. Defaults to 3600 seconds.

3600
quiet bool

Suppress the SCIP solver output.

True

Returns:

Type Description
list[bool]

Feasibility mask over the instances as they were before filtering, in that order: True where the instance was kept, False where it was removed.

Raises:

Type Description
ValueError

If max_runtime is not positive.