本文へスキップ
DispatchAtlas
検索

チュートリアル

これらのチュートリアルは学習-指向のエンド-ツー-エンドの歩みです:各ステップは実行可能な スクリプトであり、示される各出力はバンドルされたスモークデータからの実際の出力であり、各実行は 決定論的です——同じシードはあなたのマシン上で同じ数を生みます。まず インストールクイックスタートを完了し、それから各スクリプトを リポジトリチェックアウトから uv run python <file>.py で実行してください。

🧭 チュートリアルを選ぶ

チュートリアル学ぶこと触れるパッケージ
最初のディスパッチ問題をモデル化し解くベンチマークインスタンスを選び、その上で2つのソルバーを実行し、スケジュールを比較し、ソルバーメタデータを読む。dispatchatlas.coredispatchatlas.benchdispatchatlas.solve
小さなキャンペーンを実行しその結果を読むチェックポイント付きキャンペーンを構成し、ワークスペースへ実行し、分析のため結果を読み込む。dispatchatlas.labdispatchatlas.analytica

より深い参照経路はチュートリアルが終わるところから続きます: ドメイン契約はスケジューリングモデルのため、 ベンチマークモデルはカタログ生成のため、 ソルバーシステムは完全なレジストリのため、 キャンペーンエンジンはオーケストレーション詳細のため、 分析エクスポートは証拠バンドルのため。より大きなキャンペーンは 準備で記述される統計とリリースのゲートを要します。

🛠️ Tutorial 1:最初のディスパッチ問題をモデル化し解く

クイックスタートは1つのソルバーで1つの問題を解きました。このチュートリアルは一段深く進みます: 特定のベンチマークインスタンスを選び、その上で2つのディスパッチングベースラインを実行し、それらが 構築するスケジュールを比較し、各ソルバーを説明するメタデータを読みます。

1️⃣ スモークカタログが何を提供するか見る

バンドルされたスモークカタログは単一のルートシードからスケジューリングファミリごとに2つの小さな 決定論的インスタンスを具現化します。これを list_problems.py として保存し、 uv run python list_problems.py で実行してください:

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)

出力:

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

各 id はそのスケジューリングファミリとゼロ-基準のインスタンス インデックスを命名します。チュートリアルの残りは smoke-job-shop-0 を使います。

2️⃣ 1つのインスタンス上で2つのソルバーを実行する

provider.get_problemProblemId を取り ValidatedProblem を返します——問題仕様にその 検証スタンプとレポートを加えたもので、ソルバーが未-検証のインスタンスを決して受け取らないように します。下の2つのソルバーは決定論的ディスパッチングベースラインです:earliest-start は トポロジカル入力順序でタスクをスケジュールし、shortest-processing-time はより短いタスクを 優先します。これを 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}"
    )

uv run python compare_solvers.py で実行:

problem: smoke-job-shop-0
tasks: 9  resources: 3
earliest-start: feasible=True makespan=353.0
shortest-processing-time: feasible=True makespan=433.0

両スケジュールは実行可能であり、このインスタンス上では素朴な規則のほうが勝ちます。 earliest-start は 353.0 で終わり、短いタスクを優先する shortest-processing-time は 433.0 で終わります。これは立ち止まって考える価値があります。shortest-processing-time は 一般には良い規則だからです——ジョブショップでは、後続タスクが待っている長いタスクを 後回しにすることがあり、スケジュール全体がそれを待つことになります。規則の評判は、 あなたのインスタンス上でそれが何をするかを教えてはくれません。

だからこそ、数値ではなく形が要点になります。1つのインスタンスはどちらの向きにも 何も証明しません——それが キャンペーン統計手法の用途です——しかしここでの比較(同じ検証済み問題、同じ停止基準、 同じ導出シード)はまさに、より大きな証拠が構築される仕方であり、単一の実行はまさに そうではないものです。

3️⃣ ソルバーメタデータを読む

各登録ソルバーは公開契約としてメタデータを担います:対応目的、能力タグ、確率性、既定の停止基準、 そして正準引用。これを 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))

uv run python inspect_metadata.py で実行:

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

このメタデータは registry.select がフィルタする対象であり、 ソルバー推薦器が説明の源とするものです。完全なレジストリ—— ベースライン、メタヒューリスティック、厳密アダプタ、そして NDSO ファミリ——は アルゴリズムソルバーシステムに編目されています。

🧪 Tutorial 2:小さなキャンペーンを実行しその結果を読む

キャンペーンは構成された実行の集合——ベンチマークインスタンスをソルバーと目的で交差させたもの ——で、明示的なシード、予算、チェックポイント、そしてディスク上の結果リポジトリで実行されます。 このチュートリアルは experiments/configs/smoke-pilot.json で追跡されるレシピと同じ形を実行 します:2つのスモークインスタンス、2つの決定論的ベースライン、1つの目的。

1️⃣ キャンペーンを構成し実行する

これを first_campaign.py として保存してください。キャンペーンを宣言し、決定論的計画へ検証し、 ドライランでそのコストを見積もり、それからローカルの my-campaigns/ ワークスペースへ実行します:

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)}")

uv run python first_campaign.py で実行:

planned runs: 4
estimated wall time: 2.0s
completed runs: 4
failed attempts: 0

4つの実行はまさに直積です:2つのベンチマークインスタンス × 2つのソルバー × 1つの目的、両ソルバー が決定論的なのでセルあたり1実行。各実行のシードは root_seed と実行の位置から導出されるため、 スクリプトの再実行は同じレコードを再現します;チェックポイントは中断されたキャンペーンが完了 した作業を繰り返さず再開できるようにします。

2️⃣ ディスクに何が着地したか調べる

runner は追跡される実験ワークスペースと同じレイアウトのワークスペースを 書き出しました:

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

実行ごとに1つの JSON レコード、キャンペーン・ソルバー・ベンチマーク・複製インデックスで アドレス指定——経路そのものがインデックスです。各レコードはその決定論的ペイロード上で 内容-ハッシュされ、それが replay モードが検証する対象です。

3️⃣ 分析のため結果を読み込む

dispatchatlas.analytica はキャンペーンランタイムをインポートせず完了したキャンペーン ディレクトリを読みます。これを 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}"
    )

uv run python read_results.py で実行:

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 はソルバーと目的ごとに記述統計を計算します。ソルバーあたりわずか2実行では、 推測手法(有意性検定、信頼区間)は検出力-不足の結果を生む代わりに制限面へ経路します——各確率的 ソルバー-インスタンスセルあたり30の独立実行という統計-検出力の下限は 分析エクスポートで記述されます。

4️⃣ 証拠バンドルをエクスポートする(任意)

同じキャンペーンディレクトリがエクスポートコマンドに供給され、表・図・補遺からなる 開示-フィルタされた証拠バンドルを書き出します:

uv run dispatchatlas export `
  --campaign-dir .\my-campaigns\results\first-campaign `
  --target-dir .\exports\first-campaign `
  --authorized-output-root .\exports `
  --tier core

バンドルは exports/first-campaign/evidence-bundles/first-campaign-core/ に着地します。 証拠バンドルページが4つの層とバンドル内容を説明します。

🎓 これが導く先

  • 追跡されるレシピ experiments/scripts/run_smoke_pilot.py はこの同じ形を 実験ワークスペースへ実行し、その構成を dispatchatlas-lab コマンドライン インターフェース用にシリアライズします。
  • キャンペーンエンジンページは実行モード、再試行ポリシー、再開、replay 検証、二重停止プロトコル、そして公正-比較保証を扱います。
  • 全-段階キャンペーンは統計設計承認が記録されるまで阻止されたままです—— リリース準備を参照。