qellaris
A benchmark for switching mobile-network sectors off to save energy, scored on what each decision actually costs.
Mobile base stations burn power even when nobody is using them. Putting
lightly used radio sectors to sleep saves energy, but sleep too many and
people go unserved. qellaris is a test bench for methods that make that
call every 15 minutes. It provides synthetic people moving on real
OpenStreetMap streets, interchangeable demand forecasters and controllers,
and one shared scoring rule that grades classical, quantum-inspired and
reinforcement-learning methods in exactly the same way.
pip install qellaris # benchmark, dashboard and command line
pip install "qellaris[quantum]" # + D-Wave samplers and Qiskit (QAOA)
qellaris menu # a guided menu: it asks, then runs the command
Requires Python 3.11 or newer. Works on Windows, macOS and Linux.
What this is not. It makes no claim of quantum advantage, real operator radio accuracy, or a calibrated digital twin of a live network. Demand, people and capacities are synthetic. The quantum solvers are classical simulations of quantum computers; nothing is sent to real quantum hardware unless you submit the exported job yourself.
Contents
Why another benchmark? · Install · Five-minute tour · The dashboard, page by page · Command-line reference · Plug in your own forecaster or controller · Solvers · The Quantum Algorithm Zoo page · Study areas and region packages · How a schedule is scored · Fair comparisons · Equity · Data, licences and limits
Why another benchmark?
Most forecasting papers stop at accuracy, and most optimisation papers assume
the forecast is perfect. In practice a network operator forecasts demand,
commits a schedule, and only then learns how many people actually turned up.
qellaris scores that whole loop, and splits every bad outcome into its two
causes:
solver_gap + forecast_gap = total_decision_regret
- forecast gap: what the forecast's errors cost you, given a perfect optimiser;
- solver gap: what the optimiser cost you, given the same forecast.
All three schedules involved (yours, the reference solver's on your forecast, and the reference solver's on the true demand) start from the same network state and are scored by the same rule. So you can tell whether a forecaster or an optimiser caused a bad result.
This matters because accuracy and good decisions disagree. In the project's own runs, a seasonal forecaster was 5.6 times more accurate than a flat average (by mean absolute error), yet the schedules built on it cost 58% more in forecast gap. The flat average over-predicts in quiet hours, keeps more sectors awake, and so rarely leaves anyone unserved. The benchmark scores the decision, not the prediction.
Install
pip install qellaris
Optional extras:
| Extra | Adds | Install |
|---|---|---|
quantum |
dimod, dwave-samplers, qiskit, qiskit-aer, scipy: the D-Wave and QAOA solvers |
pip install "qellaris[quantum]" |
dev |
pytest, build, twine |
pip install "qellaris[dev]" |
Without the quantum extra the benchmark simply offers fewer solvers.
Downloaded maps and run results are written to a workspace/ folder in the
current directory. Set the QELLARIS_HOME environment variable to put them
somewhere fixed.
Five-minute tour
qellaris menu
The menu asks a few questions, prints the exact command it is about to run, and runs it. Use it to learn the commands, then type them directly. Its options:
- Open the dashboard
- Run the benchmark (quick look, all solvers, or five seeds)
- Browse the Quantum Algorithm Zoo
- Download a study area from OpenStreetMap
- Share or import study areas as
.zippackages - Worked examples (write your own agent)
- Which solvers are installed?
Or go straight to it:
qellaris ui # dashboard at http://127.0.0.1:8765
qellaris benchmark --geography kensington # leaderboard of every installed solver
qellaris template --compare # four example policies side by side
qellaris zoo --fit direct # quantum algorithms that could schedule
python -m qellaris <command> works the same way if your scripts folder is
not on PATH.
The dashboard, page by page
qellaris ui opens an interactive dashboard (built with Mesa and Solara) in
your browser. The left sidebar sets the population size, random seed, demand
event, scheduling solver and map; Step and ▶ run the simulation, and
Reset rebuilds it with new settings. Six named tabs:
Live map. The study area with its demand areas, how busy each one is, radio coverage, every sector (awake or asleep), the simulated people, and which mast serves which area. A toggle colours buildings by the use the model assigns them. Footprints whose OpenStreetMap tags state their use are drawn solid; ones that inherit their area's use are drawn faint. Beside the map: the time of day, the current decision and its costs, and a live solver leaderboard that runs every installed solver on the interval about to be scheduled and ranks them.
Time series. Total and forecast demand over time, the split of decision cost into forecast gap and solver gap, and energy, unserved demand and equity per interval.
Equity audit. Who actually gets served, across twelve synthetic dimensions: age, income band, device, access needs, work pattern, household and more. For each it shows the gap between best- and worst-served groups, which direction the gap runs, and whether the energy-saving schedule caused it. The same interval is replayed with every sector awake for comparison.
Demographics. Who is out there and what they are doing (travelling, at home, at work, out), with no service information at all. It is kept separate from the equity audit so "this group is large and busy" is never confused with "this group is being failed".
Study area. Download a new area by place name or bounding box, see every
area you already have, switch to one with Use (no restart), save any area
as a .zip region package, or drop someone else's package in. Beside it, a
land-use audit shows what the area actually contains and which venue (if any)
hosts the demand event.
Quantum Algorithm Zoo. Every algorithm from the Quantum Algorithm Zoo with a plain-language description, plus a guide to plugging one into the benchmark (see the Quantum Algorithm Zoo section below).
Command-line reference
Everything is one command, qellaris, with subcommands. Each subcommand also
exists as its own script (qellaris-zoo, qellaris-menu, ...).
| Command | What it does |
|---|---|
qellaris menu |
Guided menu over everything below |
qellaris ui [--port 8765] [--no-browser] |
Interactive dashboard |
qellaris benchmark [...] |
Forecast-and-schedule benchmark; prints a leaderboard and writes CSV/JSON results |
qellaris benchmark --list-backends |
Which solvers are installed |
qellaris equity --scenarios protective neutral ... |
Compare fairness rules on one identical population |
qellaris train --episodes 120 |
Train the reinforcement-learning controller |
qellaris template --compare / --forecast / --explain / --broker |
Worked examples for writing your own agent |
qellaris region NAME --place "Camden, London" --radius-km 1 |
Download a study area from OpenStreetMap |
qellaris region-pack list / export / import |
Share study areas as .zip files |
qellaris zoo [--fit direct] [--search WORD] [--guide] |
Quantum Algorithm Zoo catalogue and guide |
qellaris hardware-job JOB.json RESULT.json |
Decode results returned from a real quantum computer |
qellaris opencellid, qellaris netbravo |
Fetch optional real cell-site and signal datasets |
Useful benchmark options:
qellaris benchmark --geography kensington \
--solvers greedy annealing qaoa always_on \
--scenario entertainment_event \
--seeds 17 23 42 101 2027 \
--output workspace/runs/my-run
--geography:kensington(bundled),synthetic(no map), or the path of any downloaded study area--scenario:normal,entertainment_event(best venue the area has), or a specific venue type such aspark_event--seeds: repeat the whole experiment and report paired confidence intervals--point-forecast: schedule on the forecast itself rather than its upper bound--inventory FILE.json: use your own fixed tower and sector layout
Run any command with --help for the full list.
Plug in your own forecaster or controller
No Mesa or Gymnasium subclassing is needed. Both receive plain JSON-safe lists, and two decorators register them:
from qellaris import register_agent, register_forecaster
from qellaris.template import build_env, run_episode
@register_forecaster("last-value")
class LastValue:
def forecast(self, request):
last = {row["zone_id"]: row["demand"] for row in request["records"]}
return [{"step": request["train_steps"] + step, "zone_id": zone,
"prediction": value, "lower": value * 0.8, "upper": value * 1.2}
for zone, value in last.items()
for step in range(request["horizon"])]
@register_agent("busy-sectors")
class BusySectors:
def act(self, state):
sectors, width = state["network_shape"]
load = state["network_vector"]
return [int(load[i * width + 2] > 0.25) for i in range(sectors)] # 1 = awake
result = run_episode(BusySectors(), build_env(days=1))
A controller receives, each interval, the forecast and its upper bound, which
sectors were awake, eight features per sector, and a separate demographic
table. It returns one 0/1 per sector. Its schedule is then scored by the
shared rule. qellaris template --explain prints exactly what arrives.
For reinforcement learning there is a Gymnasium environment:
import gymnasium as gym
import qellaris.control_env # registers the environment
env = gym.make("QaioSectorControl-v0", n_agents=240)
state, info = env.reset(seed=17)
state, reward, terminated, truncated, info = env.step(env.action_space.sample())
The reward is the negative of the same score every solver gets, and info
reports energy, unserved demand and switching separately.
Solvers
| Solver | How it chooses | Needs |
|---|---|---|
greedy |
Builds a schedule sector by sector, then improves it locally | — |
exact |
Branch and bound; reports whether it proved its answer optimal | — |
annealing |
Simulated annealing on the QUBO formula | — |
rl |
A small learned policy (one shared linear model per sector) | — |
always_on |
Keeps every sector awake: the do-nothing reference | — |
dwave_anneal, tabu, steepest |
D-Wave's classical samplers on the QUBO | [quantum] |
qubo_exact |
Checks every combination of the QUBO core | [quantum] |
qaoa, qaoa_noisy |
QAOA circuits in Qiskit's simulator (ideal and noisy) | [quantum] |
QUBO is the scheduling problem rewritten as one formula over on/off
variables, the input format quantum annealers and QAOA accept. A full
network has too many sectors to simulate on a quantum computer, so greedy
fixes the sectors it is sure about and the quantum-style solvers decide only
the 12 to 16 it is least sure about (the core). They return suggested
schedules only; the shared scoring grades them, so no method can look better
by being marked differently.
When qaoa runs, the first test interval is also saved as
hardware_job.json, which holds everything a real quantum computer needs. It
is marked PREPARED_NOT_SUBMITTED until someone sends it. When results come
back, qellaris hardware-job turns them into a full schedule.
The Quantum Algorithm Zoo page
The Quantum Algorithm Zoo, maintained by
Stephen Jordan, catalogues quantum algorithms that are faster than the best
known classical methods for some problem. qellaris lists all 74 entries, on
the dashboard and from the command line, each with a short description of
what the algorithm does and how. Each one is also rated for where it could fit
in this benchmark:
| Rating | Count | Meaning | Examples |
|---|---|---|---|
| Wired in | 1 | Already runs here, on a simulator | QAOA |
| Direct fit | 9 | Could choose which sectors sleep | Grover search, quantum annealing, quantum-walk simulated annealing, thermal-state sampling, quantum backtracking, SDP bounds, DQI |
| Adjacent | 13 | Could help forecasting, training or checking results | linear systems (HHL), quantum ML, gradient estimation, network flows |
| Not applicable | 51 | Solves a problem not in this benchmark | Shor's factoring, knot invariants, hidden subgroups |
Two more views explain how to add one, and in which benchmark scenarios it could make a difference. Examples are demand spikes, proving a schedule is optimal, and larger problems on real hardware.
qellaris zoo # the whole catalogue with descriptions
qellaris zoo --fit direct # only the ones that could produce a schedule
qellaris zoo --search annealing
qellaris zoo --guide # how to plug one in + which scenario to use
from qellaris import quantum_zoo
for row in quantum_zoo.by_fit("direct"):
print(row["name"], "-", row["description"])
print(" fits at:", row["hook"])
print(" how:", row["how"])
print(" when:", row["when"])
Adding a new schedule-producing algorithm means one subclass with one method:
from qellaris.quantum import QUANTUM_SOLVERS, QuboBackendSolver
from qellaris.solvers import SolverProvenance
class MyQuantumSolver(QuboBackendSolver):
provenance = SolverProvenance("my_quantum", "quantum", "simulator",
"what it is, for the results table")
core_size = 14
def available(self):
return True
def sample(self, reduced):
# `reduced` is the QUBO for the core. Return on/off lists, best first.
return my_algorithm(reduced)
QUANTUM_SOLVERS["my_quantum"] = MyQuantumSolver # now usable in --solvers
The zoo's speedups describe how each algorithm scales on very large versions of its own problem. None was measured on this benchmark.
Study areas and region packages
The Kensington (London) study area ships inside the package and works offline. Any other area can be downloaded from OpenStreetMap by name or by bounding box:
qellaris region kreuzberg --place "Kreuzberg, Berlin" --radius-km 1.0
qellaris region nottingham --bbox 52.938,-1.172,52.966,-1.125
qellaris benchmark --geography workspace/regions/kreuzberg
A download saves streets, land use, building footprints and mapped masts, plus a manifest counting what arrived, so an empty layer is visible rather than silent. Areas wider than 12 km are refused before any request is made.
Downloads take minutes, so a finished area can be passed on as a single region package:
qellaris region-pack list # what you have, and whether it is complete
qellaris region-pack export kreuzberg -o kreuzberg.zip
qellaris region-pack import kreuzberg.zip # on another machine
Importing accepts only the known map-layer files, refuses paths that would land outside the regions folder, and never runs anything inside the package. The dashboard's Study area page offers the same three actions with buttons.
The map shapes the simulation: the land use found in the area sets the mix of residents, commuters, students and visitors, and decides which demand events are possible. An area with no stadium gets no match day.
How a schedule is scored
Energy uses the EARTH base-station power model (Auer et al., 2011). Each transceiver chain draws 130 W when awake and idle, plus 4.7 W for every watt radiated (up to 20 W), and 75 W asleep. A 2x2 MIMO sector therefore draws 260 W idle, 448 W at full load and 150 W asleep. A sleeping sector is not free, and that is counted.
Each schedule is scored by:
- routing demand to awake sectors with a capacity-limited maximum flow;
- totalling energy in watt-hours, load-dependent;
- adding a price for unserved demand (550 Wh per unit), a larger price for falling below 95% service in critical areas such as hospitals (1,100 Wh), and a small price per sector switched on or off (1.375 Wh).
Lower is better. The prices are policy choices, stated openly, not measurements. Each person's service comes from the same flow calculation, so the dashboard's "people served" always matches the solver's "demand served".
Fair comparisons
- Paired seeds. Within a seed every solver faces exactly the same people
and demand, so differences are measured seed by seed. That removes the
large seed-to-seed variation that would otherwise hide real differences.
--seedsreports bootstrap confidence intervals for each paired difference. - Held-out events. A demand spike the forecaster never saw runs beside an identical run without it, so you can see where robustness breaks.
- Separate regimes. Service is reported separately for quiet periods and the event, so an overloaded event cell cannot hide differences elsewhere.
- Switching and equity are reported next to energy, so a saving cannot hide as constant sector flapping or as one neglected group.
- Honest proofs.
exactreportsproven_optimalper interval rather than implying a proof it ran out of time to finish.
Equity
Fairness is built into the allocation and the audit, not added as a chart at the end. Critical areas (healthcare, for example) get a 95% service floor. When capacity runs short inside an area, people with more constraints are served first. The audit reports demand-weighted service gaps across synthetic groups and attributes each gap to either the network or the energy-saving schedule.
qellaris equity compares twelve named fairness rules on one identical
population, from neutral (no one prioritised) through headcount and
volume_first to maximum_protection.
All attributes are synthetic scenario variables, not data about real people. Gender, ethnicity and language are not modelled. Income band is recorded for audit only and never changes behaviour.
Data, licences and limits
- Code: MIT licence.
- Bundled Kensington map: © OpenStreetMap contributors, Open Database Licence (ODbL). Downloaded areas carry the same attribution.
- Optional datasets: OpenCellID (needs a free key) for approximate cell sites; JRC NetBravo (DOI 10.2905/JRC.YA8MHB0) for observed signal strength, used only to calibrate, never relabelled as observed demand.
- Synthetic by design: demand, people, demographics, sector capacities, directions and bands are modelled unless you supply a real inventory. Do not describe results as observed operator traffic.
- Single operator: the benchmark models one network; there is no competition between operators.
- Not a placement tool: tower positions and sector directions are inputs (automatic or your JSON file), not something the benchmark optimises.
The test suite ships inside the package as qellaris.tests.
Metadata
Release files for qellaris 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| qellaris-0.5.0.tar.gz | 2.0 MB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qellaris-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.9 MB
Release files / qellaris-0.5.0.tar.gz
| Download URL | qellaris-0.5.0.tar.gz |
|---|---|
| Size | 2.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
00103a6037f77bda915fbd3a3e6ce1ed79b1357aff9d2105cf0f02c1bb18760a
|
|
BLAKE2b-256 checksum How to use checksums |
0b977d19ce0360e274b0a329f6485b500cfe8faac7524756d63ebb7ac59b9e69
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / qellaris-0.5.0-py3-none-any.whl
| Download URL | qellaris-0.5.0-py3-none-any.whl |
|---|---|
| Size | 1.9 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
193d87a0ab53187d41770ea58c984595c571eee4d94ae6ab3092e8824fd7b306
|
|
BLAKE2b-256 checksum How to use checksums |
5931187a5ba0e63508f5c0edbad4313540023848bc42b54cf170e8fa2f081750
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for qellaris 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.