danielpetrica / dochia-cycle-model
A small, calibrated, on-device statistical model for menstrual-cycle forecasting (PHP reference).
Package info
github.com/danielpetrica/dochia-cycle-model
Language:Python
pkg:composer/danielpetrica/dochia-cycle-model
Requires
- php: >=8.2
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A small, auditable, on-device statistical model for menstrual-cycle forecasting: a calibrated next-onset distribution, a daily onset hazard, a corrected ovulation-variance identity, and explicit abstention. Reference implementations in PHP, Python, R, Julia and JavaScript; no neural network, no server, no network calls.
Not a medical device. Not contraception. Not a diagnosis. Forecasts are estimates with uncertainty and must not be used for medical decisions.
Repository: danielpetrica/dochia-cycle-model
Why this exists
Most cycle apps show a single date with false precision. This model shows:
- a range and a probability, not just a day;
- daily probabilities ("chance today", "chance within 3 days") and a late-period signal;
- abstention when the history is too variable or incomplete;
- a corrected ovulation/fertile-window distribution (the common
SD(cycle)² + SD(luteal)²error is fixed by subtracting the luteal variance:SD(O)² = SD(CL)² − SD(luteal)²).
It is deliberately tiny: ~50–200 effective parameters, <10 KB of parameters, milliseconds of pure-PHP inference. It exists to make uncertainty honest, not to promise a date.
What it is not
- Not an LLM or an "AI" model.
- Not a medical device, contraceptive, fertility diagnostic, or clinical tool.
- Not validated on the general population. Validation is on a selected charted cohort plus synthetic stress tests; see docs/VALIDATION.md.
- It does not predict ovulation or phases from wearables. Phase inference (BBT/LH/mucus) is future work.
Quick start (PHP)
git clone https://github.com/danielpetrica/dochia-cycle-model.git
cd dochia-cycle-model
Requirements: PHP >= 8.2, no extensions or Composer dependencies required (Composer optional).
<?php require 'src/Erf.php'; require 'src/LognormalMixture.php'; require 'src/CalibratedPredictive.php'; require 'src/ModelParams.php'; require 'src/ModelParamsFactory.php'; require 'src/MicroModel.php'; use Dochia\MicroModel; use Dochia\ModelParamsFactory; $model = new MicroModel(ModelParamsFactory::literature()); $forecast = $model->forecast([29, 28, 30, 27, 31, 29], currentDay: 12); echo $forecast['quantiles']['0.5']; // median cycle length (days) echo $forecast['hazard']['p_within_3']; // P(onset within 3 days | not yet) echo $forecast['ovulation']['sd_day']; // corrected ovulation-day SD
Or install via Packagist:
composer require danielpetrica/dochia-cycle-model
For local development in this repository:
composer dump-autoload
CLI demo:
php bin/forecast.php 29 28 30 27 31 29 php bin/forecast.php --json 29 28 30 27 31 29
Cross-language implementations
The same equations are implemented and parity-tested in five languages:
| Language | File | Runtime |
|---|---|---|
| PHP | src/ |
PHP 8.2+ |
| Python | python/dochia.py |
Python 3.11+ |
| R | r/dochia.R |
R 4.x (base) |
| Julia | julia/dochia.jl |
Julia 1.10+ (stdlib) |
| JavaScript | javascript/dochia.mjs |
Node 22+ |
All five agree on the shared 12-case forecast fixture and the synthetic walk-forward accuracy fixture (319 assertions each; MAE 3.8474 d, cov80 83.26%, cov95 93.21%, abstain 34.39% on that synthetic evaluation set). Tolerances and commands: docs/PARITY.md.
python3 python/parity_test.py php tests/php/parity_test.php Rscript r/parity_test.R julia julia/parity_test.jl node javascript/parity_test.mjs
Quick start (Python reference)
The Python implementation mirrors the PHP reference and is used to generate the golden and parity fixtures. It needs only the standard library.
cd python python3 - <<'PY' import dochia as mm params = mm.ModelParams.from_json('../params/dochia-v1.json') f = mm.forecast([29, 28, 30, 27, 31, 29], current_day=12, params=params) print(f['quantiles']['0.5'], f['hazard']['p_within_3']) PY
How accurate is it?
On a held-out real validation split (283 women / 858 forecasts from the public Creighton cycle-length corpus; even-ID women only), the ship-safe dochia-v1 model:
| Metric | Result |
|---|---|
| Average miss of the central guess | 2.67 days |
| Half of guesses within | 1.48 days |
| Guess within 1 / 2 / 3 / 7 days | 35% / 62% / 75% / 94% |
| Nominal 80% interval actually contained the date | 82.0% |
| Nominal 95% interval actually contained the date | 94.4% |
That is not an accuracy improvement over a standard normal hierarchical model (2.69-day average miss, 76.9%/90.3% coverage); the improvement is that the stated uncertainty is now honest. On deliberately hard synthetic cohorts (PCOS-like, postpartum) the model abstains frequently rather than pretending. Full methods, tables and caveats: docs/VALIDATION.md.
Repository layout
src/ PHP reference (namespace Dochia)
python/ Python reference, simulation, fixture/params generators
r/ base-R reference + parity test
julia/ Julia reference + parity test
javascript/ JavaScript (Node) reference + parity test
params/ dochia-v1.json (ship-safe published priors only)
tests/php/ standalone PHP test runner, golden vectors, parity test
tests/fixtures/ golden + cross-language parity/evaluation fixtures
testing/ synthetic test-data generators + 16 tests (stdlib only)
docs/ model card, validation, parity, test-data, integration
huggingface/ ready-to-push Hugging Face model/dataset cards
research/ optional reproduction scripts for the external validation
bin/forecast.php PHP CLI
examples/ sample input
No real cycle data is bundled. The research validation scripts fetch the public Creighton corpus directly from its source under its own licence (CC BY-NC 3.0); do not redistribute that data.
Test it
PHP (no PHPUnit required):
php tests/php/run_tests.php php tests/php/parity_test.php
Python + synthetic data:
python3 python/parity_test.py
cd testing/
python3 build_suites.py
python3 evaluate_suites.py
python3 test_generators.py -v
R / Julia / JavaScript parity:
Rscript r/parity_test.R julia julia/parity_test.jl node javascript/parity_test.mjs
Documentation
- Model card — intended use, factors, metrics, ethical considerations.
- Validation — data, methods, results, limits.
- Cross-language parity — five implementations, shared fixtures, tolerances, commands.
- Test data — synthetic regimes generated from published summary statistics, leakage rules, custom real data.
- Integration — adding the model to a local-first app.
- NOTICE — third-party references and data licensing.
Licence
Code: MIT (see LICENSE). Documentation, model card and synthetic test data: CC BY 4.0. No real cycle data is included or redistributed.
Citation
If you use this model or test-data methodology, cite:
Petrica, D. (2026). Dochia: a calibrated on-device statistical model for menstrual-cycle forecasting (v0.1.1). Zenodo. https://doi.org/10.5281/zenodo.23956336
Machine-readable citation: CITATION.cff. Please also cite the primary sources listed in docs/VALIDATION.md and NOTICE.
Contributing
Issues and pull requests are welcome. Please read CONTRIBUTING.md; in particular: no medical/contraceptive claims, no real data without permission, tests before "fixed".
Disclaimer
This is research/educational software. It is not a medical device and must not be used for contraception, diagnosis, or treatment decisions. If a period is late and pregnancy is possible, test; for persistent irregularity or concerning symptoms, consult a qualified clinician.
