certflow 1.0.1__tar.gz → 1.0.2__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.
- {certflow-1.0.1 → certflow-1.0.2}/CHANGELOG.md +22 -0
- {certflow-1.0.1 → certflow-1.0.2}/CITATION.cff +1 -0
- {certflow-1.0.1 → certflow-1.0.2}/PKG-INFO +7 -4
- {certflow-1.0.1 → certflow-1.0.2}/README.md +3 -3
- {certflow-1.0.1 → certflow-1.0.2}/pyproject.toml +3 -2
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/__init__.py +1 -1
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/harness.py +3 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/realworld.py +8 -1
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_harness.py +29 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_realworld.py +32 -2
- {certflow-1.0.1 → certflow-1.0.2}/.gitignore +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/LICENSE +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/baselines.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/cert.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/ch.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/conformal.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/drift.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/egraph.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/episodes.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/fastgraph.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/graphcore.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/movingai.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/oracle.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/roadnet.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/sensing.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/snapshot.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/src/certflow/types.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_baselines.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_cert.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_ch.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_conformal.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_drift_oracle.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_egraph.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_episodes.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_fastgraph.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_graphcore.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_movingai.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_movingai_experiment.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_roadnet.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_snapshot.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_state_sync.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_tier1_smoke.py +0 -0
- {certflow-1.0.1 → certflow-1.0.2}/tests/test_tier2_smoke.py +0 -0
|
@@ -4,6 +4,27 @@ All notable changes to CERT-FLOW are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [1.0.2] - 2026-06-10
|
|
8
|
+
|
|
9
|
+
Packaging and serialization fixes for the freshly published library.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `pytest` now discovers the `src/`-layout package on a fresh checkout: the
|
|
13
|
+
`[tool.pytest.ini_options]` `pythonpath` was `["."]` (repo root, no package
|
|
14
|
+
there), so `python -m pytest` failed with `ModuleNotFoundError: certflow`
|
|
15
|
+
unless `PYTHONPATH=src` was set or the package was installed. Set to
|
|
16
|
+
`["src"]`.
|
|
17
|
+
- `EpisodeResult.oracle_cost` is now serialized by `save_results` and restored
|
|
18
|
+
by `load_results`. It was dropped on save, so reloaded Tier-2 results came
|
|
19
|
+
back with `oracle_cost = nan` and any regret analysis silently reported NaN.
|
|
20
|
+
Legacy result files without the field still load (oracle_cost stays nan).
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- `realworld` optional extra (`pip install 'certflow[realworld]'`) declaring
|
|
24
|
+
the `pandas` and `tables` dependencies the METR-LA / PEMS-BAY traffic
|
|
25
|
+
adapter needs. The core install stays numpy/scipy only; `_load_traffic` now
|
|
26
|
+
raises a clear `ImportError` pointing at the extra when pandas is absent.
|
|
27
|
+
|
|
7
28
|
## [1.0.1] - 2026-06-10
|
|
8
29
|
|
|
9
30
|
First PyPI release (`pip install certflow`).
|
|
@@ -50,5 +71,6 @@ Planning under Drifting Costs (Extended Version)*.
|
|
|
50
71
|
sum-aware certificate, impossibility of a tighter lower bound,
|
|
51
72
|
decision-uniform validity, churn floor).
|
|
52
73
|
|
|
74
|
+
[1.0.2]: https://github.com/Archerkattri/CERT-FLOW/releases/tag/v1.0.2
|
|
53
75
|
[1.0.1]: https://github.com/Archerkattri/CERT-FLOW/releases/tag/v1.0.1
|
|
54
76
|
[1.0.0]: https://github.com/Archerkattri/CERT-FLOW/releases/tag/v1.0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: certflow
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Certified route planning under drifting costs: conformal LB<=OPT<=UB certificates, certificate-directed sensing, proof-gated preprocessing
|
|
5
5
|
Project-URL: Homepage, https://github.com/Archerkattri/CERT-FLOW
|
|
6
6
|
Project-URL: Repository, https://github.com/Archerkattri/CERT-FLOW
|
|
@@ -26,13 +26,16 @@ Provides-Extra: dev
|
|
|
26
26
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
27
27
|
Provides-Extra: fast
|
|
28
28
|
Requires-Dist: numba; extra == 'fast'
|
|
29
|
+
Provides-Extra: realworld
|
|
30
|
+
Requires-Dist: pandas>=1.5; extra == 'realworld'
|
|
31
|
+
Requires-Dist: tables>=3.7; extra == 'realworld'
|
|
29
32
|
Description-Content-Type: text/markdown
|
|
30
33
|
|
|
31
34
|
<p align="center"><img src="assets/banner.svg" alt="CERT-FLOW" width="100%"/></p>
|
|
32
35
|
|
|
33
36
|
<p align="center">
|
|
34
37
|
<a href="https://pypi.org/project/certflow/"><img alt="PyPI" src="https://img.shields.io/pypi/v/certflow?color=009E73"></a>
|
|
35
|
-
<a href="#reproducing-every-number"><img alt="tests" src="https://img.shields.io/badge/tests-
|
|
38
|
+
<a href="#reproducing-every-number"><img alt="tests" src="https://img.shields.io/badge/tests-227%20passing-0072B2"></a>
|
|
36
39
|
<img alt="python" src="https://img.shields.io/badge/python-3.10%2B-56B4E9">
|
|
37
40
|
<img alt="license" src="https://img.shields.io/badge/license-MIT-1a7f37">
|
|
38
41
|
<img alt="coverage claim" src="https://img.shields.io/badge/certificate%20coverage-1.000%20measured-D55E00">
|
|
@@ -107,8 +110,8 @@ To develop or reproduce the paper numbers, work from a clone:
|
|
|
107
110
|
```bash
|
|
108
111
|
git clone https://github.com/Archerkattri/CERT-FLOW && cd CERT-FLOW
|
|
109
112
|
python -m venv cert_env && source cert_env/bin/activate
|
|
110
|
-
pip install -e ".[dev,fast]"
|
|
111
|
-
pytest # full suite:
|
|
113
|
+
pip install -e ".[dev,fast,realworld]" h5py
|
|
114
|
+
pytest # full suite: 227 with datasets; data-dependent tests skip cleanly without data/
|
|
112
115
|
```
|
|
113
116
|
|
|
114
117
|
## Reproducing every number
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://pypi.org/project/certflow/"><img alt="PyPI" src="https://img.shields.io/pypi/v/certflow?color=009E73"></a>
|
|
5
|
-
<a href="#reproducing-every-number"><img alt="tests" src="https://img.shields.io/badge/tests-
|
|
5
|
+
<a href="#reproducing-every-number"><img alt="tests" src="https://img.shields.io/badge/tests-227%20passing-0072B2"></a>
|
|
6
6
|
<img alt="python" src="https://img.shields.io/badge/python-3.10%2B-56B4E9">
|
|
7
7
|
<img alt="license" src="https://img.shields.io/badge/license-MIT-1a7f37">
|
|
8
8
|
<img alt="coverage claim" src="https://img.shields.io/badge/certificate%20coverage-1.000%20measured-D55E00">
|
|
@@ -77,8 +77,8 @@ To develop or reproduce the paper numbers, work from a clone:
|
|
|
77
77
|
```bash
|
|
78
78
|
git clone https://github.com/Archerkattri/CERT-FLOW && cd CERT-FLOW
|
|
79
79
|
python -m venv cert_env && source cert_env/bin/activate
|
|
80
|
-
pip install -e ".[dev,fast]"
|
|
81
|
-
pytest # full suite:
|
|
80
|
+
pip install -e ".[dev,fast,realworld]" h5py
|
|
81
|
+
pytest # full suite: 227 with datasets; data-dependent tests skip cleanly without data/
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
## Reproducing every number
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "certflow"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.2"
|
|
4
4
|
authors = [{name = "Krishi Attri", email = "krishiattriwork@gmail.com"}]
|
|
5
5
|
license = {text = "MIT"}
|
|
6
6
|
description = "Certified route planning under drifting costs: conformal LB<=OPT<=UB certificates, certificate-directed sensing, proof-gated preprocessing"
|
|
@@ -41,6 +41,7 @@ Issues = "https://github.com/Archerkattri/CERT-FLOW/issues"
|
|
|
41
41
|
[project.optional-dependencies]
|
|
42
42
|
fast = ["numba"]
|
|
43
43
|
dev = ["pytest>=7.0"]
|
|
44
|
+
realworld = ["pandas>=1.5", "tables>=3.7"]
|
|
44
45
|
|
|
45
46
|
[build-system]
|
|
46
47
|
requires = ["hatchling"]
|
|
@@ -61,5 +62,5 @@ include = [
|
|
|
61
62
|
]
|
|
62
63
|
|
|
63
64
|
[tool.pytest.ini_options]
|
|
64
|
-
pythonpath = ["
|
|
65
|
+
pythonpath = ["src"]
|
|
65
66
|
testpaths = ["tests"]
|
|
@@ -26,7 +26,7 @@ from certflow.cert import CertPlanner, PlannerConfig
|
|
|
26
26
|
from certflow.conformal import ACITracker, ConformalScorer
|
|
27
27
|
from certflow.types import Certificate, EdgeBelief, World
|
|
28
28
|
|
|
29
|
-
__version__ = "1.0.
|
|
29
|
+
__version__ = "1.0.2"
|
|
30
30
|
|
|
31
31
|
__all__ = [
|
|
32
32
|
"ACITracker",
|
|
@@ -426,6 +426,7 @@ def _episode_result_to_dict(ep: EpisodeResult) -> dict[str, Any]:
|
|
|
426
426
|
"travel_cost": _float_to_json(ep.travel_cost),
|
|
427
427
|
"sense_cost": _float_to_json(ep.sense_cost),
|
|
428
428
|
"reached_goal": ep.reached_goal,
|
|
429
|
+
"oracle_cost": _float_to_json(ep.oracle_cost),
|
|
429
430
|
}
|
|
430
431
|
|
|
431
432
|
|
|
@@ -435,6 +436,8 @@ def _episode_result_from_dict(d: dict[str, Any]) -> EpisodeResult:
|
|
|
435
436
|
travel_cost=float(_float_from_json(d["travel_cost"])),
|
|
436
437
|
sense_cost=float(_float_from_json(d["sense_cost"])),
|
|
437
438
|
reached_goal=bool(d["reached_goal"]),
|
|
439
|
+
# tolerate legacy files written before oracle_cost was serialised
|
|
440
|
+
oracle_cost=float(_float_from_json(d.get("oracle_cost", _NAN_SENTINEL))),
|
|
438
441
|
)
|
|
439
442
|
|
|
440
443
|
|
|
@@ -28,7 +28,14 @@ DATA_DIR = Path(__file__).resolve().parents[2] / "data"
|
|
|
28
28
|
@lru_cache(maxsize=2)
|
|
29
29
|
def _load_traffic(dataset: str) -> tuple[list[str], np.ndarray, dict[tuple[str, str], float]]:
|
|
30
30
|
"""(sensor_ids, speeds[bins x sensors] mph with missing filled, distances m)."""
|
|
31
|
-
|
|
31
|
+
try:
|
|
32
|
+
import pandas as pd
|
|
33
|
+
except ImportError as exc: # pragma: no cover - exercised via a stubbed import
|
|
34
|
+
raise ImportError(
|
|
35
|
+
"The real-world traffic adapter (METR-LA / PEMS-BAY) needs pandas and "
|
|
36
|
+
"PyTables, which are not part of the core install. Install them with:\n"
|
|
37
|
+
" pip install 'certflow[realworld]'"
|
|
38
|
+
) from exc
|
|
32
39
|
|
|
33
40
|
if dataset == "metr-la":
|
|
34
41
|
h5, dist_csv = DATA_DIR / "metr-la/metr_la.h5", DATA_DIR / "metr-la/distances_la_2012.csv"
|
|
@@ -368,6 +368,35 @@ class TestSaveLoad:
|
|
|
368
368
|
path = save_results(result, tmp)
|
|
369
369
|
assert path.name == f"{config.config_id()}.json"
|
|
370
370
|
|
|
371
|
+
def test_oracle_cost_round_trips(self):
|
|
372
|
+
"""Tier-2 regret depends on oracle_cost surviving save/load (not nan)."""
|
|
373
|
+
config = ExperimentConfig(n_seeds=1, base_seed=0)
|
|
374
|
+
ep = EpisodeResult(
|
|
375
|
+
rounds=[],
|
|
376
|
+
travel_cost=12.5,
|
|
377
|
+
sense_cost=1.0,
|
|
378
|
+
reached_goal=True,
|
|
379
|
+
oracle_cost=9.75,
|
|
380
|
+
)
|
|
381
|
+
result = ExperimentResult(config=config, episodes=[ep], seeds=[7])
|
|
382
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
383
|
+
path = save_results(result, tmp)
|
|
384
|
+
loaded = load_results(path)
|
|
385
|
+
|
|
386
|
+
loaded_oracle = loaded.episodes[0].oracle_cost
|
|
387
|
+
assert not math.isnan(loaded_oracle)
|
|
388
|
+
assert math.isclose(loaded_oracle, 9.75)
|
|
389
|
+
|
|
390
|
+
def test_oracle_cost_nan_round_trips(self):
|
|
391
|
+
"""Tier-1 episodes leave oracle_cost as nan; that must survive too."""
|
|
392
|
+
config = ExperimentConfig(n_seeds=1, base_seed=0)
|
|
393
|
+
ep = EpisodeResult(rounds=[], travel_cost=1.0, sense_cost=0.0, reached_goal=False)
|
|
394
|
+
result = ExperimentResult(config=config, episodes=[ep], seeds=[0])
|
|
395
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
396
|
+
path = save_results(result, tmp)
|
|
397
|
+
loaded = load_results(path)
|
|
398
|
+
assert math.isnan(loaded.episodes[0].oracle_cost)
|
|
399
|
+
|
|
371
400
|
def test_sensed_edge_tuple_roundtrip(self):
|
|
372
401
|
round_log = RoundLog(
|
|
373
402
|
t=0.0,
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
"""Smoke tests for the METR-LA replay world (skipped if data absent)."""
|
|
2
|
-
import
|
|
2
|
+
import builtins
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
DATA = Path(__file__).resolve().parents[1] / "data/metr-la/metr_la.h5"
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_core_import_does_not_need_pandas():
|
|
11
|
+
"""Importing certflow core (and realworld module) must not require pandas."""
|
|
12
|
+
import certflow # noqa: F401
|
|
13
|
+
import certflow.realworld # noqa: F401
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_load_traffic_clear_message_without_pandas(monkeypatch):
|
|
17
|
+
"""If pandas is absent, the adapter points users at certflow[realworld]."""
|
|
18
|
+
from certflow import realworld
|
|
19
|
+
|
|
20
|
+
real_import = builtins.__import__
|
|
21
|
+
|
|
22
|
+
def _no_pandas(name, *args, **kwargs):
|
|
23
|
+
if name == "pandas" or name.startswith("pandas."):
|
|
24
|
+
raise ImportError("No module named 'pandas'")
|
|
25
|
+
return real_import(name, *args, **kwargs)
|
|
26
|
+
|
|
27
|
+
monkeypatch.setattr(builtins, "__import__", _no_pandas)
|
|
28
|
+
with pytest.raises(ImportError) as exc:
|
|
29
|
+
realworld._load_traffic("metr-la")
|
|
30
|
+
assert "certflow[realworld]" in str(exc.value)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# The data-dependent smoke tests below skip cleanly when METR-LA is absent.
|
|
34
|
+
_skip_no_data = pytest.mark.skipif(not DATA.exists(), reason="METR-LA not downloaded")
|
|
9
35
|
|
|
10
36
|
|
|
11
37
|
@pytest.fixture(scope="module")
|
|
@@ -15,6 +41,7 @@ def world():
|
|
|
15
41
|
return TrafficWorld(seed=0, n_bins=144) # half a day
|
|
16
42
|
|
|
17
43
|
|
|
44
|
+
@_skip_no_data
|
|
18
45
|
def test_graph_and_costs_sane(world):
|
|
19
46
|
n_edges = sum(len(nbrs) for nbrs in world.graph.values())
|
|
20
47
|
assert n_edges > 500
|
|
@@ -26,6 +53,7 @@ def test_graph_and_costs_sane(world):
|
|
|
26
53
|
assert abs(world.true_cost(e, 299.0) - world.true_cost(e, 301.0)) < 5.0
|
|
27
54
|
|
|
28
55
|
|
|
56
|
+
@_skip_no_data
|
|
29
57
|
def test_determinism_and_noise(world):
|
|
30
58
|
from certflow.realworld import TrafficWorld
|
|
31
59
|
|
|
@@ -37,11 +65,13 @@ def test_determinism_and_noise(world):
|
|
|
37
65
|
assert abs(sum(obs) / len(obs) - truth) < 2.0 # mean ~ truth (scale 5s)
|
|
38
66
|
|
|
39
67
|
|
|
68
|
+
@_skip_no_data
|
|
40
69
|
def test_a1_violation_rate_measured(world):
|
|
41
70
|
# rho is the p95 of |dc/dt|: violations exist by construction (~5%)
|
|
42
71
|
assert 0.0 < world.a1_violation_rate < 0.15
|
|
43
72
|
|
|
44
73
|
|
|
74
|
+
@_skip_no_data
|
|
45
75
|
def test_planner_runs_on_traffic(world):
|
|
46
76
|
from certflow.cert import CertPlanner
|
|
47
77
|
from certflow.realworld import far_endpoints, traffic_planner_config
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|