qellaris 0.5.0__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.
- qellaris-0.5.0/LICENSE +25 -0
- qellaris-0.5.0/MANIFEST.in +19 -0
- qellaris-0.5.0/PKG-INFO +505 -0
- qellaris-0.5.0/PUBLISHING.md +140 -0
- qellaris-0.5.0/PYPI_README.md +460 -0
- qellaris-0.5.0/README.md +1182 -0
- qellaris-0.5.0/examples/example.py +91 -0
- qellaris-0.5.0/paper/QAIO_2027_Baseline_Benchmark_Paper.docx +0 -0
- qellaris-0.5.0/paper/build_overleaf.py +37 -0
- qellaris-0.5.0/paper/build_paper.py +311 -0
- qellaris-0.5.0/paper/overleaf/README.md +20 -0
- qellaris-0.5.0/paper/overleaf/main.tex +530 -0
- qellaris-0.5.0/paper/overleaf/references.bib +172 -0
- qellaris-0.5.0/paper/paper-source.md +288 -0
- qellaris-0.5.0/pyproject.toml +99 -0
- qellaris-0.5.0/setup.cfg +4 -0
- qellaris-0.5.0/src/qellaris/__init__.py +40 -0
- qellaris-0.5.0/src/qellaris/__main__.py +111 -0
- qellaris-0.5.0/src/qellaris/_version.py +3 -0
- qellaris-0.5.0/src/qellaris/cli.py +230 -0
- qellaris-0.5.0/src/qellaris/control_env.py +504 -0
- qellaris-0.5.0/src/qellaris/data/kensington_buildings.json +356793 -0
- qellaris-0.5.0/src/qellaris/data/kensington_landuse.json +51216 -0
- qellaris-0.5.0/src/qellaris/data/kensington_streets.json +55913 -0
- qellaris-0.5.0/src/qellaris/data/kensington_towers.json +1 -0
- qellaris-0.5.0/src/qellaris/decision_metrics.py +70 -0
- qellaris-0.5.0/src/qellaris/demographics.py +316 -0
- qellaris-0.5.0/src/qellaris/domain.py +171 -0
- qellaris-0.5.0/src/qellaris/equity.py +627 -0
- qellaris-0.5.0/src/qellaris/equity_scenarios.py +230 -0
- qellaris-0.5.0/src/qellaris/evaluation.py +260 -0
- qellaris-0.5.0/src/qellaris/experiment.py +518 -0
- qellaris-0.5.0/src/qellaris/forecasting.py +217 -0
- qellaris-0.5.0/src/qellaris/geography.py +409 -0
- qellaris-0.5.0/src/qellaris/grid.py +420 -0
- qellaris-0.5.0/src/qellaris/hardware_job.py +113 -0
- qellaris-0.5.0/src/qellaris/inventory.py +103 -0
- qellaris-0.5.0/src/qellaris/menu.py +210 -0
- qellaris-0.5.0/src/qellaris/milp_reference.py +56 -0
- qellaris-0.5.0/src/qellaris/netbravo.py +442 -0
- qellaris-0.5.0/src/qellaris/ofcom.py +231 -0
- qellaris-0.5.0/src/qellaris/optimization.py +332 -0
- qellaris-0.5.0/src/qellaris/paths.py +98 -0
- qellaris-0.5.0/src/qellaris/placement.py +245 -0
- qellaris-0.5.0/src/qellaris/power.py +171 -0
- qellaris-0.5.0/src/qellaris/quantum.py +514 -0
- qellaris-0.5.0/src/qellaris/quantum_zoo.py +594 -0
- qellaris-0.5.0/src/qellaris/qubo.py +152 -0
- qellaris-0.5.0/src/qellaris/radio.py +189 -0
- qellaris-0.5.0/src/qellaris/real_data.py +98 -0
- qellaris-0.5.0/src/qellaris/region_packages.py +202 -0
- qellaris-0.5.0/src/qellaris/regions.py +519 -0
- qellaris-0.5.0/src/qellaris/rl.py +237 -0
- qellaris-0.5.0/src/qellaris/road_network.py +197 -0
- qellaris-0.5.0/src/qellaris/scenario_generation.py +102 -0
- qellaris-0.5.0/src/qellaris/sdk.py +195 -0
- qellaris-0.5.0/src/qellaris/simulation.py +928 -0
- qellaris-0.5.0/src/qellaris/solara_app.py +2255 -0
- qellaris-0.5.0/src/qellaris/solvers.py +375 -0
- qellaris-0.5.0/src/qellaris/template.py +736 -0
- qellaris-0.5.0/src/qellaris/tests/__init__.py +1 -0
- qellaris-0.5.0/src/qellaris/tests/conftest.py +8 -0
- qellaris-0.5.0/src/qellaris/tests/test_benchmark.py +182 -0
- qellaris-0.5.0/src/qellaris/tests/test_demographics.py +261 -0
- qellaris-0.5.0/src/qellaris/tests/test_demographics_page.py +114 -0
- qellaris-0.5.0/src/qellaris/tests/test_entry_points.py +194 -0
- qellaris-0.5.0/src/qellaris/tests/test_equity.py +67 -0
- qellaris-0.5.0/src/qellaris/tests/test_equity_scenarios.py +307 -0
- qellaris-0.5.0/src/qellaris/tests/test_equity_state.py +152 -0
- qellaris-0.5.0/src/qellaris/tests/test_evaluation.py +144 -0
- qellaris-0.5.0/src/qellaris/tests/test_geometry.py +121 -0
- qellaris-0.5.0/src/qellaris/tests/test_hardware_job.py +38 -0
- qellaris-0.5.0/src/qellaris/tests/test_inventory.py +114 -0
- qellaris-0.5.0/src/qellaris/tests/test_land_use.py +222 -0
- qellaris-0.5.0/src/qellaris/tests/test_launcher.py +88 -0
- qellaris-0.5.0/src/qellaris/tests/test_layout.py +103 -0
- qellaris-0.5.0/src/qellaris/tests/test_layout_factorial_archive.py +43 -0
- qellaris-0.5.0/src/qellaris/tests/test_menu.py +73 -0
- qellaris-0.5.0/src/qellaris/tests/test_milp_reference.py +28 -0
- qellaris-0.5.0/src/qellaris/tests/test_netbravo.py +73 -0
- qellaris-0.5.0/src/qellaris/tests/test_netbravo_ingest.py +156 -0
- qellaris-0.5.0/src/qellaris/tests/test_paths.py +139 -0
- qellaris-0.5.0/src/qellaris/tests/test_placement.py +110 -0
- qellaris-0.5.0/src/qellaris/tests/test_power.py +213 -0
- qellaris-0.5.0/src/qellaris/tests/test_quantum.py +219 -0
- qellaris-0.5.0/src/qellaris/tests/test_quantum_zoo.py +101 -0
- qellaris-0.5.0/src/qellaris/tests/test_radio.py +57 -0
- qellaris-0.5.0/src/qellaris/tests/test_region_packages.py +130 -0
- qellaris-0.5.0/src/qellaris/tests/test_regions.py +231 -0
- qellaris-0.5.0/src/qellaris/tests/test_rl_env.py +159 -0
- qellaris-0.5.0/src/qellaris/tests/test_scenario_generation.py +38 -0
- qellaris-0.5.0/src/qellaris/tests/test_sdk.py +60 -0
- qellaris-0.5.0/src/qellaris/tests/test_study_area_page.py +149 -0
- qellaris-0.5.0/src/qellaris/tests/test_template.py +218 -0
- qellaris-0.5.0/src/qellaris/tests/test_ui_model.py +157 -0
- qellaris-0.5.0/src/qellaris/ui.py +198 -0
- qellaris-0.5.0/src/qellaris.egg-info/PKG-INFO +505 -0
- qellaris-0.5.0/src/qellaris.egg-info/SOURCES.txt +102 -0
- qellaris-0.5.0/src/qellaris.egg-info/dependency_links.txt +1 -0
- qellaris-0.5.0/src/qellaris.egg-info/entry_points.txt +13 -0
- qellaris-0.5.0/src/qellaris.egg-info/requires.txt +21 -0
- qellaris-0.5.0/src/qellaris.egg-info/top_level.txt +1 -0
- qellaris-0.5.0/workspace/runs/qaio-simulator/solver_comparison.csv +8 -0
- qellaris-0.5.0/workspace/runs/qaio-simulator/summary.json +364 -0
qellaris-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Qellaris benchmark team
|
|
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.
|
|
22
|
+
|
|
23
|
+
Bundled Kensington map extracts (src/qellaris/data/*.json) are derived from
|
|
24
|
+
OpenStreetMap, (c) OpenStreetMap contributors, and are available under the
|
|
25
|
+
Open Database License (ODbL) 1.0: https://opendatacommons.org/licenses/odbl/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Source release: reproducible application, examples and paper; no local workspace.
|
|
2
|
+
include README.md PYPI_README.md PUBLISHING.md LICENSE pyproject.toml MANIFEST.in
|
|
3
|
+
recursive-include src/qellaris *.py *.json
|
|
4
|
+
recursive-include examples *.py
|
|
5
|
+
include paper/paper-source.md
|
|
6
|
+
include paper/build_paper.py paper/build_overleaf.py
|
|
7
|
+
include paper/QAIO_2027_Baseline_Benchmark_Paper.docx
|
|
8
|
+
include paper/overleaf/main.tex paper/overleaf/references.bib paper/overleaf/README.md
|
|
9
|
+
# Keep the exact input to the paper-table builder, not the entire run history.
|
|
10
|
+
include workspace/runs/qaio-simulator/solver_comparison.csv
|
|
11
|
+
include workspace/runs/qaio-simulator/summary.json
|
|
12
|
+
global-exclude *.py[cod] ~$* *.log
|
|
13
|
+
prune paper/tmp_render
|
|
14
|
+
prune paper/tmp_render_20260902
|
|
15
|
+
prune .idea
|
|
16
|
+
prune .run
|
|
17
|
+
prune .venv
|
|
18
|
+
prune .venv-py311
|
|
19
|
+
prune .pytest_cache
|
qellaris-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qellaris
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Decision-focused evaluation harness for cellular sleep control: synthetic demand, interchangeable forecasters/controllers, shared capacity-flow scoring.
|
|
5
|
+
Author: Qellaris benchmark team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Quantum Algorithm Zoo, https://quantumalgorithmzoo.org/
|
|
8
|
+
Keywords: cellular networks,energy saving,cell sleep,benchmark,qubo,qaoa,quantum optimisation,reinforcement learning,mesa,agent-based model,forecasting
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
|
+
Classifier: Topic :: System :: Networking
|
|
21
|
+
Classifier: Framework :: Jupyter
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: mesa<4,>=3.3
|
|
26
|
+
Requires-Dist: solara<2,>=1.61
|
|
27
|
+
Requires-Dist: matplotlib<5,>=3.9
|
|
28
|
+
Requires-Dist: numpy<3,>=2
|
|
29
|
+
Requires-Dist: ipyvuetify<3
|
|
30
|
+
Requires-Dist: ipyvue<2
|
|
31
|
+
Requires-Dist: networkx<4,>=3.4
|
|
32
|
+
Requires-Dist: altair<7,>=5
|
|
33
|
+
Requires-Dist: gymnasium<2,>=1.0
|
|
34
|
+
Provides-Extra: quantum
|
|
35
|
+
Requires-Dist: dimod>=0.12; extra == "quantum"
|
|
36
|
+
Requires-Dist: dwave-samplers>=1.3; extra == "quantum"
|
|
37
|
+
Requires-Dist: qiskit>=1.0; extra == "quantum"
|
|
38
|
+
Requires-Dist: qiskit-aer>=0.15; extra == "quantum"
|
|
39
|
+
Requires-Dist: scipy>=1.11; extra == "quantum"
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
42
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
43
|
+
Requires-Dist: twine>=6; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# qellaris
|
|
47
|
+
|
|
48
|
+
**A benchmark for switching mobile-network sectors off to save energy, scored
|
|
49
|
+
on what each decision actually costs.**
|
|
50
|
+
|
|
51
|
+
Mobile base stations burn power even when nobody is using them. Putting
|
|
52
|
+
lightly used radio sectors to sleep saves energy, but sleep too many and
|
|
53
|
+
people go unserved. `qellaris` is a test bench for methods that make that
|
|
54
|
+
call every 15 minutes. It provides synthetic people moving on real
|
|
55
|
+
OpenStreetMap streets, interchangeable demand forecasters and controllers,
|
|
56
|
+
and **one shared scoring rule** that grades classical, quantum-inspired and
|
|
57
|
+
reinforcement-learning methods in exactly the same way.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install qellaris # benchmark, dashboard and command line
|
|
61
|
+
pip install "qellaris[quantum]" # + D-Wave samplers and Qiskit (QAOA)
|
|
62
|
+
qellaris menu # a guided menu: it asks, then runs the command
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Requires Python 3.11 or newer. Works on Windows, macOS and Linux.
|
|
66
|
+
|
|
67
|
+
> **What this is not.** It makes no claim of quantum advantage, real operator
|
|
68
|
+
> radio accuracy, or a calibrated digital twin of a live network. Demand,
|
|
69
|
+
> people and capacities are synthetic. The quantum solvers are classical
|
|
70
|
+
> simulations of quantum computers; nothing is sent to real quantum hardware
|
|
71
|
+
> unless you submit the exported job yourself.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Contents
|
|
76
|
+
|
|
77
|
+
Why another benchmark? · Install · Five-minute tour · The dashboard, page by
|
|
78
|
+
page · Command-line reference · Plug in your own forecaster or controller ·
|
|
79
|
+
Solvers · The Quantum Algorithm Zoo page · Study areas and region packages ·
|
|
80
|
+
How a schedule is scored · Fair comparisons · Equity · Data, licences and
|
|
81
|
+
limits
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Why another benchmark?
|
|
86
|
+
|
|
87
|
+
Most forecasting papers stop at accuracy, and most optimisation papers assume
|
|
88
|
+
the forecast is perfect. In practice a network operator forecasts demand,
|
|
89
|
+
commits a schedule, and only then learns how many people actually turned up.
|
|
90
|
+
`qellaris` scores that whole loop, and splits every bad outcome into its two
|
|
91
|
+
causes:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
solver_gap + forecast_gap = total_decision_regret
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
* **forecast gap**: what the forecast's errors cost you, given a perfect
|
|
98
|
+
optimiser;
|
|
99
|
+
* **solver gap**: what the optimiser cost you, given the same forecast.
|
|
100
|
+
|
|
101
|
+
All three schedules involved (yours, the reference solver's on your
|
|
102
|
+
forecast, and the reference solver's on the true demand) start from the same
|
|
103
|
+
network state and are scored by the same rule. So you can tell whether a
|
|
104
|
+
forecaster or an optimiser caused a bad result.
|
|
105
|
+
|
|
106
|
+
This matters because **accuracy and good decisions disagree**. In the
|
|
107
|
+
project's own runs, a seasonal forecaster was 5.6 times more accurate than a
|
|
108
|
+
flat average (by mean absolute error), yet the schedules built on it cost 58%
|
|
109
|
+
more in forecast gap. The flat average over-predicts in quiet hours, keeps
|
|
110
|
+
more sectors awake, and so rarely leaves anyone unserved. The benchmark
|
|
111
|
+
scores the decision, not the prediction.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Install
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install qellaris
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Optional extras:
|
|
122
|
+
|
|
123
|
+
| Extra | Adds | Install |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| `quantum` | `dimod`, `dwave-samplers`, `qiskit`, `qiskit-aer`, `scipy`: the D-Wave and QAOA solvers | `pip install "qellaris[quantum]"` |
|
|
126
|
+
| `dev` | `pytest`, `build`, `twine` | `pip install "qellaris[dev]"` |
|
|
127
|
+
|
|
128
|
+
Without the `quantum` extra the benchmark simply offers fewer solvers.
|
|
129
|
+
|
|
130
|
+
Downloaded maps and run results are written to a `workspace/` folder in the
|
|
131
|
+
current directory. Set the `QELLARIS_HOME` environment variable to put them
|
|
132
|
+
somewhere fixed.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Five-minute tour
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
qellaris menu
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The menu asks a few questions, prints the exact command it is about to run,
|
|
143
|
+
and runs it. Use it to learn the commands, then type them directly. Its
|
|
144
|
+
options:
|
|
145
|
+
|
|
146
|
+
1. Open the dashboard
|
|
147
|
+
2. Run the benchmark (quick look, all solvers, or five seeds)
|
|
148
|
+
3. Browse the Quantum Algorithm Zoo
|
|
149
|
+
4. Download a study area from OpenStreetMap
|
|
150
|
+
5. Share or import study areas as `.zip` packages
|
|
151
|
+
6. Worked examples (write your own agent)
|
|
152
|
+
7. Which solvers are installed?
|
|
153
|
+
|
|
154
|
+
Or go straight to it:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
qellaris ui # dashboard at http://127.0.0.1:8765
|
|
158
|
+
qellaris benchmark --geography kensington # leaderboard of every installed solver
|
|
159
|
+
qellaris template --compare # four example policies side by side
|
|
160
|
+
qellaris zoo --fit direct # quantum algorithms that could schedule
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`python -m qellaris <command>` works the same way if your scripts folder is
|
|
164
|
+
not on `PATH`.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## The dashboard, page by page
|
|
169
|
+
|
|
170
|
+
`qellaris ui` opens an interactive dashboard (built with Mesa and Solara) in
|
|
171
|
+
your browser. The left sidebar sets the population size, random seed, demand
|
|
172
|
+
event, scheduling solver and map; **Step** and **▶** run the simulation, and
|
|
173
|
+
**Reset** rebuilds it with new settings. Six named tabs:
|
|
174
|
+
|
|
175
|
+
**Live map.** The study area with its demand areas, how busy each one is,
|
|
176
|
+
radio coverage, every sector (awake or asleep), the simulated people, and
|
|
177
|
+
which mast serves which area. A toggle colours buildings by the use the model
|
|
178
|
+
assigns them. Footprints whose OpenStreetMap tags state their use are drawn
|
|
179
|
+
solid; ones that inherit their area's use are drawn faint. Beside the map: the
|
|
180
|
+
time of day, the current decision and its costs, and a live **solver
|
|
181
|
+
leaderboard** that runs every installed solver on the interval about to be
|
|
182
|
+
scheduled and ranks them.
|
|
183
|
+
|
|
184
|
+
**Time series.** Total and forecast demand over time, the split of decision
|
|
185
|
+
cost into forecast gap and solver gap, and energy, unserved demand and equity
|
|
186
|
+
per interval.
|
|
187
|
+
|
|
188
|
+
**Equity audit.** Who actually gets served, across twelve synthetic
|
|
189
|
+
dimensions: age, income band, device, access needs, work pattern, household
|
|
190
|
+
and more. For each it shows the gap between best- and worst-served groups,
|
|
191
|
+
which direction the gap runs, and whether the energy-saving schedule caused
|
|
192
|
+
it. The same interval is replayed with every sector awake for comparison.
|
|
193
|
+
|
|
194
|
+
**Demographics.** Who is out there and what they are doing (travelling, at
|
|
195
|
+
home, at work, out), with no service information at all. It is kept separate
|
|
196
|
+
from the equity audit so "this group is large and busy" is never confused
|
|
197
|
+
with "this group is being failed".
|
|
198
|
+
|
|
199
|
+
**Study area.** Download a new area by place name or bounding box, see every
|
|
200
|
+
area you already have, switch to one with **Use** (no restart), save any area
|
|
201
|
+
as a `.zip` region package, or drop someone else's package in. Beside it, a
|
|
202
|
+
land-use audit shows what the area actually contains and which venue (if any)
|
|
203
|
+
hosts the demand event.
|
|
204
|
+
|
|
205
|
+
**Quantum Algorithm Zoo.** Every algorithm from the Quantum Algorithm Zoo
|
|
206
|
+
with a plain-language description, plus a guide to plugging one into the
|
|
207
|
+
benchmark (see the Quantum Algorithm Zoo section below).
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Command-line reference
|
|
212
|
+
|
|
213
|
+
Everything is one command, `qellaris`, with subcommands. Each subcommand also
|
|
214
|
+
exists as its own script (`qellaris-zoo`, `qellaris-menu`, ...).
|
|
215
|
+
|
|
216
|
+
| Command | What it does |
|
|
217
|
+
| --- | --- |
|
|
218
|
+
| `qellaris menu` | Guided menu over everything below |
|
|
219
|
+
| `qellaris ui [--port 8765] [--no-browser]` | Interactive dashboard |
|
|
220
|
+
| `qellaris benchmark [...]` | Forecast-and-schedule benchmark; prints a leaderboard and writes CSV/JSON results |
|
|
221
|
+
| `qellaris benchmark --list-backends` | Which solvers are installed |
|
|
222
|
+
| `qellaris equity --scenarios protective neutral ...` | Compare fairness rules on one identical population |
|
|
223
|
+
| `qellaris train --episodes 120` | Train the reinforcement-learning controller |
|
|
224
|
+
| `qellaris template --compare / --forecast / --explain / --broker` | Worked examples for writing your own agent |
|
|
225
|
+
| `qellaris region NAME --place "Camden, London" --radius-km 1` | Download a study area from OpenStreetMap |
|
|
226
|
+
| `qellaris region-pack list / export / import` | Share study areas as `.zip` files |
|
|
227
|
+
| `qellaris zoo [--fit direct] [--search WORD] [--guide]` | Quantum Algorithm Zoo catalogue and guide |
|
|
228
|
+
| `qellaris hardware-job JOB.json RESULT.json` | Decode results returned from a real quantum computer |
|
|
229
|
+
| `qellaris opencellid`, `qellaris netbravo` | Fetch optional real cell-site and signal datasets |
|
|
230
|
+
|
|
231
|
+
Useful `benchmark` options:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
qellaris benchmark --geography kensington \
|
|
235
|
+
--solvers greedy annealing qaoa always_on \
|
|
236
|
+
--scenario entertainment_event \
|
|
237
|
+
--seeds 17 23 42 101 2027 \
|
|
238
|
+
--output workspace/runs/my-run
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
* `--geography`: `kensington` (bundled), `synthetic` (no map), or the path of
|
|
242
|
+
any downloaded study area
|
|
243
|
+
* `--scenario`: `normal`, `entertainment_event` (best venue the area has), or
|
|
244
|
+
a specific venue type such as `park_event`
|
|
245
|
+
* `--seeds`: repeat the whole experiment and report paired confidence
|
|
246
|
+
intervals
|
|
247
|
+
* `--point-forecast`: schedule on the forecast itself rather than its upper
|
|
248
|
+
bound
|
|
249
|
+
* `--inventory FILE.json`: use your own fixed tower and sector layout
|
|
250
|
+
|
|
251
|
+
Run any command with `--help` for the full list.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Plug in your own forecaster or controller
|
|
256
|
+
|
|
257
|
+
No Mesa or Gymnasium subclassing is needed. Both receive plain JSON-safe
|
|
258
|
+
lists, and two decorators register them:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from qellaris import register_agent, register_forecaster
|
|
262
|
+
from qellaris.template import build_env, run_episode
|
|
263
|
+
|
|
264
|
+
@register_forecaster("last-value")
|
|
265
|
+
class LastValue:
|
|
266
|
+
def forecast(self, request):
|
|
267
|
+
last = {row["zone_id"]: row["demand"] for row in request["records"]}
|
|
268
|
+
return [{"step": request["train_steps"] + step, "zone_id": zone,
|
|
269
|
+
"prediction": value, "lower": value * 0.8, "upper": value * 1.2}
|
|
270
|
+
for zone, value in last.items()
|
|
271
|
+
for step in range(request["horizon"])]
|
|
272
|
+
|
|
273
|
+
@register_agent("busy-sectors")
|
|
274
|
+
class BusySectors:
|
|
275
|
+
def act(self, state):
|
|
276
|
+
sectors, width = state["network_shape"]
|
|
277
|
+
load = state["network_vector"]
|
|
278
|
+
return [int(load[i * width + 2] > 0.25) for i in range(sectors)] # 1 = awake
|
|
279
|
+
|
|
280
|
+
result = run_episode(BusySectors(), build_env(days=1))
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
A controller receives, each interval, the forecast and its upper bound, which
|
|
284
|
+
sectors were awake, eight features per sector, and a separate demographic
|
|
285
|
+
table. It returns one 0/1 per sector. Its schedule is then scored by the
|
|
286
|
+
shared rule. `qellaris template --explain` prints exactly what arrives.
|
|
287
|
+
|
|
288
|
+
For reinforcement learning there is a Gymnasium environment:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
import gymnasium as gym
|
|
292
|
+
import qellaris.control_env # registers the environment
|
|
293
|
+
|
|
294
|
+
env = gym.make("QaioSectorControl-v0", n_agents=240)
|
|
295
|
+
state, info = env.reset(seed=17)
|
|
296
|
+
state, reward, terminated, truncated, info = env.step(env.action_space.sample())
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The reward is the negative of the same score every solver gets, and `info`
|
|
300
|
+
reports energy, unserved demand and switching separately.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Solvers
|
|
305
|
+
|
|
306
|
+
| Solver | How it chooses | Needs |
|
|
307
|
+
| --- | --- | --- |
|
|
308
|
+
| `greedy` | Builds a schedule sector by sector, then improves it locally | — |
|
|
309
|
+
| `exact` | Branch and bound; reports whether it *proved* its answer optimal | — |
|
|
310
|
+
| `annealing` | Simulated annealing on the QUBO formula | — |
|
|
311
|
+
| `rl` | A small learned policy (one shared linear model per sector) | — |
|
|
312
|
+
| `always_on` | Keeps every sector awake: the do-nothing reference | — |
|
|
313
|
+
| `dwave_anneal`, `tabu`, `steepest` | D-Wave's classical samplers on the QUBO | `[quantum]` |
|
|
314
|
+
| `qubo_exact` | Checks every combination of the QUBO core | `[quantum]` |
|
|
315
|
+
| `qaoa`, `qaoa_noisy` | QAOA circuits in Qiskit's simulator (ideal and noisy) | `[quantum]` |
|
|
316
|
+
|
|
317
|
+
**QUBO** is the scheduling problem rewritten as one formula over on/off
|
|
318
|
+
variables, the input format quantum annealers and QAOA accept. A full
|
|
319
|
+
network has too many sectors to simulate on a quantum computer, so `greedy`
|
|
320
|
+
fixes the sectors it is sure about and the quantum-style solvers decide only
|
|
321
|
+
the 12 to 16 it is least sure about (the *core*). They return suggested
|
|
322
|
+
schedules only; the shared scoring grades them, so no method can look better
|
|
323
|
+
by being marked differently.
|
|
324
|
+
|
|
325
|
+
When `qaoa` runs, the first test interval is also saved as
|
|
326
|
+
`hardware_job.json`, which holds everything a real quantum computer needs. It
|
|
327
|
+
is marked `PREPARED_NOT_SUBMITTED` until someone sends it. When results come
|
|
328
|
+
back, `qellaris hardware-job` turns them into a full schedule.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## The Quantum Algorithm Zoo page
|
|
333
|
+
|
|
334
|
+
The [Quantum Algorithm Zoo](https://quantumalgorithmzoo.org/), maintained by
|
|
335
|
+
Stephen Jordan, catalogues quantum algorithms that are faster than the best
|
|
336
|
+
known classical methods for some problem. `qellaris` lists all 74 entries, on
|
|
337
|
+
the dashboard and from the command line, each with a short description of
|
|
338
|
+
what the algorithm does and how. Each one is also rated for where it could fit
|
|
339
|
+
in this benchmark:
|
|
340
|
+
|
|
341
|
+
| Rating | Count | Meaning | Examples |
|
|
342
|
+
| --- | --- | --- | --- |
|
|
343
|
+
| Wired in | 1 | Already runs here, on a simulator | QAOA |
|
|
344
|
+
| Direct fit | 9 | Could choose which sectors sleep | Grover search, quantum annealing, quantum-walk simulated annealing, thermal-state sampling, quantum backtracking, SDP bounds, DQI |
|
|
345
|
+
| Adjacent | 13 | Could help forecasting, training or checking results | linear systems (HHL), quantum ML, gradient estimation, network flows |
|
|
346
|
+
| Not applicable | 51 | Solves a problem not in this benchmark | Shor's factoring, knot invariants, hidden subgroups |
|
|
347
|
+
|
|
348
|
+
Two more views explain how to add one, and in which benchmark scenarios it
|
|
349
|
+
could make a difference. Examples are demand spikes, proving a schedule is
|
|
350
|
+
optimal, and larger problems on real hardware.
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
qellaris zoo # the whole catalogue with descriptions
|
|
354
|
+
qellaris zoo --fit direct # only the ones that could produce a schedule
|
|
355
|
+
qellaris zoo --search annealing
|
|
356
|
+
qellaris zoo --guide # how to plug one in + which scenario to use
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
from qellaris import quantum_zoo
|
|
361
|
+
|
|
362
|
+
for row in quantum_zoo.by_fit("direct"):
|
|
363
|
+
print(row["name"], "-", row["description"])
|
|
364
|
+
print(" fits at:", row["hook"])
|
|
365
|
+
print(" how:", row["how"])
|
|
366
|
+
print(" when:", row["when"])
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Adding a new schedule-producing algorithm means one subclass with one method:
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
from qellaris.quantum import QUANTUM_SOLVERS, QuboBackendSolver
|
|
373
|
+
from qellaris.solvers import SolverProvenance
|
|
374
|
+
|
|
375
|
+
class MyQuantumSolver(QuboBackendSolver):
|
|
376
|
+
provenance = SolverProvenance("my_quantum", "quantum", "simulator",
|
|
377
|
+
"what it is, for the results table")
|
|
378
|
+
core_size = 14
|
|
379
|
+
|
|
380
|
+
def available(self):
|
|
381
|
+
return True
|
|
382
|
+
|
|
383
|
+
def sample(self, reduced):
|
|
384
|
+
# `reduced` is the QUBO for the core. Return on/off lists, best first.
|
|
385
|
+
return my_algorithm(reduced)
|
|
386
|
+
|
|
387
|
+
QUANTUM_SOLVERS["my_quantum"] = MyQuantumSolver # now usable in --solvers
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
The zoo's speedups describe how each algorithm scales on very large versions
|
|
391
|
+
of its own problem. None was measured on this benchmark.
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Study areas and region packages
|
|
396
|
+
|
|
397
|
+
The **Kensington** (London) study area ships inside the package and works
|
|
398
|
+
offline. Any other area can be downloaded from OpenStreetMap by name or by
|
|
399
|
+
bounding box:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
qellaris region kreuzberg --place "Kreuzberg, Berlin" --radius-km 1.0
|
|
403
|
+
qellaris region nottingham --bbox 52.938,-1.172,52.966,-1.125
|
|
404
|
+
qellaris benchmark --geography workspace/regions/kreuzberg
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
A download saves streets, land use, building footprints and mapped masts,
|
|
408
|
+
plus a manifest counting what arrived, so an empty layer is visible rather
|
|
409
|
+
than silent. Areas wider than 12 km are refused before any request is made.
|
|
410
|
+
|
|
411
|
+
Downloads take minutes, so a finished area can be passed on as a single
|
|
412
|
+
**region package**:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
qellaris region-pack list # what you have, and whether it is complete
|
|
416
|
+
qellaris region-pack export kreuzberg -o kreuzberg.zip
|
|
417
|
+
qellaris region-pack import kreuzberg.zip # on another machine
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Importing accepts only the known map-layer files, refuses paths that would
|
|
421
|
+
land outside the regions folder, and never runs anything inside the package.
|
|
422
|
+
The dashboard's Study area page offers the same three actions with buttons.
|
|
423
|
+
|
|
424
|
+
The map shapes the simulation: the land use found in the area sets the mix of
|
|
425
|
+
residents, commuters, students and visitors, and decides which demand events
|
|
426
|
+
are possible. An area with no stadium gets no match day.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## How a schedule is scored
|
|
431
|
+
|
|
432
|
+
Energy uses the EARTH base-station power model (Auer et al., 2011). Each
|
|
433
|
+
transceiver chain draws 130 W when awake and idle, plus 4.7 W for every watt
|
|
434
|
+
radiated (up to 20 W), and 75 W asleep. A 2x2 MIMO sector therefore draws
|
|
435
|
+
260 W idle, 448 W at full load and 150 W asleep. A sleeping sector is not
|
|
436
|
+
free, and that is counted.
|
|
437
|
+
|
|
438
|
+
Each schedule is scored by:
|
|
439
|
+
|
|
440
|
+
* routing demand to awake sectors with a capacity-limited maximum flow;
|
|
441
|
+
* totalling energy in watt-hours, load-dependent;
|
|
442
|
+
* adding a price for unserved demand (550 Wh per unit), a larger price for
|
|
443
|
+
falling below 95% service in critical areas such as hospitals (1,100 Wh),
|
|
444
|
+
and a small price per sector switched on or off (1.375 Wh).
|
|
445
|
+
|
|
446
|
+
Lower is better. The prices are policy choices, stated openly, not
|
|
447
|
+
measurements. Each person's service comes from the same flow calculation, so
|
|
448
|
+
the dashboard's "people served" always matches the solver's "demand served".
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Fair comparisons
|
|
453
|
+
|
|
454
|
+
* **Paired seeds.** Within a seed every solver faces exactly the same people
|
|
455
|
+
and demand, so differences are measured seed by seed. That removes the
|
|
456
|
+
large seed-to-seed variation that would otherwise hide real differences.
|
|
457
|
+
`--seeds` reports bootstrap confidence intervals for each paired
|
|
458
|
+
difference.
|
|
459
|
+
* **Held-out events.** A demand spike the forecaster never saw runs beside
|
|
460
|
+
an identical run without it, so you can see where robustness breaks.
|
|
461
|
+
* **Separate regimes.** Service is reported separately for quiet periods and
|
|
462
|
+
the event, so an overloaded event cell cannot hide differences elsewhere.
|
|
463
|
+
* **Switching and equity** are reported next to energy, so a saving cannot
|
|
464
|
+
hide as constant sector flapping or as one neglected group.
|
|
465
|
+
* **Honest proofs.** `exact` reports `proven_optimal` per interval rather
|
|
466
|
+
than implying a proof it ran out of time to finish.
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## Equity
|
|
471
|
+
|
|
472
|
+
Fairness is built into the allocation and the audit, not added as a chart at
|
|
473
|
+
the end. Critical areas (healthcare, for example) get a 95% service floor.
|
|
474
|
+
When capacity runs short inside an area, people with more constraints are
|
|
475
|
+
served first. The audit reports demand-weighted service gaps across synthetic
|
|
476
|
+
groups and attributes each gap to either the network or the energy-saving
|
|
477
|
+
schedule.
|
|
478
|
+
|
|
479
|
+
`qellaris equity` compares twelve named fairness rules on one identical
|
|
480
|
+
population, from `neutral` (no one prioritised) through `headcount` and
|
|
481
|
+
`volume_first` to `maximum_protection`.
|
|
482
|
+
|
|
483
|
+
All attributes are synthetic scenario variables, not data about real people.
|
|
484
|
+
Gender, ethnicity and language are not modelled. Income band is recorded for
|
|
485
|
+
audit only and never changes behaviour.
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Data, licences and limits
|
|
490
|
+
|
|
491
|
+
* **Code:** MIT licence.
|
|
492
|
+
* **Bundled Kensington map:** © OpenStreetMap contributors, Open Database
|
|
493
|
+
Licence (ODbL). Downloaded areas carry the same attribution.
|
|
494
|
+
* **Optional datasets:** OpenCellID (needs a free key) for approximate cell
|
|
495
|
+
sites; JRC NetBravo (DOI 10.2905/JRC.YA8MHB0) for observed signal strength,
|
|
496
|
+
used only to calibrate, never relabelled as observed demand.
|
|
497
|
+
* **Synthetic by design:** demand, people, demographics, sector capacities,
|
|
498
|
+
directions and bands are modelled unless you supply a real inventory. Do
|
|
499
|
+
not describe results as observed operator traffic.
|
|
500
|
+
* **Single operator:** the benchmark models one network; there is no
|
|
501
|
+
competition between operators.
|
|
502
|
+
* **Not a placement tool:** tower positions and sector directions are inputs
|
|
503
|
+
(automatic or your JSON file), not something the benchmark optimises.
|
|
504
|
+
|
|
505
|
+
The test suite ships inside the package as `qellaris.tests`.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Publishing `qellaris` to PyPI
|
|
2
|
+
|
|
3
|
+
Python packages are published on **PyPI** (<https://pypi.org>), the Python
|
|
4
|
+
Package Index run by the Python Software Foundation. python.org itself does
|
|
5
|
+
not host packages. Once an upload goes through, anyone can run
|
|
6
|
+
`pip install qellaris`.
|
|
7
|
+
|
|
8
|
+
The name `qellaris` was unclaimed on PyPI on 2026-10-17. The first upload
|
|
9
|
+
claims it for your account.
|
|
10
|
+
|
|
11
|
+
## What is already set up
|
|
12
|
+
|
|
13
|
+
| File | Purpose |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `pyproject.toml` | Name, version (0.5.0), dependencies, `[quantum]`/`[dev]` extras, console scripts, classifiers, project URLs, licence |
|
|
16
|
+
| `PYPI_README.md` | The project page PyPI shows (`readme =` in `pyproject.toml`) |
|
|
17
|
+
| `LICENSE` | MIT, plus the ODbL notice for the bundled OSM extracts |
|
|
18
|
+
| `MANIFEST.in` | What goes into the source archive (sdist) |
|
|
19
|
+
| `src/qellaris/_version.py` | Runtime `qellaris.__version__`. Keep it equal to `pyproject.toml` |
|
|
20
|
+
|
|
21
|
+
## 1. One-time account setup
|
|
22
|
+
|
|
23
|
+
1. Create accounts on <https://pypi.org/account/register/> and on the test
|
|
24
|
+
index <https://test.pypi.org/account/register/>. They are separate.
|
|
25
|
+
2. Turn on two-factor authentication. PyPI requires it before you can upload.
|
|
26
|
+
3. Create an API token under *Account settings → API tokens*. The first token
|
|
27
|
+
has to be "entire account" scope because the project does not exist yet.
|
|
28
|
+
After the first upload, replace it with a token scoped to `qellaris`.
|
|
29
|
+
4. Do the same on TestPyPI.
|
|
30
|
+
|
|
31
|
+
Never commit a token. Either let twine prompt for it (username `__token__`,
|
|
32
|
+
password = the token), or set it for the current shell only:
|
|
33
|
+
|
|
34
|
+
```powershell
|
|
35
|
+
$env:TWINE_USERNAME = "__token__"
|
|
36
|
+
$env:TWINE_PASSWORD = "<paste token here>"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 2. Run the tests
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
python -m pip install -e ".[dev]"
|
|
43
|
+
python -m pytest
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 3. Build
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue
|
|
50
|
+
python -m build
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This writes `dist/qellaris-0.5.0-py3-none-any.whl` and
|
|
54
|
+
`dist/qellaris-0.5.0.tar.gz`.
|
|
55
|
+
|
|
56
|
+
## 4. Check the build
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
python -m twine check dist/*
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Both files must say `PASSED`. This checks that `PYPI_README.md` will render
|
|
63
|
+
on the project page.
|
|
64
|
+
|
|
65
|
+
Then smoke-test the wheel in a clean environment:
|
|
66
|
+
|
|
67
|
+
```powershell
|
|
68
|
+
python -m venv $env:TEMP\qellaris-smoke
|
|
69
|
+
& $env:TEMP\qellaris-smoke\Scripts\python.exe -m pip install dist\qellaris-0.5.0-py3-none-any.whl
|
|
70
|
+
& $env:TEMP\qellaris-smoke\Scripts\qellaris.exe zoo --fit wired
|
|
71
|
+
& $env:TEMP\qellaris-smoke\Scripts\qellaris.exe benchmark --list-backends
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 5. Rehearse on TestPyPI
|
|
75
|
+
|
|
76
|
+
```powershell
|
|
77
|
+
python -m twine upload --repository testpypi dist/*
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Install it back from there. The extra index lets pip find Mesa, Solara and
|
|
81
|
+
the other dependencies, which TestPyPI does not host:
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ qellaris
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 6. Publish for real
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
python -m twine upload dist/*
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
You can't overwrite or reuse a version number on PyPI, even after deleting
|
|
94
|
+
it. If something is wrong, bump the version and upload again.
|
|
95
|
+
|
|
96
|
+
## Releasing a new version
|
|
97
|
+
|
|
98
|
+
1. Bump `version` in `pyproject.toml` **and** `__version__` in
|
|
99
|
+
`src/qellaris/_version.py`.
|
|
100
|
+
2. Repeat steps 2–6.
|
|
101
|
+
3. Tag the commit: `git tag v0.5.1` and `git push --tags`.
|
|
102
|
+
|
|
103
|
+
## Optional: publish from GitHub without a token
|
|
104
|
+
|
|
105
|
+
PyPI's *Trusted Publishing* lets a GitHub Actions workflow upload with no
|
|
106
|
+
stored secret. On PyPI, open *Your projects → qellaris → Publishing* and add
|
|
107
|
+
the repository `newguy1234/qaio-cellular-benchmark` with the workflow file
|
|
108
|
+
name. Then add `.github/workflows/publish.yml`:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
name: publish
|
|
112
|
+
on:
|
|
113
|
+
release:
|
|
114
|
+
types: [published]
|
|
115
|
+
jobs:
|
|
116
|
+
pypi:
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
environment: pypi
|
|
119
|
+
permissions:
|
|
120
|
+
id-token: write
|
|
121
|
+
steps:
|
|
122
|
+
- uses: actions/checkout@v4
|
|
123
|
+
- uses: actions/setup-python@v5
|
|
124
|
+
with:
|
|
125
|
+
python-version: "3.12"
|
|
126
|
+
- run: python -m pip install build
|
|
127
|
+
- run: python -m build
|
|
128
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Publishing a GitHub release then uploads that version.
|
|
132
|
+
|
|
133
|
+
## Before the first upload, check
|
|
134
|
+
|
|
135
|
+
- [ ] The author in `pyproject.toml` (`Qellaris benchmark team`) and the
|
|
136
|
+
copyright line in `LICENSE` are how you want to be credited publicly.
|
|
137
|
+
- [ ] The GitHub URLs in `[project.urls]` point at the public repository.
|
|
138
|
+
- [ ] `python -m twine check dist/*` passes.
|
|
139
|
+
- [ ] The wheel is a reasonable size. It carries the bundled Kensington
|
|
140
|
+
OSM extracts, and PyPI's default limit is 100 MB per file.
|