Учебники
Эти учебники — обучающе-ориентированные сквозные прогоны: каждый шаг — исполняемый скрипт,
каждый показанный вывод — реальный вывод из упакованных дымовых данных, и каждый прогон
детерминирован — те же семена производят те же числа на вашей машине. Сначала завершите
установку и
быстрый старт, затем запустите каждый скрипт из чекаута
репозитория через 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.1summarize_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, двойные протоколы остановки, и гарантии справедливого-сравнения.
- Кампании полной-стадии остаются заблокированными, пока не записано одобрение статистического дизайна — см. готовность к релизу.