Critical-path method (CPM) and Monte Carlo schedule-risk engine for project management
Project description
trueppm-scheduler
Project-schedule math as a library — critical path and delivery-risk forecasting, without a 500 MB desktop app or a SaaS subscription.
Answer the two questions every plan has to answer:
- "What's the earliest this can finish, and which tasks can't slip?" — a full forward/backward critical-path pass computes early/late dates, total and free float, and flags the tasks on the critical path.
- "How confident are we in that date?" — Monte Carlo simulation turns three-point estimates into a P50/P80/P95 forecast, so you can commit to a date you'll actually hit instead of the best-case one.
It's pure Python with just networkx and numpy underneath — no Django, no web server, no GUI. Drop it into a backend, a data pipeline, a Jupyter notebook, or a CLI and get the same engine that powers the TruePPM platform.
Why reach for this
- Real scheduling semantics, not a toy. All four dependency types (finish-to-start, start-to-start, finish-to-finish, start-to-finish) with calendar-aware lag — most lightweight schedulers only do finish-to-start and count raw calendar days, which silently overruns any plan with a weekend in it.
- Working-time aware. A built-in working-day calendar skips weekends and honors holiday exceptions, so durations resolve to real delivery dates.
- Risk forecasting built in. PERT-Beta Monte Carlo, numpy-vectorized at ~10k runs/sec — the difference between "due March 3" and "70% likely by March 3, 95% by March 14."
- Fails loud on bad input. Cycle detection that names the offending task IDs, plus up-front validation of durations, lag, and project span — no silent wrong answers, no spinning on a degenerate graph.
- Embeds anywhere. Two dependencies, no framework. Serialize a plan to JSON, schedule it, and read back structured results.
Features
- Forward/backward CPM pass with all four dependency types (FS, SS, FF, SF), total/free float, and critical-path flagging
- Calendar-aware working-day arithmetic (weekend skip + holiday exceptions)
- Monte Carlo schedule-risk simulation via PERT-Beta distributions (numpy-vectorized, ~10k runs/sec) → P50/P80/P95 completion dates
- JSON round-tripping for plans (
Project.from_json()/Project.to_json()) - CLI:
trueppm-scheduler schedule/trueppm-scheduler monte-carlo
Install
pip install trueppm-scheduler
Quick start
from datetime import date, timedelta
from trueppm_scheduler import schedule, Calendar, Project, Task, Dependency, DependencyType
calendar = Calendar() # Mon–Fri, no holidays (whole-day scheduling)
task_a = Task(id="t-1", name="Design", duration=timedelta(days=5))
task_b = Task(id="t-2", name="Build", duration=timedelta(days=10))
dep = Dependency(predecessor_id="t-1", successor_id="t-2", dep_type=DependencyType.FS)
project = Project(
id="p-1",
name="My Project",
start_date=date(2026, 1, 5),
tasks=[task_a, task_b],
dependencies=[dep],
calendar=calendar,
)
result = schedule(project)
build = next(t for t in result.tasks if t.id == "t-2")
print(build.early_finish) # 2026-01-23 (15 working days from 2026-01-05, across two weekends)
Scheduling granularity. The engine schedules in whole working-day units.
Calendar.hours_per_dayandCalendar.timezoneround-trip through serialization for API parity but are not consumed by the CPM or Monte Carlo passes — they do not change any computed date. Sub-day scheduling is a future change.
See the full documentation for CPM output fields, Monte Carlo usage, and CLI reference.
Errors and input limits
Every exception the engine raises subclasses ValueError, so one
except ValueError catches them all — but each is individually catchable:
| Exception | Raised when |
|---|---|
CyclicDependencyError |
The dependency graph contains a cycle. .cycle lists the task IDs forming it. |
SimulationCapExceeded |
monte_carlo(runs=…) exceeds max_runs, or the project has more tasks than max_tasks. |
InvalidScheduleInput |
The input is structurally valid but out of range (see limits below). |
The engine walks the working calendar one day at a time, so it validates input up front rather than spinning on a degenerate project:
- Calendar —
working_daysmust set at least one weekday bit (Mon–Sun); a calendar whoseexceptionsblanket the entire search window is rejected too. - Duration — each task duration must be between
0andMAX_DURATION_DAYS(36_525, ~100 years). Negative durations are rejected. - Lag — each dependency lag must be within
±MAX_LAG_DAYS(36_525). - Project span — the cumulative span (every task's worst-case duration plus
the magnitude of every lag) must stay under
MAX_PROJECT_SPAN_DAYS(366_000, ~1000 years), regardless of task count. - Monte Carlo —
runsmust be>= 1.
Project.from_json() also rejects the non-standard JSON literals NaN,
Infinity, and -Infinity.
from trueppm_scheduler import schedule, InvalidScheduleInput
try:
result = schedule(project)
except InvalidScheduleInput as e:
print("Bad input:", e) # "Task 't-1' duration exceeds the maximum of 36525 days (got …)."
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file trueppm_scheduler-0.2.0a1.tar.gz.
File metadata
- Download URL: trueppm_scheduler-0.2.0a1.tar.gz
- Upload date:
- Size: 49.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b341bf3a6abddc3697ba0873e13d639e0c947709cb7e4c123fb8d562b4b3060
|
|
| MD5 |
c4214111567215aee5ea37236a35b2a6
|
|
| BLAKE2b-256 |
d4274ff5c0eb556f6d3c0616fa275a76e9276a448b5d7ed428a1cee14292c393
|
File details
Details for the file trueppm_scheduler-0.2.0a1-py3-none-any.whl.
File metadata
- Download URL: trueppm_scheduler-0.2.0a1-py3-none-any.whl
- Upload date:
- Size: 27.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fbd6d06890d9a3130eb0a007376161522bcd08cdda5f0c47379fb016bbda040
|
|
| MD5 |
0f54d0f0fffbfa61c335f78fd1638727
|
|
| BLAKE2b-256 |
10e8e7243d19e0616956a337a373984dfca632325f22afc9ec368326273d8ee1
|