Open-Source Library
Python
Operations Research
Linear Programming
Workforce Management
Scheduling
Optimization

Pyworkforce

A Python library for workforce management optimization: Erlang-C queue modeling, LP-based shift scheduling, and multi-skill rostering — built to replace spreadsheet planning with mathematical guarantees.

September 1, 2021·4 min read
Pyworkforce

The Problem It Solves

Operations teams at call centers and service organizations make staffing decisions every week that directly determine service quality and labor costs. The question is simple to state: how many agents do you need, on which shifts, with what skills?

In practice, most teams answer it with spreadsheets and intuition. The spreadsheet tells them last week's headcount. The intuition adjusts for seasonality. The result is a schedule that over-staffs some hours and under-staffs others, and a planning process that can't be audited, repeated, or improved systematically.

The mathematics to do this rigorously has existed for decades — Erlang C for queue modeling, linear programming for shift scheduling, integer programming for rostering. What was missing was a Python library that composed these into a usable, production-ready toolkit.

What I Built

pyworkforce implements the full workforce management pipeline in three composable modules:

Queue modeling answers: given expected call volume and handling time, how many agents are needed to meet a service level target? The Erlang C formula is the industry standard; the library wraps it with production-ready defaults including shrinkage (agent non-productive time) and occupancy caps.

Shift scheduling answers: given per-hour staffing requirements, which shifts should run and how many agents on each? This is formulated as a linear programming problem — minimize the gap between required and assigned headcount, given a set of available shift templates.

Rostering answers: given the required shift assignments, which specific agent works which shift sequence? This is a combinatorial problem with hard constraints — maximum consecutive days, minimum rest between shifts, skill requirements, days-off patterns.

Each module can be used independently or chained into a full pipeline.

Why Mathematical Guarantees Matter

The alternative to LP-based scheduling is heuristics — rule-of-thumb shift patterns, round-number headcounts, historical copy-paste. Heuristics are fast to compute and easy to explain, but they produce sub-optimal solutions and, critically, they can't tell you how sub-optimal.

A linear program, when it terminates with status = "Optimal", guarantees that no feasible solution exists that performs better on the objective. For a staffing problem, that means: you cannot staff this demand with fewer agents while meeting the service level target. That kind of statement is not possible with a spreadsheet.

The rostering constraints work the same way. If minimum rest between shifts is 11 hours and a valid roster exists, the solver finds it. If no valid roster exists under the given constraints, it returns "Infeasible" — which is also valuable information.

The Pipeline in Practice

The three modules compose naturally. A call center running this end-to-end would:

python
from pyworkforce.queuing import ErlangC
from pyworkforce.scheduling import MinAbsDifference
from pyworkforce.rostering import MinHoursRoster

# Step 1: how many agents per hour to hit the service level?
erlang = ErlangC(
    transactions=120,   # calls arriving in the interval
    interval=60,        # interval length (minutes)
    aht=4,              # average handling time (minutes)
    asa=20/60,          # target answer speed (minutes)
    shrinkage=0.30,     # non-productive agent time
)
positions_per_hour = erlang.required_positions(
    service_level=0.80,
    max_occupancy=0.85,
)["positions"]

# Step 2: which shifts cover that demand, and how many of each?
scheduler = MinAbsDifference(
    num_days=7,
    periods=24,
    shifts_coverage={
        "Morning":   [0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0],
        "Afternoon": [0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0],
        "Night":     [1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1],
    },
    required_resources=[[positions_per_hour] * 24] * 7,
    max_period_concurrency=positions_per_hour,
    max_shift_concurrency=positions_per_hour,
)
shift_plan = scheduler.solve()

# Step 3: assign individual agents to shift sequences
roster = MinHoursRoster(
    num_days=7,
    resources=[f"agent_{i}" for i in range(25)],
    shifts=["Morning", "Afternoon", "Night"],
    shifts_hours=[8, 8, 8],
    min_working_hours=40,
    max_resting=2,
    required_resources={
        "Morning":   [positions_per_hour] * 7,
        "Afternoon": [positions_per_hour] * 7,
        "Night":     [positions_per_hour] * 7,
    },
)
weekly_roster = roster.solve()

From call volume to individual weekly schedule — with mathematical guarantees at each step.

Key Design Decisions

The shrinkage parameter in the Erlang C module deserves attention. Raw Erlang C gives you the number of agents needed assuming they are available 100% of the time. Real agents take breaks, attend training, handle admin tasks, and call in sick. Ignoring shrinkage typically underestimates headcount by 25–35%, which is exactly why spreadsheet-based plans chronically under-staff.

The scheduling objective — MinAbsDifference — minimizes the absolute difference between required and scheduled headcount across all periods, rather than just minimizing total cost. This produces balanced coverage rather than solutions that sacrifice certain hours to hit a numeric target.

The rostering module models constraints as hard constraints in the integer program. There is no penalty weighting, no soft constraint relaxation, no post-processing. A schedule either respects the constraints or the solver returns infeasible.

Where It Stands

pyworkforce has been actively developed since 2021 and is currently at v0.5.4. It is published on PyPI, documented at pyworkforce.rodrigo-arenas.com, and built on top of Google OR-Tools.

It was built from direct work on staffing optimization problems for operations teams — the kind of engagement where a spreadsheet answer and a mathematical answer diverge by enough to matter.


About the author

Rodrigo Arenas is a software architect and machine learning engineer in Medellín, Colombia. He builds products, platforms, and open-source software for AI, Data Engineering, and Machine Learning — creator of Ciaren, sklearn-genetic-opt, and PyWorkforce.



Rodrigo Arenas — ML Engineer & Open Source Maintainer

Working on something similar? I can help you take it to production.

Building an AI or data platform?

Products, platforms, and open-source software for AI, Data Engineering, and Machine Learning.

View Projects
Vision

Building an ecosystem of products and open-source software for AI, Data Engineering, and Machine Learning.

Projects
sklearn-genetic-optPyWorkforceCiaren

Iterixs (Currently building)

© 2026 Rodrigo Arenas