Tutorials
Diese Tutorials sind lern-orientierte End-zu-End-Durchläufe: jeder Schritt ist ein
ausführbares Skript, jede gezeigte Ausgabe ist echte Ausgabe aus den gebündelten
Smoke-Daten, und jeder Lauf ist deterministisch — dieselben Seeds erzeugen dieselben Zahlen
auf Ihrer Maschine. Schließen Sie zuerst die Installation und
den Schnellstart ab, dann führen Sie jedes Skript aus dem
Repository-Checkout mit uv run python <file>.py aus.
🧭 Wählen Sie Ein Tutorial
| Tutorial | Sie lernen | Berührte Pakete |
|---|---|---|
| Ein erstes Dispatch-Problem modellieren und lösen | Eine Benchmark-Instanz wählen, zwei Solver darauf ausführen, Zeitpläne vergleichen, und Solver-Metadaten lesen. | dispatchatlas.core, dispatchatlas.bench, dispatchatlas.solve |
| Eine kleine Kampagne ausführen und ihre Ergebnisse lesen | Eine checkpoint-gestützte Kampagne konfigurieren, sie in einen Workspace ausführen, und die Ergebnisse zur Analyse laden. | dispatchatlas.lab, dispatchatlas.analytica |
Tiefere Referenzpfade setzen fort, wo die Tutorials enden: Domänenverträge für das Planungsmodell, Benchmark-Modell für die Katalog-Generierung, Solver-System für das vollständige Registry, Kampagnen-Engine für das Orchestrierungs-Detail, und Analyse-Exporte für Belegpakete. Größere Kampagnen erfordern die statistischen und Release-Gates, die in Bereitschaft beschrieben sind.
🛠️ Tutorial 1: Ein Erstes Dispatch-Problem Modellieren Und Lösen
Der Schnellstart löste ein Problem mit einem Solver. Dieses Tutorial geht eine Ebene tiefer: Sie wählen eine spezifische Benchmark-Instanz, führen zwei Dispatching-Baselines darauf aus, vergleichen die Zeitpläne, die sie bauen, und lesen die Metadaten, die jeden Solver erklären.
1️⃣ Sehen, was der Smoke-Katalog bietet
Der gebündelte Smoke-Katalog materialisiert zwei kleine deterministische Instanzen pro
Planungsfamilie aus einem einzigen Wurzel-Seed. Speichern Sie dies als list_problems.py und
führen Sie es mit uv run python list_problems.py aus:
from dispatchatlas.bench import smoke_benchmark_provider
provider = smoke_benchmark_provider(root_seed=20260527)
for problem_id in provider.list_problem_ids():
print(problem_id.value)Ausgabe:
smoke-cloud-edge-0
smoke-cloud-edge-1
smoke-workflow-0
smoke-workflow-1
smoke-machine-scheduling-unrelated-0
smoke-machine-scheduling-unrelated-1
smoke-job-shop-0
smoke-job-shop-1
smoke-flexible-job-shop-0
smoke-flexible-job-shop-1
smoke-permutation-flow-shop-0
smoke-permutation-flow-shop-1
smoke-setup-flow-shop-0
smoke-setup-flow-shop-1
smoke-rcpsp-renewable-0
smoke-rcpsp-renewable-1
smoke-open-shop-0
smoke-open-shop-1
smoke-hybrid-flow-shop-0
smoke-hybrid-flow-shop-1
smoke-distributed-permutation-flow-shop-0
smoke-distributed-permutation-flow-shop-1
smoke-no-wait-flow-shop-0
smoke-no-wait-flow-shop-1
smoke-blocking-flow-shop-0
smoke-blocking-flow-shop-1
smoke-distributed-assembly-flow-shop-0
smoke-distributed-assembly-flow-shop-1
smoke-multi-objective-pfsp-0
smoke-multi-objective-pfsp-1
smoke-rcpsp-max-0
smoke-rcpsp-max-1
smoke-rcpsp-multi-mode-0
smoke-rcpsp-multi-mode-1
smoke-multi-project-rcpsp-0
smoke-multi-project-rcpsp-1
smoke-unrelated-parallel-setup-0
smoke-unrelated-parallel-setup-1
smoke-reentrant-fab-0
smoke-reentrant-fab-1
smoke-distributed-flexible-job-shop-0
smoke-distributed-flexible-job-shop-1
smoke-facility-assignment-0
smoke-facility-assignment-1Jede id nennt ihre Planungsfamilie und einen null-basierten
Instanz-Index. Der Rest des Tutorials nutzt smoke-job-shop-0.
2️⃣ Zwei Solver auf einer Instanz ausführen
provider.get_problem nimmt eine ProblemId und gibt ein ValidatedProblem zurück — die
Problem-Spezifikation plus ihren Validierungsstempel und -bericht, sodass ein Solver niemals
eine unvalidierte Instanz empfängt. Die zwei Solver unten sind deterministische
Dispatching-Baselines: earliest-start plant Aufgaben in topologischer Eingabereihenfolge,
während shortest-processing-time kürzere Aufgaben priorisiert. Speichern Sie dies als
compare_solvers.py:
from dispatchatlas.bench import smoke_benchmark_provider
from dispatchatlas.core import ProblemId, TerminationPolicy, derive_seed
from dispatchatlas.solve import default_solver_registry
provider = smoke_benchmark_provider(root_seed=20260527)
validated = provider.get_problem(ProblemId("smoke-job-shop-0"))
spec = validated.spec
print(f"problem: {spec.id.value}")
print(f"tasks: {len(spec.tasks)} resources: {len(spec.resources)}")
registry = default_solver_registry()
for solver_id in ("earliest-start", "shortest-processing-time"):
metadata = registry.get_metadata(solver_id)
solver = registry.create(solver_id)
run = solver.solve(
problem=validated,
stop=TerminationPolicy(max_iterations=metadata.default_stop.max_iterations),
seed=derive_seed(20260610, "docs.tutorial.compare", 0),
)
makespan = run.result.objective_values[0]
print(
f"{solver_id}: feasible={run.result.feasible} "
f"{makespan.objective_name}={makespan.value:.1f}"
)Führen Sie es mit uv run python compare_solvers.py aus:
problem: smoke-job-shop-0
tasks: 9 resources: 3
earliest-start: feasible=True makespan=353.0
shortest-processing-time: feasible=True makespan=433.0Beide Zeitpläne sind machbar, und auf dieser Instanz gewinnt die schlichtere Regel: earliest-start endet bei 353.0, während shortest-processing-time, das kürzere Aufgaben bevorzugt, erst bei 433.0 endet. Dabei lohnt es sich zu verweilen, denn shortest-processing-time ist im Allgemeinen eine gute Regel — im Job Shop kann sie eine lange Aufgabe zurückstellen, auf die eine nachgelagerte Aufgabe wartet, und der ganze Zeitplan wartet mit. Der Ruf einer Regel sagt Ihnen nicht, was sie auf Ihrer Instanz tut.
Und genau darum geht es um die Form und nicht um die Zahl. Eine Instanz beweist in keine Richtung etwas — dafür sind die Kampagnen und die statistischen Methoden da — aber der Vergleich hier (gleiches validiertes Problem, gleiche Stoppkriterien, gleicher abgeleiteter Seed) ist genau, wie größerer Beleg gebaut wird, und ein einzelner Lauf ist genau das nicht.
3️⃣ Die Solver-Metadaten lesen
Jeder registrierte Solver trägt Metadaten als seinen öffentlichen Vertrag: unterstützte
Ziele, Fähigkeits-Tags, Stochastizität, Standard-Stoppkriterien, und eine kanonische
Zitation. Speichern Sie dies als inspect_metadata.py:
from dispatchatlas.solve import default_solver_registry
registry = default_solver_registry()
metadata = registry.get_metadata("shortest-processing-time")
print(f"solver: {metadata.solver_id}")
print(f"stochasticity: {metadata.stochasticity}")
print(f"citation: {metadata.citation.reference}")
print("capabilities:", ", ".join(c.value for c in metadata.capabilities))Führen Sie es mit uv run python inspect_metadata.py aus:
solver: shortest-processing-time
stochasticity: deterministic
citation: Smith, W. E. (1956). Various optimizers for single-stage production. Naval Research Logistics Quarterly, 3(1-2), 59-66.
capabilities: single-objective, capacity-aware, precedence-aware, constructive, dispatching, deterministicDiese Metadaten sind das, worauf registry.select filtert und woraus der
Solver-Empfehler erklärt. Das vollständige Registry —
Baselines, Metaheuristiken, exakte Adapter, und die NDSO-Familie — ist in
Algorithmen und Solver-System katalogisiert.
🧪 Tutorial 2: Eine Kleine Kampagne Ausführen Und Ihre Ergebnisse Lesen
Eine Kampagne ist eine konfigurierte Menge von Läufen — Benchmark-Instanzen gekreuzt mit
Solvern und Zielen — ausgeführt mit expliziten Seeds, Budgets, Checkpoints, und einem
On-Disk-Ergebnis-Repository. Dieses Tutorial führt dieselbe Form aus wie das verfolgte Rezept
bei experiments/configs/smoke-pilot.json: zwei Smoke-Instanzen, zwei deterministische
Baselines, ein Ziel.
1️⃣ Die Kampagne konfigurieren und ausführen
Speichern Sie dies als first_campaign.py. Es deklariert die Kampagne, validiert sie in
einen deterministischen Plan, schätzt ihre Kosten mit einem Trockenlauf, dann führt es sie in
einen lokalen my-campaigns/-Workspace aus:
from pathlib import Path
from dispatchatlas.core import TerminationPolicy
from dispatchatlas.lab import (
CampaignConfig,
CampaignKind,
CampaignStage,
ExecutionMode,
OutputPolicy,
ResourceBudget,
default_campaign_runner,
)
workspace = Path("my-campaigns")
config = CampaignConfig(
campaign_id="first-campaign",
benchmark_ids=("smoke-job-shop-0", "smoke-workflow-0"),
solver_ids=("earliest-start", "shortest-processing-time"),
objectives=("makespan",),
root_seed=20260610,
seed_namespace="docs.tutorial.first-campaign",
stop=TerminationPolicy(max_iterations=5),
output=OutputPolicy(root_dir=str(workspace)),
stage=CampaignStage.SMOKE,
kind=CampaignKind.PILOT,
resources=ResourceBudget(
max_workers=2,
max_concurrent_runs=2,
estimated_seconds_per_run=0.5,
),
execution_mode=ExecutionMode.SEQUENTIAL,
)
runner = default_campaign_runner(workspace)
plan = runner.validate(config)
budget = runner.dry_run(plan)
print(f"planned runs: {len(plan.runs)}")
print(f"estimated wall time: {budget.estimated_wall_time_seconds:.1f}s")
index = runner.run(plan)
print(f"completed runs: {index.run_count}")
print(f"failed attempts: {len(index.failures)}")Führen Sie es mit uv run python first_campaign.py aus:
planned runs: 4
estimated wall time: 2.0s
completed runs: 4
failed attempts: 0Vier Läufe ist genau das Kreuzprodukt: 2 Benchmark-Instanzen × 2 Solver × 1 Ziel, mit einem
Lauf pro Zelle, weil beide Solver deterministisch sind. Der Seed jedes Laufs leitet sich von
root_seed und der Position des Laufs ab, sodass ein Wieder-Ausführen des Skripts dieselben
Aufzeichnungen reproduziert; der Checkpoint lässt eine unterbrochene Kampagne fortsetzen, ohne
abgeschlossene Arbeit zu wiederholen.
2️⃣ Inspizieren, was auf der Festplatte landete
Der Runner schrieb einen Workspace mit derselben Anordnung wie der verfolgte Experimente-Workspace:
my-campaigns/
.checkpoints/first-campaign.json
ENVIRONMENT.md
logs/first-campaign_<timestamp>.log
results/first-campaign/
plan.json
environment.json
earliest-start/smoke-job-shop-0/run_0.json
earliest-start/smoke-workflow-0/run_0.json
shortest-processing-time/smoke-job-shop-0/run_0.json
shortest-processing-time/smoke-workflow-0/run_0.jsonEine JSON-Aufzeichnung pro Lauf, adressiert nach Kampagne, Solver, Benchmark, und
Replikat-Index — der Pfad selbst ist der Index. Jede Aufzeichnung ist über ihre
deterministische Nutzlast inhalts-gehasht, wogegen der replay-Modus verifiziert.
3️⃣ Die Ergebnisse zur Analyse laden
dispatchatlas.analytica liest abgeschlossene Kampagnen-Verzeichnisse, ohne die
Kampagnen-Laufzeit zu importieren. Speichern Sie dies als read_results.py:
from pathlib import Path
from dispatchatlas.analytica import load_result_dataset, summarize_dataset
dataset = load_result_dataset(Path("my-campaigns"), "first-campaign")
print(f"campaign: {dataset.campaign_id}")
print(f"completed runs: {len(dataset.completed)}")
summary = summarize_dataset(dataset)
for solver in summary.solver_summaries:
print(
f"{solver.solver_id}: runs={solver.count} "
f"feasible={solver.feasible_count} "
f"mean {solver.objective_name}={solver.mean:.1f}"
)Führen Sie es mit uv run python read_results.py aus:
campaign: first-campaign
completed runs: 4
earliest-start: runs=2 feasible=2 mean makespan=182.1
shortest-processing-time: runs=2 feasible=2 mean makespan=222.1summarize_dataset berechnet deskriptive Statistiken pro Solver und Ziel. Mit nur zwei
Läufen pro Solver leiten die inferenziellen Methoden (Signifikanztests, Konfidenzintervalle)
zur Limitationsoberfläche, statt unterbesetzte Ergebnisse zu erzeugen — die
statistische-Power-Untergrenze von 30 unabhängigen Läufen pro stochastischer
Solver-Instanz-Zelle ist in Analyse-Exporte beschrieben.
4️⃣ Ein Belegpaket exportieren (optional)
Dasselbe Kampagnen-Verzeichnis speist den Export-Befehl, der ein offenlegungs-gefiltertes Belegpaket aus Tabellen, Abbildungen, und Supplement schreibt:
uv run dispatchatlas export `
--campaign-dir .\my-campaigns\results\first-campaign `
--target-dir .\exports\first-campaign `
--authorized-output-root .\exports `
--tier coreDas Paket landet bei exports/first-campaign/evidence-bundles/first-campaign-core/. Die
Seite Belegpakete erklärt die vier Stufen und den Paketinhalt.
🎓 Wohin dies führt
- Das verfolgte Rezept
experiments/scripts/run_smoke_pilot.pyführt diese gleiche Form in den Experimente-Workspace aus und serialisiert seine Konfiguration für diedispatchatlas-lab-Kommandozeilen-Schnittstelle. - Die Seite Kampagnen-Engine deckt Ausführungsmodi, Wiederholungsrichtlinie, Wiederaufnahme, Replay-Verifizierung, duale Stopp-Protokolle, und Fair-Vergleich-Garantien ab.
- Vollphasen-Kampagnen bleiben blockiert, bis die Genehmigung des statistischen Designs aufgezeichnet ist — siehe Release-Bereitschaft.