tscp 0.1.0.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tscp-0.1.0.dev0/.gitignore +51 -0
- tscp-0.1.0.dev0/CHANGELOG.md +72 -0
- tscp-0.1.0.dev0/LICENSE +21 -0
- tscp-0.1.0.dev0/PKG-INFO +130 -0
- tscp-0.1.0.dev0/README.md +84 -0
- tscp-0.1.0.dev0/docs/source/api.md +91 -0
- tscp-0.1.0.dev0/docs/source/concepts.md +81 -0
- tscp-0.1.0.dev0/docs/source/conf.py +46 -0
- tscp-0.1.0.dev0/docs/source/index.md +28 -0
- tscp-0.1.0.dev0/docs/source/tutorials/component.md +46 -0
- tscp-0.1.0.dev0/docs/source/tutorials/compose.md +35 -0
- tscp-0.1.0.dev0/docs/source/tutorials/learned.md +26 -0
- tscp-0.1.0.dev0/docs/source/tutorials/plugin.md +40 -0
- tscp-0.1.0.dev0/docs/source/tutorials/production.md +27 -0
- tscp-0.1.0.dev0/examples/compose.py +42 -0
- tscp-0.1.0.dev0/examples/configs/compose.yaml +18 -0
- tscp-0.1.0.dev0/examples/configs/custom_component.yaml +6 -0
- tscp-0.1.0.dev0/examples/configs/learned_score.yaml +8 -0
- tscp-0.1.0.dev0/examples/configs/production.yaml +12 -0
- tscp-0.1.0.dev0/examples/configs/quickstart.yaml +12 -0
- tscp-0.1.0.dev0/examples/custom_component.py +94 -0
- tscp-0.1.0.dev0/examples/learned_score.py +118 -0
- tscp-0.1.0.dev0/examples/plugin/pyproject.toml +17 -0
- tscp-0.1.0.dev0/examples/plugin/spec.yaml +5 -0
- tscp-0.1.0.dev0/examples/plugin/src/tscp_example_plugin/__init__.py +66 -0
- tscp-0.1.0.dev0/examples/production.py +91 -0
- tscp-0.1.0.dev0/examples/quickstart.py +49 -0
- tscp-0.1.0.dev0/pyproject.toml +130 -0
- tscp-0.1.0.dev0/src/tscp/__init__.py +45 -0
- tscp-0.1.0.dev0/src/tscp/data/__init__.py +19 -0
- tscp-0.1.0.dev0/src/tscp/data/archive.py +488 -0
- tscp-0.1.0.dev0/src/tscp/data/availability.py +55 -0
- tscp-0.1.0.dev0/src/tscp/data/io.py +109 -0
- tscp-0.1.0.dev0/src/tscp/data/splits.py +169 -0
- tscp-0.1.0.dev0/src/tscp/datasets/__init__.py +6 -0
- tscp-0.1.0.dev0/src/tscp/datasets/synthetic.py +177 -0
- tscp-0.1.0.dev0/src/tscp/features/__init__.py +22 -0
- tscp-0.1.0.dev0/src/tscp/features/base.py +64 -0
- tscp-0.1.0.dev0/src/tscp/features/calendar.py +105 -0
- tscp-0.1.0.dev0/src/tscp/features/combine.py +69 -0
- tscp-0.1.0.dev0/src/tscp/features/context.py +42 -0
- tscp-0.1.0.dev0/src/tscp/features/history.py +58 -0
- tscp-0.1.0.dev0/src/tscp/features/phase.py +41 -0
- tscp-0.1.0.dev0/src/tscp/imports.py +32 -0
- tscp-0.1.0.dev0/src/tscp/methods/__init__.py +48 -0
- tscp-0.1.0.dev0/src/tscp/methods/aci.py +44 -0
- tscp-0.1.0.dev0/src/tscp/methods/base.py +228 -0
- tscp-0.1.0.dev0/src/tscp/methods/compose.py +90 -0
- tscp-0.1.0.dev0/src/tscp/methods/engine.py +128 -0
- tscp-0.1.0.dev0/src/tscp/methods/io.py +106 -0
- tscp-0.1.0.dev0/src/tscp/methods/nexcp.py +42 -0
- tscp-0.1.0.dev0/src/tscp/methods/split.py +30 -0
- tscp-0.1.0.dev0/src/tscp/metrics/__init__.py +30 -0
- tscp-0.1.0.dev0/src/tscp/metrics/conditional.py +66 -0
- tscp-0.1.0.dev0/src/tscp/metrics/interval.py +75 -0
- tscp-0.1.0.dev0/src/tscp/metrics/report.py +135 -0
- tscp-0.1.0.dev0/src/tscp/nn/__init__.py +13 -0
- tscp-0.1.0.dev0/src/tscp/nn/data.py +85 -0
- tscp-0.1.0.dev0/src/tscp/nn/losses.py +63 -0
- tscp-0.1.0.dev0/src/tscp/nn/training.py +125 -0
- tscp-0.1.0.dev0/src/tscp/online/__init__.py +6 -0
- tscp-0.1.0.dev0/src/tscp/online/aci.py +53 -0
- tscp-0.1.0.dev0/src/tscp/online/base.py +67 -0
- tscp-0.1.0.dev0/src/tscp/py.typed +0 -0
- tscp-0.1.0.dev0/src/tscp/quantile/__init__.py +16 -0
- tscp-0.1.0.dev0/src/tscp/quantile/base.py +73 -0
- tscp-0.1.0.dev0/src/tscp/quantile/distribution.py +170 -0
- tscp-0.1.0.dev0/src/tscp/quantile/weighted.py +103 -0
- tscp-0.1.0.dev0/src/tscp/regions/__init__.py +15 -0
- tscp-0.1.0.dev0/src/tscp/regions/base.py +139 -0
- tscp-0.1.0.dev0/src/tscp/regions/box.py +82 -0
- tscp-0.1.0.dev0/src/tscp/regions/interval.py +62 -0
- tscp-0.1.0.dev0/src/tscp/registry.py +179 -0
- tscp-0.1.0.dev0/src/tscp/scores/__init__.py +9 -0
- tscp-0.1.0.dev0/src/tscp/scores/absolute.py +22 -0
- tscp-0.1.0.dev0/src/tscp/scores/base.py +87 -0
- tscp-0.1.0.dev0/src/tscp/scores/cqr.py +71 -0
- tscp-0.1.0.dev0/src/tscp/scores/normalized.py +74 -0
- tscp-0.1.0.dev0/src/tscp/scores/signed.py +46 -0
- tscp-0.1.0.dev0/src/tscp/sources/__init__.py +7 -0
- tscp-0.1.0.dev0/src/tscp/sources/base.py +32 -0
- tscp-0.1.0.dev0/src/tscp/sources/expanding.py +25 -0
- tscp-0.1.0.dev0/src/tscp/sources/holdout.py +18 -0
- tscp-0.1.0.dev0/src/tscp/testing/__init__.py +40 -0
- tscp-0.1.0.dev0/src/tscp/testing/archives.py +80 -0
- tscp-0.1.0.dev0/src/tscp/testing/checks.py +832 -0
- tscp-0.1.0.dev0/src/tscp/testing/components.py +28 -0
- tscp-0.1.0.dev0/src/tscp/typing.py +24 -0
- tscp-0.1.0.dev0/src/tscp/utils/__init__.py +14 -0
- tscp-0.1.0.dev0/src/tscp/utils/component.py +76 -0
- tscp-0.1.0.dev0/src/tscp/utils/config.py +135 -0
- tscp-0.1.0.dev0/src/tscp/utils/hashing.py +35 -0
- tscp-0.1.0.dev0/src/tscp/weights/__init__.py +7 -0
- tscp-0.1.0.dev0/src/tscp/weights/base.py +22 -0
- tscp-0.1.0.dev0/src/tscp/weights/exponential.py +37 -0
- tscp-0.1.0.dev0/src/tscp/weights/uniform.py +23 -0
- tscp-0.1.0.dev0/tests/conftest.py +31 -0
- tscp-0.1.0.dev0/tests/data/test_archive.py +176 -0
- tscp-0.1.0.dev0/tests/data/test_from_forecasts.py +61 -0
- tscp-0.1.0.dev0/tests/data/test_io.py +131 -0
- tscp-0.1.0.dev0/tests/data/test_splits.py +95 -0
- tscp-0.1.0.dev0/tests/datasets/test_synthetic.py +46 -0
- tscp-0.1.0.dev0/tests/features/test_features.py +113 -0
- tscp-0.1.0.dev0/tests/fixtures/golden_regions.npz +0 -0
- tscp-0.1.0.dev0/tests/methods/test_compose.py +75 -0
- tscp-0.1.0.dev0/tests/methods/test_engine.py +76 -0
- tscp-0.1.0.dev0/tests/methods/test_golden.py +102 -0
- tscp-0.1.0.dev0/tests/methods/test_io.py +99 -0
- tscp-0.1.0.dev0/tests/methods/test_presets.py +104 -0
- tscp-0.1.0.dev0/tests/methods/test_release.py +48 -0
- tscp-0.1.0.dev0/tests/methods/test_scale.py +61 -0
- tscp-0.1.0.dev0/tests/methods/test_split.py +57 -0
- tscp-0.1.0.dev0/tests/metrics/test_conditional.py +34 -0
- tscp-0.1.0.dev0/tests/metrics/test_interval.py +54 -0
- tscp-0.1.0.dev0/tests/metrics/test_report.py +66 -0
- tscp-0.1.0.dev0/tests/nn/test_nn.py +152 -0
- tscp-0.1.0.dev0/tests/online/test_aci.py +60 -0
- tscp-0.1.0.dev0/tests/quantile/test_distribution.py +68 -0
- tscp-0.1.0.dev0/tests/quantile/test_weighted.py +117 -0
- tscp-0.1.0.dev0/tests/regions/test_contract.py +93 -0
- tscp-0.1.0.dev0/tests/scores/test_scores.py +155 -0
- tscp-0.1.0.dev0/tests/test_examples.py +35 -0
- tscp-0.1.0.dev0/tests/test_imports.py +34 -0
- tscp-0.1.0.dev0/tests/test_plugins.py +84 -0
- tscp-0.1.0.dev0/tests/test_registry.py +42 -0
- tscp-0.1.0.dev0/tests/testing/test_checks.py +230 -0
- tscp-0.1.0.dev0/tests/utils/test_config.py +79 -0
- tscp-0.1.0.dev0/tests/weights/test_exponential.py +43 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.venv/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.coverage
|
|
11
|
+
.coverage.*
|
|
12
|
+
coverage.xml
|
|
13
|
+
htmlcov/
|
|
14
|
+
|
|
15
|
+
# Extra packages installed with pip --target on remote hosts
|
|
16
|
+
.pydeps/
|
|
17
|
+
|
|
18
|
+
# Private, local-only files: notes, AI-assistant instructions and settings, editor settings
|
|
19
|
+
/ignore/
|
|
20
|
+
AGENTS.md
|
|
21
|
+
CLAUDE.md
|
|
22
|
+
CLAUDE.local.md
|
|
23
|
+
GEMINI.md
|
|
24
|
+
.claude/
|
|
25
|
+
.cursor/
|
|
26
|
+
.cursorrules
|
|
27
|
+
.windsurfrules
|
|
28
|
+
.aider*
|
|
29
|
+
.continue/
|
|
30
|
+
.codex/
|
|
31
|
+
.mcp.json
|
|
32
|
+
.github/copilot-instructions.md
|
|
33
|
+
.vscode/
|
|
34
|
+
.idea/
|
|
35
|
+
*.code-workspace
|
|
36
|
+
|
|
37
|
+
# Sphinx build output and autosummary pages
|
|
38
|
+
docs/build/
|
|
39
|
+
docs/source/generated/
|
|
40
|
+
|
|
41
|
+
# Notebooks
|
|
42
|
+
.ipynb_checkpoints/
|
|
43
|
+
|
|
44
|
+
# Data and run outputs stay out of git. Anchored to the repo root: the package has its own
|
|
45
|
+
# data/ subpackage (src/tscp/data, tests/data), which must stay tracked.
|
|
46
|
+
/data/
|
|
47
|
+
/artifacts/
|
|
48
|
+
/logs/
|
|
49
|
+
|
|
50
|
+
# macOS
|
|
51
|
+
.DS_Store
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to tscp are listed here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Package layout under `src/tscp/`: one subpackage per design axis (`sources`, `scores`,
|
|
12
|
+
`weights`, `quantile`, `online`, `regions`), plus `data`, `methods`, `metrics`,
|
|
13
|
+
`features`, `datasets` and `utils`.
|
|
14
|
+
- `ResidualArchive`, `SplitPlan` with `chronological_split`, the availability rule, and
|
|
15
|
+
self-contained `.npz` archives with fingerprints.
|
|
16
|
+
- Row subsets that keep the archive they came from as their `timeline`, with
|
|
17
|
+
`known_counts` for reading the history before a row causally. `split()` cuts the
|
|
18
|
+
timeline after the split's last row; `head()` returns the first rows.
|
|
19
|
+
- The `ConformalPredictor` composer, and the presets `SplitCP`, `NexCP` and `ACI`,
|
|
20
|
+
registered in `method_map` and built with `get_method`. Trainable components are fitted
|
|
21
|
+
on the `fit` split, with the `validation` split when there is one.
|
|
22
|
+
- Scores that read whole rows (forecasts, outputs, context), with channels and
|
|
23
|
+
per-element miscoverage levels: `AbsoluteScore`, `SignedScore`, `NormalizedScore`
|
|
24
|
+
(forecaster scale or fitted scale) and `CQRScore` (symmetric or per side). Scores over
|
|
25
|
+
the horizon or across series are calibrated on rows whose targets are all observed.
|
|
26
|
+
- Quantile models return a `ScoreDistribution`: thresholds at any number of levels from
|
|
27
|
+
one sort, and the conformal p-value of any score. `WeightedQuantile` returns an
|
|
28
|
+
`EmpiricalDistribution`.
|
|
29
|
+
- Online updates answer queries keyed by their position and observe outcomes as their
|
|
30
|
+
targets arrive, with the realized levels on request: `ACIUpdate`, run by the online
|
|
31
|
+
engine in `tscp.methods.engine`.
|
|
32
|
+
- Other components: `HoldoutSource`, `ExpandingSource`, `UniformWeights`,
|
|
33
|
+
`ExponentialWeights`.
|
|
34
|
+
- Regions: `Interval`, and `Box` over steps, series or both. Every region exposes its
|
|
35
|
+
tensors, so indexing and device moves work for every type.
|
|
36
|
+
- Metrics: coverage, log-width, Winkler score, rolling, worst-window and grouped coverage,
|
|
37
|
+
and `evaluate`, which returns a `Report` with per-step and joint coverage for any
|
|
38
|
+
region type.
|
|
39
|
+
- Synthetic archives: a Gaussian control and a variance shift.
|
|
40
|
+
- Feature maps, computed causally from a row's timeline: `ResidualHistory` (the last
|
|
41
|
+
residuals known at each origin, per step), `Phase` (position in a cycle of base
|
|
42
|
+
steps), `Calendar` (minute to day of year, from timestamps), `ContextColumns` and
|
|
43
|
+
`Concat`.
|
|
44
|
+
- Optional archive `timestamps` (int64 nanoseconds, hashed). Archive files are schema
|
|
45
|
+
version 2; version 1 files still load.
|
|
46
|
+
- `tscp.nn` for learned components: `pinball_loss`, `interval_score`, `RowBatches` of
|
|
47
|
+
archive rows with their features, `train` (seeded, with early stopping on the
|
|
48
|
+
validation split) and `seeded`. `examples/learned_score.py` fits a score whose scale is
|
|
49
|
+
a small network on the context.
|
|
50
|
+
- `context_scale`: a synthetic control whose error scale follows a known covariate.
|
|
51
|
+
- Batch production: `ResidualArchive.from_forecasts` builds an archive from a backtest's
|
|
52
|
+
arrays (NaN truth means unobserved; timestamps as int64 nanoseconds or `datetime64`),
|
|
53
|
+
and `targets()` gives the truth back. `save_method` and `load_method` keep a fitted
|
|
54
|
+
method in one `.npz` file, without pickle; components whose fitted state lives outside
|
|
55
|
+
torch set `refit_on_load`. Fresh components load fitted state whose shape depends on
|
|
56
|
+
the data. `examples/production.py` runs the whole workflow.
|
|
57
|
+
- A `py.typed` marker, for type checkers.
|
|
58
|
+
- A release workflow that publishes to PyPI through trusted publishing when a version
|
|
59
|
+
tag is pushed.
|
|
60
|
+
- `tscp.testing`: checks that a method or component keeps tscp's contracts
|
|
61
|
+
(`check_method`, `check_score`, `check_weights`, `check_quantile_model`,
|
|
62
|
+
`check_online_update`, `check_region`), with `synthetic_archive` and `MaxAbsScore`.
|
|
63
|
+
tscp checks its own presets and components with them.
|
|
64
|
+
- `tscp.registry`: components and presets registered by kind and short name. `register`
|
|
65
|
+
adds new ones; a taken name raises unless `replace=True`. Plugins register through
|
|
66
|
+
the `tscp.plugins` entry-point group and are loaded when a name or a config's class is
|
|
67
|
+
not found. `compose` builds a method from a spec of short names, such as one read from
|
|
68
|
+
YAML; `examples/plugin` is an installable example plugin.
|
|
69
|
+
- Recorded constructor configs (`to_config`, `from_config`) and config hashes.
|
|
70
|
+
- Sphinx docs with a concepts page and tutorials (composing a method, writing a component,
|
|
71
|
+
a learned component and a plugin, batch production) whose code lives in `examples/`
|
|
72
|
+
and runs in the tests; CI that builds them with warnings as errors; pre-commit.
|
tscp-0.1.0.dev0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roberto Neglia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tscp-0.1.0.dev0/PKG-INFO
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tscp
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Summary: Conformal prediction for time series, in PyTorch
|
|
5
|
+
Project-URL: Documentation, https://tscp.readthedocs.io
|
|
6
|
+
Project-URL: Source, https://github.com/RobertoNeglia/tscp
|
|
7
|
+
Project-URL: Issues, https://github.com/RobertoNeglia/tscp/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/RobertoNeglia/tscp/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Roberto Neglia
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: conformal-prediction,forecasting,prediction-intervals,pytorch,time-series,uncertainty-quantification
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: numpy>=1.26
|
|
23
|
+
Requires-Dist: torch>=2.2
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: furo; extra == 'dev'
|
|
26
|
+
Requires-Dist: myst-parser>=3; extra == 'dev'
|
|
27
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
30
|
+
Requires-Dist: pyyaml>=6; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff==0.17.4; extra == 'dev'
|
|
32
|
+
Requires-Dist: sphinx-copybutton; extra == 'dev'
|
|
33
|
+
Requires-Dist: sphinx<9,>=7; extra == 'dev'
|
|
34
|
+
Provides-Extra: doc
|
|
35
|
+
Requires-Dist: furo; extra == 'doc'
|
|
36
|
+
Requires-Dist: myst-parser>=3; extra == 'doc'
|
|
37
|
+
Requires-Dist: sphinx-copybutton; extra == 'doc'
|
|
38
|
+
Requires-Dist: sphinx<9,>=7; extra == 'doc'
|
|
39
|
+
Provides-Extra: examples
|
|
40
|
+
Requires-Dist: pyyaml>=6; extra == 'examples'
|
|
41
|
+
Provides-Extra: test
|
|
42
|
+
Requires-Dist: pytest-cov>=5; extra == 'test'
|
|
43
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
44
|
+
Requires-Dist: pyyaml>=6; extra == 'test'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# tscp
|
|
48
|
+
|
|
49
|
+
[](https://github.com/RobertoNeglia/tscp/actions/workflows/ci.yaml)
|
|
50
|
+
[](https://tscp.readthedocs.io/en/latest/)
|
|
51
|
+
|
|
52
|
+
Conformal prediction for time series, in PyTorch: the published methods behind one interface,
|
|
53
|
+
from one-step intervals to joint regions over a forecast horizon and across series, with one
|
|
54
|
+
evaluation protocol.
|
|
55
|
+
|
|
56
|
+
**Status:** early development. The data contract, the component interfaces, contract
|
|
57
|
+
checks for new components, a registry with plugins, feature maps, helpers for learned
|
|
58
|
+
components and saved methods are in place, with split CP, NexCP and ACI and the absolute,
|
|
59
|
+
signed, normalized and CQR scores; more methods will follow.
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
tscp is in early development; its pre-releases install with
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install --pre tscp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For development, clone the repository and install it with `pip install -e ".[dev]"`.
|
|
70
|
+
Documentation: https://tscp.readthedocs.io
|
|
71
|
+
|
|
72
|
+
## Quickstart
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from tscp.datasets import variance_shift
|
|
76
|
+
from tscp.methods import get_method
|
|
77
|
+
from tscp.metrics import evaluate
|
|
78
|
+
|
|
79
|
+
archive = variance_shift(horizon=1) # a synthetic residual archive
|
|
80
|
+
method = get_method("nexcp", decay=0.99, alpha=0.1)
|
|
81
|
+
region = method.fit(archive).calibrate(archive).predict(archive)
|
|
82
|
+
print(evaluate(region, archive.split("test"), alpha=0.1, window=200))
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`examples/quickstart.py` compares the three methods on two synthetic archives.
|
|
86
|
+
|
|
87
|
+
## Build your own method
|
|
88
|
+
|
|
89
|
+
A method is one component per axis: residual source, score, calibration weights, quantile
|
|
90
|
+
model, online update. `compose` builds one from short names, such as a spec read from YAML:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from tscp.methods import compose
|
|
94
|
+
|
|
95
|
+
method = compose(
|
|
96
|
+
{
|
|
97
|
+
"score": "signed",
|
|
98
|
+
"source": "expanding",
|
|
99
|
+
"weights": {"name": "exponential", "decay": 0.99},
|
|
100
|
+
"update": {"name": "aci", "gamma": 0.005},
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
A new component subclasses its axis's base class, and `tscp.testing` checks that it keeps
|
|
106
|
+
the contracts every method relies on, such as causality and split discipline:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from tscp.testing import check_method
|
|
110
|
+
|
|
111
|
+
check_method(lambda: compose({"score": "signed", "update": "aci"}))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Packages can register components under short names as plugins. The tutorials in `docs/`
|
|
115
|
+
cover composing methods, writing components, learned components, plugins and batch
|
|
116
|
+
production; the code they show is in `examples/` and runs in the tests.
|
|
117
|
+
|
|
118
|
+
## Batch production
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from tscp.data import ResidualArchive
|
|
122
|
+
from tscp.methods import load_method, save_method
|
|
123
|
+
|
|
124
|
+
archive = ResidualArchive.from_forecasts(
|
|
125
|
+
truth, forecasts, sizes={"fit": 0.2, "calibration": 0.3, "test": None}
|
|
126
|
+
)
|
|
127
|
+
save_method(method.fit(archive), "method.npz") # one file, no pickle
|
|
128
|
+
region = load_method("method.npz").calibrate(archive).predict(archive)
|
|
129
|
+
intervals = region.to_target(archive.split("test").forecasts)
|
|
130
|
+
```
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# tscp
|
|
2
|
+
|
|
3
|
+
[](https://github.com/RobertoNeglia/tscp/actions/workflows/ci.yaml)
|
|
4
|
+
[](https://tscp.readthedocs.io/en/latest/)
|
|
5
|
+
|
|
6
|
+
Conformal prediction for time series, in PyTorch: the published methods behind one interface,
|
|
7
|
+
from one-step intervals to joint regions over a forecast horizon and across series, with one
|
|
8
|
+
evaluation protocol.
|
|
9
|
+
|
|
10
|
+
**Status:** early development. The data contract, the component interfaces, contract
|
|
11
|
+
checks for new components, a registry with plugins, feature maps, helpers for learned
|
|
12
|
+
components and saved methods are in place, with split CP, NexCP and ACI and the absolute,
|
|
13
|
+
signed, normalized and CQR scores; more methods will follow.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
tscp is in early development; its pre-releases install with
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install --pre tscp
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For development, clone the repository and install it with `pip install -e ".[dev]"`.
|
|
24
|
+
Documentation: https://tscp.readthedocs.io
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from tscp.datasets import variance_shift
|
|
30
|
+
from tscp.methods import get_method
|
|
31
|
+
from tscp.metrics import evaluate
|
|
32
|
+
|
|
33
|
+
archive = variance_shift(horizon=1) # a synthetic residual archive
|
|
34
|
+
method = get_method("nexcp", decay=0.99, alpha=0.1)
|
|
35
|
+
region = method.fit(archive).calibrate(archive).predict(archive)
|
|
36
|
+
print(evaluate(region, archive.split("test"), alpha=0.1, window=200))
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`examples/quickstart.py` compares the three methods on two synthetic archives.
|
|
40
|
+
|
|
41
|
+
## Build your own method
|
|
42
|
+
|
|
43
|
+
A method is one component per axis: residual source, score, calibration weights, quantile
|
|
44
|
+
model, online update. `compose` builds one from short names, such as a spec read from YAML:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from tscp.methods import compose
|
|
48
|
+
|
|
49
|
+
method = compose(
|
|
50
|
+
{
|
|
51
|
+
"score": "signed",
|
|
52
|
+
"source": "expanding",
|
|
53
|
+
"weights": {"name": "exponential", "decay": 0.99},
|
|
54
|
+
"update": {"name": "aci", "gamma": 0.005},
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
A new component subclasses its axis's base class, and `tscp.testing` checks that it keeps
|
|
60
|
+
the contracts every method relies on, such as causality and split discipline:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from tscp.testing import check_method
|
|
64
|
+
|
|
65
|
+
check_method(lambda: compose({"score": "signed", "update": "aci"}))
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Packages can register components under short names as plugins. The tutorials in `docs/`
|
|
69
|
+
cover composing methods, writing components, learned components, plugins and batch
|
|
70
|
+
production; the code they show is in `examples/` and runs in the tests.
|
|
71
|
+
|
|
72
|
+
## Batch production
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from tscp.data import ResidualArchive
|
|
76
|
+
from tscp.methods import load_method, save_method
|
|
77
|
+
|
|
78
|
+
archive = ResidualArchive.from_forecasts(
|
|
79
|
+
truth, forecasts, sizes={"fit": 0.2, "calibration": 0.3, "test": None}
|
|
80
|
+
)
|
|
81
|
+
save_method(method.fit(archive), "method.npz") # one file, no pickle
|
|
82
|
+
region = load_method("method.npz").calibrate(archive).predict(archive)
|
|
83
|
+
intervals = region.to_target(archive.split("test").forecasts)
|
|
84
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
## Data contract
|
|
4
|
+
|
|
5
|
+
```{eval-rst}
|
|
6
|
+
.. automodule:: tscp.data
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Axis 1: residual sources
|
|
10
|
+
|
|
11
|
+
```{eval-rst}
|
|
12
|
+
.. automodule:: tscp.sources
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Axis 2: scores
|
|
16
|
+
|
|
17
|
+
```{eval-rst}
|
|
18
|
+
.. automodule:: tscp.scores
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Axis 3: calibration weights
|
|
22
|
+
|
|
23
|
+
```{eval-rst}
|
|
24
|
+
.. automodule:: tscp.weights
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Axis 4: quantile models
|
|
28
|
+
|
|
29
|
+
```{eval-rst}
|
|
30
|
+
.. automodule:: tscp.quantile
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Axis 5: online updates
|
|
34
|
+
|
|
35
|
+
```{eval-rst}
|
|
36
|
+
.. automodule:: tscp.online
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Axis 6: regions
|
|
40
|
+
|
|
41
|
+
```{eval-rst}
|
|
42
|
+
.. automodule:: tscp.regions
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Methods
|
|
46
|
+
|
|
47
|
+
```{eval-rst}
|
|
48
|
+
.. automodule:: tscp.methods
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Metrics
|
|
52
|
+
|
|
53
|
+
```{eval-rst}
|
|
54
|
+
.. automodule:: tscp.metrics
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
```{eval-rst}
|
|
60
|
+
.. automodule:: tscp.features
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Registry and plugins
|
|
64
|
+
|
|
65
|
+
```{eval-rst}
|
|
66
|
+
.. automodule:: tscp.registry
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Learned components
|
|
70
|
+
|
|
71
|
+
```{eval-rst}
|
|
72
|
+
.. automodule:: tscp.nn
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Checks
|
|
76
|
+
|
|
77
|
+
```{eval-rst}
|
|
78
|
+
.. automodule:: tscp.testing
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Datasets
|
|
82
|
+
|
|
83
|
+
```{eval-rst}
|
|
84
|
+
.. automodule:: tscp.datasets.synthetic
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Utilities
|
|
88
|
+
|
|
89
|
+
```{eval-rst}
|
|
90
|
+
.. automodule:: tscp.utils
|
|
91
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
tscp is post-hoc: it starts from the forecasts of a model that is already trained, and it
|
|
4
|
+
never trains the forecaster. A conformal method in tscp is one choice per *axis*, run
|
|
5
|
+
through a fixed lifecycle on a *residual archive*.
|
|
6
|
+
|
|
7
|
+
## The residual archive
|
|
8
|
+
|
|
9
|
+
A {class}`~tscp.data.ResidualArchive` holds the residuals of one forecaster on one
|
|
10
|
+
dataset, with what methods need beside them.
|
|
11
|
+
|
|
12
|
+
- **Residuals** are truth minus forecast, in the target's original units, with shape
|
|
13
|
+
`[T, H, N]`: forecast origins, horizon steps and series. tscp never standardizes them.
|
|
14
|
+
- **Origins and steps** count base time steps. The target of origin `s` at step `h` is at
|
|
15
|
+
time `s + h`.
|
|
16
|
+
- **Splits** are named row sets in time order: `fit` and an optional `validation` for
|
|
17
|
+
components that are trained, `calibration` for conformal scores, and `test`. No two
|
|
18
|
+
splits share a target time.
|
|
19
|
+
- **Optional fields:** point forecasts, other forecaster outputs (such as predicted
|
|
20
|
+
quantiles, in target units), context known at each origin, an observed mask for missing
|
|
21
|
+
targets, timestamps, series names and a graph over the series.
|
|
22
|
+
|
|
23
|
+
{meth}`~tscp.data.ResidualArchive.from_forecasts` builds an archive from a backtest's
|
|
24
|
+
arrays, and {func}`~tscp.data.save_archive` writes it to a self-contained `.npz` file,
|
|
25
|
+
checked against a fingerprint when it is read back.
|
|
26
|
+
|
|
27
|
+
## Knowing only the past
|
|
28
|
+
|
|
29
|
+
The residual of origin `s` at step `h` is known once its target time has passed: at origin
|
|
30
|
+
`t` it is usable only if `s + h <= t`. Everything that grows or adapts during the test
|
|
31
|
+
period follows this rule: expanding calibration pools, online updates and features.
|
|
32
|
+
|
|
33
|
+
A row subset, made by `select` or `split`, keeps the archive it came from as its
|
|
34
|
+
`timeline`. Components read the history before a row from there, through
|
|
35
|
+
{meth}`~tscp.data.ResidualArchive.known_counts`. `split` cuts the timeline after the
|
|
36
|
+
split's last row, so a component fitted on the fit split cannot reach anything later.
|
|
37
|
+
{func}`tscp.testing.check_method` checks both properties for any method.
|
|
38
|
+
|
|
39
|
+
## One choice per axis
|
|
40
|
+
|
|
41
|
+
| Axis | Base class | What it decides |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Residual source | {class}`~tscp.sources.Source` | which rows calibrate, and whether test residuals join them once known |
|
|
44
|
+
| Score | {class}`~tscp.scores.Score` | how an error becomes a number, and the region a threshold on it defines |
|
|
45
|
+
| Weights | {class}`~tscp.weights.Weights` | which past errors count, and how much |
|
|
46
|
+
| Quantile model | {class}`~tscp.quantile.QuantileModel` | the conformal distribution of the score, read at any level |
|
|
47
|
+
| Online update | {class}`~tscp.online.OnlineUpdate` | how the threshold reacts to its own misses |
|
|
48
|
+
| Region | {class}`~tscp.regions.Region` | what the user gets: a membership test and a size |
|
|
49
|
+
|
|
50
|
+
A score keeps the dimensions of `[H, N]` it does not reduce: an interval per step and
|
|
51
|
+
series, or one joint region over the horizon. A score may also split an element into
|
|
52
|
+
channels, such as the two sides of an interval, and share the miscoverage among them.
|
|
53
|
+
Feature maps ({class}`~tscp.features.FeatureMap`) turn what is known at each origin
|
|
54
|
+
into vectors for weights and quantile models.
|
|
55
|
+
|
|
56
|
+
## The lifecycle
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
method.fit(archive) # trainable components see the fit and validation splits only
|
|
60
|
+
method.calibrate(archive) # scores of the calibration pool
|
|
61
|
+
region = method.predict(archive) # one region per test origin
|
|
62
|
+
report = evaluate(region, archive.split("test"), alpha)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Without an online update, `predict` reads every threshold at once. With one, it answers the
|
|
66
|
+
test origins in time order and hands the update each outcome once its target time has
|
|
67
|
+
passed. {func}`~tscp.metrics.evaluate` returns a {class}`~tscp.metrics.Report`: coverage,
|
|
68
|
+
coverage per step and over the horizon, region size as log-volume per dimension, and
|
|
69
|
+
conditional coverage on request.
|
|
70
|
+
|
|
71
|
+
A fitted method is saved with {func}`~tscp.methods.save_method` and loaded with
|
|
72
|
+
{func}`~tscp.methods.load_method`; calibration is cheap, so a loaded method is
|
|
73
|
+
calibrated again before it predicts.
|
|
74
|
+
|
|
75
|
+
## Regions
|
|
76
|
+
|
|
77
|
+
Regions live in residual coordinates: a target `y` with forecast `f` is covered when
|
|
78
|
+
`y - f` lies in the region. Coverage and volume do not change under this shift, so
|
|
79
|
+
evaluation never needs the forecasts, and `region.to_target(forecasts)` maps regions back
|
|
80
|
+
to target units. Region size is reported as log-volume per dimension, never as the mean of
|
|
81
|
+
raw volumes, which the widest regions would dominate.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Sphinx configuration for the tscp documentation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
sys.path.insert(0, os.path.abspath("../../src"))
|
|
7
|
+
|
|
8
|
+
import tscp # noqa: E402
|
|
9
|
+
|
|
10
|
+
project = "tscp"
|
|
11
|
+
author = "Roberto Neglia"
|
|
12
|
+
copyright = "2026, Roberto Neglia"
|
|
13
|
+
release = tscp.__version__
|
|
14
|
+
version = release
|
|
15
|
+
|
|
16
|
+
extensions = [
|
|
17
|
+
"myst_parser",
|
|
18
|
+
"sphinx.ext.autodoc",
|
|
19
|
+
"sphinx.ext.intersphinx",
|
|
20
|
+
"sphinx.ext.napoleon",
|
|
21
|
+
"sphinx.ext.viewcode",
|
|
22
|
+
"sphinx_copybutton",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
source_suffix = {".md": "markdown", ".rst": "restructuredtext"}
|
|
26
|
+
exclude_patterns = ["build", "generated"]
|
|
27
|
+
|
|
28
|
+
autodoc_default_options = {"members": True, "imported-members": True, "member-order": "bysource"}
|
|
29
|
+
autodoc_typehints = "description"
|
|
30
|
+
napoleon_google_docstring = True
|
|
31
|
+
napoleon_numpy_docstring = False
|
|
32
|
+
myst_heading_anchors = 3
|
|
33
|
+
|
|
34
|
+
# Set TSCP_DOCS_OFFLINE=1 to build without fetching the inventories of other projects.
|
|
35
|
+
intersphinx_mapping = (
|
|
36
|
+
{}
|
|
37
|
+
if os.environ.get("TSCP_DOCS_OFFLINE")
|
|
38
|
+
else {
|
|
39
|
+
"python": ("https://docs.python.org/3", None),
|
|
40
|
+
"numpy": ("https://numpy.org/doc/stable", None),
|
|
41
|
+
"torch": ("https://docs.pytorch.org/docs/stable", None),
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
html_theme = "furo"
|
|
46
|
+
html_title = f"tscp {release}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# tscp
|
|
2
|
+
|
|
3
|
+
Conformal prediction for time series, in PyTorch: the published methods behind one
|
|
4
|
+
interface, from one-step intervals to joint regions over a forecast horizon and across
|
|
5
|
+
series, with one evaluation protocol.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from tscp.datasets import variance_shift
|
|
9
|
+
from tscp.methods import get_method
|
|
10
|
+
from tscp.metrics import evaluate
|
|
11
|
+
|
|
12
|
+
archive = variance_shift(horizon=1)
|
|
13
|
+
method = get_method("nexcp", decay=0.99, alpha=0.1)
|
|
14
|
+
region = method.fit(archive).calibrate(archive).predict(archive)
|
|
15
|
+
print(evaluate(region, archive.split("test"), alpha=0.1, window=200))
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```{toctree}
|
|
19
|
+
:maxdepth: 2
|
|
20
|
+
|
|
21
|
+
concepts
|
|
22
|
+
tutorials/compose
|
|
23
|
+
tutorials/component
|
|
24
|
+
tutorials/learned
|
|
25
|
+
tutorials/plugin
|
|
26
|
+
tutorials/production
|
|
27
|
+
api
|
|
28
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Write a component
|
|
2
|
+
|
|
3
|
+
A component is one choice on one axis. Writing one means subclassing the axis's base
|
|
4
|
+
class and implementing a method or two; everything else (fitting, masks for unavailable
|
|
5
|
+
and missing data, online release, devices, configs) is the composer's job.
|
|
6
|
+
|
|
7
|
+
This weight scheme counts only the past errors at the same point of a cycle, such as the
|
|
8
|
+
same hour of the day, and lets older cycles count less:
|
|
9
|
+
|
|
10
|
+
```{literalinclude} ../../../examples/custom_component.py
|
|
11
|
+
:language: python
|
|
12
|
+
:pyobject: PeriodicWeights
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Its constructor takes only JSON-friendly arguments, so its config records and rebuilds
|
|
16
|
+
it. `forward` returns nonnegative, unnormalized weights of each pool row for each query;
|
|
17
|
+
the composer multiplies them by the availability and observed masks, so the component
|
|
18
|
+
never handles either.
|
|
19
|
+
|
|
20
|
+
## Check it
|
|
21
|
+
|
|
22
|
+
{mod}`tscp.testing` checks that a component keeps its axis's contract, and that a method
|
|
23
|
+
keeps tscp's: causality, split discipline, config round trips, saving and loading, and
|
|
24
|
+
devices. A check raises an `AssertionError` that names the first property that fails.
|
|
25
|
+
|
|
26
|
+
```{literalinclude} ../../../examples/custom_component.py
|
|
27
|
+
:language: python
|
|
28
|
+
:pyobject: main
|
|
29
|
+
:start-at: def periodic_method
|
|
30
|
+
:end-at: print("PeriodicWeights
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
There is a check per axis: {func}`~tscp.testing.check_score`,
|
|
34
|
+
{func}`~tscp.testing.check_weights`, {func}`~tscp.testing.check_quantile_model`,
|
|
35
|
+
{func}`~tscp.testing.check_online_update`, {func}`~tscp.testing.check_region` and
|
|
36
|
+
{func}`~tscp.testing.check_feature_map`.
|
|
37
|
+
|
|
38
|
+
## Use it
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python examples/custom_component.py --config examples/configs/custom_component.yaml
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
On errors whose size depends on the hour, split conformal over-covers some hours and
|
|
45
|
+
under-covers others; the periodic weights keep every hour near the target. To make the
|
|
46
|
+
component buildable by name in specs, register it ({doc}`plugin`).
|