Project scheduling and earned value control for Python: CPM, PERT with Monte Carlo schedule risk, minimum-cost crashing, EVM and earned schedule, validated against published reference values.
Project description
pmcontrols
Project scheduling and earned value control for Python.
CPM, PERT with Monte Carlo schedule risk, minimum-cost crashing, and EVM/earned-schedule control — computed from the defining formulations, never from spreadsheet conventions, and checked against published reference values in the test suite.
Companion to lotsampling (acceptance sampling): lotsampling judges the lot, pmcontrols keeps the project honest.
Motivation
Every project office computes CPI and SPI; almost all of it happens in
Excel. R and commercial tools (Primavera, Deltek, @RISK) cover schedule
risk and earned value; in Python the landscape is a 4 KB CPM toy and an
abandoned ERP add-on. There is no maintained library a cost engineer or
a project-controls researcher can pip install to get:
- the critical path with full ES/EF/LS/LF/slack accounting
- PERT three-point analysis plus what the textbook procedure cannot give you: a Monte Carlo completion distribution and per-activity criticality indices
- crashing as optimization — the cheapest set of compressions meeting a deadline, solved as the classical time/cost trade-off LP rather than by manual marginal-cost inspection
- EVM with earned schedule — the full indicator set (CV, SV, CPI, SPI, the EAC family, TCPI, VAC) plus Lipke's time-based ES, SPI(t) and duration forecast IEAC(t), which plain EVM gets wrong late in projects
Quickstart
import pmcontrols as pm
activities = [
{"id": "A", "predecessors": [], "duration": 2},
{"id": "B", "predecessors": [], "duration": 3},
{"id": "C", "predecessors": ["A"], "duration": 2},
{"id": "D", "predecessors": ["B"], "duration": 4},
{"id": "E", "predecessors": ["C"], "duration": 4},
{"id": "F", "predecessors": ["C"], "duration": 3},
{"id": "G", "predecessors": ["D", "E"], "duration": 5},
{"id": "H", "predecessors": ["F", "G"], "duration": 2},
]
r = pm.cpm(activities)
r.stats["project_duration"] # 15.0
r.meta["critical_activities"] # ['A', 'C', 'E', 'G', 'H']
r.table # tidy ES/EF/LS/LF/slack DataFrame
# Cheapest way to finish in 13 periods (linear program):
r = pm.crash(crash_activities, target=13)
r.stats["total_crash_cost"] # optimal, not greedy
# Plan once, freeze, control forever:
pm.plan(periods, pv_curve).save("pmb.json") # commit this to git
r = pm.evm("pmb.json", ev=30_000, ac=35_000, at=4)
r.ok # False — CPI and SPI(t) below threshold
print(r.summary()) # audit text with plain-language verdict
r.stats["ieac_t"] # earned-schedule duration forecast
r.ok is the automation primitive: a weekly cron job can evaluate the
latest actuals against the frozen PMB and exit nonzero the moment cost
or schedule efficiency breaches a threshold.
Validation philosophy
Every release must reproduce, in tests/validation_cases.json (each
case ships with its full derivation):
- the General Foundry reference network — complete ES/EF/LS/LF/slack table, 15-period critical path, and optimal crash costs to 14 and 13 periods,
- hand-derived EVM/earned-schedule cases covering the full indicator set and the ES interpolation formula,
- identity and property checks (slack = LS−ES = LF−EF; SPI(t) = ES/AT; crash cost monotone in target; Monte Carlo means converging to analytic values),
- (before 0.1) published PMI/Lipke earned-schedule case studies, verified privately where copyright prevents redistribution.
Status
0.1.0 — first public release. The API surface is small on purpose; the
Result/PMB contract is frozen and append-only from this version on.
Roadmap
| Version | Scope |
|---|---|
| 0.2 | Published PMI/Lipke earned-schedule case-study validation; Gantt and OC plotting (separate from the statistics) |
| 0.3 | Resource leveling and constrained scheduling; schedule risk drivers (correlation, risk events); EVM from time-phased ledgers (DataFrame in, indicators out) |
| 0.4 | Critical chain buffers; probabilistic crashing (crash under uncertainty); ES forecasting variants (performance factors); JOSS paper |
Out of scope: Gantt-chart project editors (use dedicated PM tools), process control, acceptance sampling (see lotsampling), generic LP modeling (use scipy/pyomo directly).
License
MIT. Written and maintained by Atakan Arikan, MSc Student at Tsinghua University and Politecnico di Milano.
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 pmcontrols-0.1.0.tar.gz.
File metadata
- Download URL: pmcontrols-0.1.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86d539702fed7420bfe24123a91de7051d9830109469651b489f5d4765c65fb0
|
|
| MD5 |
e26991615df19412bb3dfd3bd8929849
|
|
| BLAKE2b-256 |
c38664198a9fb0f74f09bc6764db511e45d33f6d1ede5916ffb398da998213e2
|
Provenance
The following attestation bundles were made for pmcontrols-0.1.0.tar.gz:
Publisher:
release.yml on arikanatakan/pmcontrols
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pmcontrols-0.1.0.tar.gz -
Subject digest:
86d539702fed7420bfe24123a91de7051d9830109469651b489f5d4765c65fb0 - Sigstore transparency entry: 1819669568
- Sigstore integration time:
-
Permalink:
arikanatakan/pmcontrols@777b217eb243ba8a6eedb17e841beabf6d36ffba -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arikanatakan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@777b217eb243ba8a6eedb17e841beabf6d36ffba -
Trigger Event:
push
-
Statement type:
File details
Details for the file pmcontrols-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pmcontrols-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f61302da7e6a697f4b84991e097db6a571beab4cd1f8dd091bda634afb6064fa
|
|
| MD5 |
cdb333b11fa8b05bd34b787ebdd69049
|
|
| BLAKE2b-256 |
79e752c90506c694e497cb8bd50eb139e53c8544ef74afdd7ac281abcc9d85d9
|
Provenance
The following attestation bundles were made for pmcontrols-0.1.0-py3-none-any.whl:
Publisher:
release.yml on arikanatakan/pmcontrols
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pmcontrols-0.1.0-py3-none-any.whl -
Subject digest:
f61302da7e6a697f4b84991e097db6a571beab4cd1f8dd091bda634afb6064fa - Sigstore transparency entry: 1819669596
- Sigstore integration time:
-
Permalink:
arikanatakan/pmcontrols@777b217eb243ba8a6eedb17e841beabf6d36ffba -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arikanatakan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@777b217eb243ba8a6eedb17e841beabf6d36ffba -
Trigger Event:
push
-
Statement type: