Перейти к содержимому
DispatchAtlas
Поиск

Учебники

Эти учебники — обучающе-ориентированные сквозные прогоны: каждый шаг — исполняемый скрипт, каждый показанный вывод — реальный вывод из упакованных дымовых данных, и каждый прогон детерминирован — те же семена производят те же числа на вашей машине. Сначала завершите установку и быстрый старт, затем запустите каждый скрипт из чекаута репозитория через uv run python <file>.py.

🧭 Выберите Учебник

УчебникВы научитесьЗатронутые пакеты
Смоделировать и решить первую задачу диспетчеризацииВыбрать экземпляр бенчмарка, запустить два солвера на нём, сравнить расписания, и прочитать метаданные солвера.dispatchatlas.core, dispatchatlas.bench, dispatchatlas.solve
Запустить небольшую кампанию и прочитать её результатыСконфигурировать кампанию с-контрольными-точками, выполнить её в рабочее-пространство, и загрузить результаты для анализа.dispatchatlas.lab, dispatchatlas.analytica

Более глубокие справочные пути продолжаются там, где учебники заканчиваются: доменные контракты для модели планирования, модель бенчмарков для генерации каталога, система солверов для полного реестра, движок кампаний для деталей оркестрации, и экспорты анализа для пакетов доказательств. Большие кампании требуют статистических и релизных воротов, описанных в готовности.

🛠️ Tutorial 1: Смоделировать И Решить Первую Задачу Диспетчеризации

Быстрый старт решил одну задачу одним солвером. Этот учебник идёт на уровень глубже: вы выбираете конкретный экземпляр бенчмарка, запускаете на нём две базовые линии диспетчеризации, сравниваете расписания, которые они строят, и читаете метаданные, объясняющие каждый солвер.

1️⃣ Посмотреть, что предлагает дымовой каталог

Упакованный дымовой каталог материализует два малых детерминированных экземпляра на семейство планирования из единственного корневого семени. Сохраните это как 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️⃣ Запустить два солвера на одном экземпляре

provider.get_problem принимает ProblemId и возвращает ValidatedProblem — спецификацию задачи плюс её штамп и отчёт валидации, так что солвер никогда не получает невалидированный экземпляр. Два солвера ниже — детерминированные базовые линии диспетчеризации: 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 в целом хорошее правило — в job shop оно может отложить длинную задачу, которой ждёт последующая, и всё расписание ждёт вместе с ней. Репутация правила не говорит вам, что оно сделает на вашем экземпляре.

Именно в этом и смысл: в форме, а не в числе. Один экземпляр не доказывает ничего ни в ту, ни в другую сторону — для этого есть кампании и статистические методы — но сравнение здесь (та же валидированная задача, те же критерии остановки, то же производное семя) — это в точности то, как строится большее доказательство, а единственный запуск — это в точности не оно.

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: два дымовых экземпляра, две детерминированные базовые линии, одна цель.

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

Четыре прогона — это в точности декартово произведение: 2 экземпляра бенчмарка × 2 солвера × 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

Одна 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 вычисляет описательную статистику по солверу и цели. С всего двумя прогонами на солвер инференциальные методы (тесты значимости, доверительные интервалы) маршрутизируются к поверхности ограничений вместо производства недо-мощных результатов — нижний-предел статистической-мощности в 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/. Страница пакеты доказательств объясняет четыре уровня и содержимое пакета.

🎓 Куда это ведёт

  • Отслеживаемый рецепт experiments/scripts/run_smoke_pilot.py выполняет эту же форму в рабочее-пространство экспериментов и сериализует свою конфигурацию для интерфейса командной-строки dispatchatlas-lab.
  • Страница движок кампаний охватывает режимы выполнения, политику повторов, возобновление, верификацию replay, двойные протоколы остановки, и гарантии справедливого-сравнения.
  • Кампании полной-стадии остаются заблокированными, пока не записано одобрение статистического дизайна — см. готовность к релизу.