Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions .dockerignore

This file was deleted.

56 changes: 1 addition & 55 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,65 +20,11 @@ jobs:
python -m pip install --upgrade pip
pip install -e ".[dev]"

- name: Generate gRPC stubs
run: bash scripts/gen_proto.sh

- name: Ruff (lint)
run: ruff check .

- name: Mypy (type-check)
run: mypy contrail_env service
run: mypy contrail_env

- name: Pytest
run: pytest -q

docker-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build solver image
run: docker build -t contrail-solver:ci .

- name: Smoke-test the server boots and accepts connections
run: |
docker run -d --name solver contrail-solver:ci
ok=0
for _ in $(seq 1 30); do
if docker exec solver python -c \
"import socket; socket.create_connection(('localhost', 50051), 2).close()"; then
echo "solver is up and accepting connections"; ok=1; break
fi
sleep 1
done
docker logs solver
docker rm -f solver
test "$ok" = 1

docker-publish:
runs-on: ubuntu-latest
# Only publish on pushes to main — not on pull requests.
# The full suite AND the image smoke-test must pass first.
needs: [lint-type-test, docker-build]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write # needed to push to ghcr.io
steps:
- uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push image to ghcr.io
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/contrail-solver:latest
ghcr.io/${{ github.repository_owner }}/contrail-solver:${{ github.sha }}
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,5 @@ build/
.mypy_cache/
.ruff_cache/

# Generated gRPC stubs (regenerated by scripts/gen_proto.sh)
service/generated/

# Benchmark output
results.csv
79 changes: 0 additions & 79 deletions Dockerfile

This file was deleted.

89 changes: 22 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,86 +7,41 @@ subject to one option per flight, pairwise contrail conflicts, and sector-capaci
limits. The problem is built on a synthetic airspace, encoded as a QUBO, and solved
three ways:

- **CP-SAT** (OR-Tools) — the classical ground-truth verifier, behind a gRPC service;
- **CP-SAT** (OR-Tools) — the classical ground-truth verifier;
- **Pasqal analog-QAOA** — a hand-coded adiabatic Ω(t), δ(t) schedule on the Rydberg
blockade Hamiltonian, tuned by Bayesian optimization (`contrail_env/pasqal_analog.py`);
- **Xanadu GBS** — Gaussian Boson Sampling of the Takagi-decomposed, WAW-weighted
complement graph (`contrail_env/xanadu_gbs.py`).

A PyQt6 dashboard drives the service, streams solver progress over ZMQ, and runs the
head-to-head benchmark (approximation ratio vs the CP-SAT optimum with bootstrap CIs,
raw feasibility rate, wall clock).
A PyQt6 dashboard builds scenarios, solves them **in-process** with a live
convergence curve, and runs the head-to-head benchmark (approximation ratio vs the
CP-SAT optimum with bootstrap CIs, raw feasibility rate, wall clock). It's a single
desktop process — no server, no broker.

The quantum pipelines need no quantum SDKs: each ships a dependency-free, physically
faithful backend (a split-operator state-vector simulator of the Rydberg Hamiltonian;
an exact Metropolis–Hastings sampler of the GBS distribution P(S) ∝ |Haf(B_S)|²).
Installing `pip install -e ".[quantum]"` switches them to Pulser's QuTiP emulator and
Strawberry Fields' gaussian backend automatically.

## Install
## Install & run

```
pip install -e . # core: the headless solver service
pip install -e ".[gui]" # add the PyQt6 desktop dashboard
bash scripts/gen_proto.sh # Windows: .\scripts\gen_proto.ps1
pip install -e ".[gui]" # core + the PyQt6 desktop dashboard
python gui/app.py # launch the dashboard
```

Requires Python 3.11+. Core deps are just the solver runtime (numpy, OR-Tools,
gRPC, ZMQ); the Qt/OpenGL dashboard stack lives in the `gui` extra so the
deployed server stays lean. The gRPC stubs are generated from
`service/proto/solver.proto`, not committed.
Requires Python 3.11+. Core deps are just the solver runtime (numpy, OR-Tools); the
Qt/OpenGL dashboard stack lives in the `gui` extra, and the optional `[quantum]`
SDKs in `[quantum]`.

