Zum Inhalt springen
DispatchAtlas
Suchen

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

TutorialSie lernenBerührte Pakete
Ein erstes Dispatch-Problem modellieren und lösenEine 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 lesenEine 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-1

Jede 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.0

Beide 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, deterministic

Diese 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: 0

Vier 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.json

Eine 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.1

summarize_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 core

Das 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.py führt diese gleiche Form in den Experimente-Workspace aus und serialisiert seine Konfiguration für die dispatchatlas-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.