Metadata-Version: 2.4
Name: qellaris
Version: 0.5.0
Summary: Decision-focused evaluation harness for cellular sleep control: synthetic demand, interchangeable forecasters/controllers, shared capacity-flow scoring.
Author: Qellaris benchmark team
License-Expression: MIT
Project-URL: Quantum Algorithm Zoo, https://quantumalgorithmzoo.org/
Keywords: cellular networks,energy saving,cell sleep,benchmark,qubo,qaoa,quantum optimisation,reinforcement learning,mesa,agent-based model,forecasting
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Topic :: System :: Networking
Classifier: Framework :: Jupyter
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mesa<4,>=3.3
Requires-Dist: solara<2,>=1.61
Requires-Dist: matplotlib<5,>=3.9
Requires-Dist: numpy<3,>=2
Requires-Dist: ipyvuetify<3
Requires-Dist: ipyvue<2
Requires-Dist: networkx<4,>=3.4
Requires-Dist: altair<7,>=5
Requires-Dist: gymnasium<2,>=1.0
Provides-Extra: quantum
Requires-Dist: dimod>=0.12; extra == "quantum"
Requires-Dist: dwave-samplers>=1.3; extra == "quantum"
Requires-Dist: qiskit>=1.0; extra == "quantum"
Requires-Dist: qiskit-aer>=0.15; extra == "quantum"
Requires-Dist: scipy>=1.11; extra == "quantum"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=6; extra == "dev"
Dynamic: license-file

# 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.

```bash
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

```bash
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

```bash
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:

1. Open the dashboard
2. Run the benchmark (quick look, all solvers, or five seeds)
3. Browse the Quantum Algorithm Zoo
4. Download a study area from OpenStreetMap
5. Share or import study areas as `.zip` packages
6. Worked examples (write your own agent)
7. Which solvers are installed?

Or go straight to it:

```bash
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:

```bash
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 as `park_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:

```python
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:

```python
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](https://quantumalgorithmzoo.org/), 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.

```bash
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
```

```python
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:

```python
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:

```bash
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**:

```bash
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.
  `--seeds` reports 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.** `exact` reports `proven_optimal` per 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`.