## Run

```
python -m service.server # gRPC solver on localhost:50051
python gui/app.py # dashboard, in a second terminal ([gui] extra)
```

## Docker

The solver service is containerized (the headless gRPC server only — not the
desktop GUI). The bind host is read from the environment, so the container
binds `0.0.0.0` while local runs default to `localhost`.

### Pull the pre-built image (easiest)

A Docker image is published to the GitHub Container Registry on every push to
main. No cloning or building required — just Docker Desktop installed.

```
docker pull ghcr.io/jaewonyun1234/contrail-solver:latest
docker run --rm -p 50051:50051 -p 5556:5556 ghcr.io/jaewonyun1234/contrail-solver:latest
```

Then run the dashboard on your machine:

```
pip install -e ".[gui]"
python gui/app.py
```

The dashboard connects to the solver on `localhost:50051` automatically.

### Build locally from source

```
docker compose up --build # build + run; gRPC on :50051, progress on :5556
# or, without compose:
docker build -t contrail-solver .
docker run --rm -p 50051:50051 -p 5556:5556 contrail-solver
```

CI builds the image, smoke-tests that the server boots, and publishes it to
ghcr.io on every push to main.

The dashboard has six tabs: live CP-SAT convergence (over ZMQ), the conflict-graph
topology, QUBO matrix statistics (size, sparsity, penalty constants), the
chosen-option trade-offs, the quantum benchmark (CP-SAT vs Pasqal vs Xanadu over
The dashboard has six tabs: live CP-SAT convergence (objective vs improvement), the
conflict-graph topology, QUBO matrix statistics (size, sparsity, penalty constants),
the chosen-option trade-offs, the quantum benchmark (CP-SAT vs Pasqal vs Xanadu over
N seeds, with live convergence curves for the BO loop and the GBS sampler), and a
geographic map — the ISSR risk as a marker overlay on a real Plotly
`geo` basemap (country borders / coastlines, drawn with SVG and bundled offline
vectors, so it needs no WebGL or network) with the chosen vs context routes on top.
geographic map — the ISSR risk as a marker overlay on a real Plotly `geo` basemap
(country borders / coastlines, drawn with SVG and bundled offline vectors, so it
needs no WebGL or network) with the chosen vs context routes animated on top.

The benchmark also runs headless:

Expand All @@ -107,19 +62,19 @@ pluggable without touching `World` or the QUBO assembly.
```
contrail_env/ synthetic environment, ISSR field, geo anchor, QUBO assembly,
CP-SAT solver, quantum pipelines (pasqal_analog, xanadu_gbs,
quantum_common, bayes_opt) and the benchmark protocol (benchmark.py)
service/ gRPC service, ZMQ progress streaming, client
gui/ PyQt6 dashboard
quantum_common, bayes_opt), the scenario builder (scenario.py),
and the benchmark protocol (benchmark.py)
gui/ PyQt6 dashboard (builds + solves scenarios in-process)
tests/ environment build, CP-SAT vs brute-force, quantum solvers vs
brute-force, benchmark round-trip, gRPC round-trip, GUI map panel
brute-force, benchmark round-trip, GUI map panel
```

## Development

```
pip install -e ".[dev]"
ruff check .
mypy contrail_env service
mypy contrail_env
pytest
```

Expand Down
6 changes: 3 additions & 3 deletions contrail_env/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
with bootstrap 95% confidence intervals on the approximation ratio.

The scenario is supplied as a factory `seed -> (evals, conflicts, buckets)`
so this module stays decoupled from the gRPC layer: the GUI passes a
factory built from its ScenarioConfig; the CLI below builds one from
contrail_env defaults.
so this module stays decoupled from any caller: the GUI passes a factory
built from its ScenarioConfig; the CLI below builds one from contrail_env
defaults.
"""

from __future__ import annotations
Expand Down
Loading
Loading