Tutoriais
Estes tutoriais são percursos orientados-ao-aprendizado, de ponta-a-ponta: cada passo é um
script executável, cada saída mostrada é saída real dos dados de fumaça empacotados, e cada
execução é determinística — as mesmas sementes produzem os mesmos números na sua máquina.
Complete primeiro a instalação e o
início rápido, depois execute cada script a partir do
checkout do repositório com uv run python <file>.py.
🧭 Escolha Um Tutorial
| Tutorial | Você aprenderá | Pacotes tocados |
|---|---|---|
| Modelar e resolver um primeiro problema de despacho | Escolher uma instância de benchmark, executar dois solvers sobre ela, comparar escalonamentos, e ler metadados de solver. | dispatchatlas.core, dispatchatlas.bench, dispatchatlas.solve |
| Executar uma campanha pequena e ler seus resultados | Configurar uma campanha com-checkpoints, executá-la em um workspace, e carregar os resultados para análise. | dispatchatlas.lab, dispatchatlas.analytica |
Caminhos de referência mais profundos continuam onde os tutoriais terminam: contratos de domínio para o modelo de escalonamento, modelo de benchmarks para a geração do catálogo, sistema de solvers para o registro completo, motor de campanhas para o detalhe de orquestração, e exportações de análise para os pacotes de evidência. Campanhas maiores requerem os gates estatísticos e de lançamento descritos em prontidão.
🛠️ Tutorial 1: Modelar E Resolver Um Primeiro Problema De Despacho
O início rápido resolveu um problema com um solver. Este tutorial vai um nível mais fundo: você escolhe uma instância de benchmark específica, executa duas linhas-base de despacho sobre ela, compara os escalonamentos que elas constroem, e lê os metadados que explicam cada solver.
1️⃣ Ver o que o catálogo de fumaça oferece
O catálogo de fumaça empacotado materializa duas pequenas instâncias determinísticas por
família de escalonamento a partir de uma única semente raiz. Salve isto como
list_problems.py e execute-o com 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)Saída:
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-1Cada id nomeia sua família de escalonamento e um índice de
instância baseado-em-zero. O resto do tutorial usa smoke-job-shop-0.
2️⃣ Executar dois solvers sobre uma instância
provider.get_problem recebe um ProblemId e retorna um ValidatedProblem — a
especificação do problema mais seu selo e relatório de validação, de modo que um solver nunca
recebe uma instância não-validada. Os dois solvers abaixo são linhas-base de despacho
determinísticas: earliest-start escalona tarefas em ordem topológica de entrada, enquanto
shortest-processing-time prioriza tarefas mais curtas. Salve isto como 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}"
)Execute-o com 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.0Ambos os escalonamentos são factíveis, e nesta instância vence a regra mais simples: earliest-start termina em 353.0 enquanto shortest-processing-time, que prioriza as tarefas mais curtas, termina em 433.0. Vale a pena parar aqui, porque shortest-processing-time é uma boa regra em geral — num job shop ela pode adiar uma tarefa longa que uma tarefa posterior está esperando, e o escalonamento inteiro espera junto. A reputação de uma regra não diz o que ela faz na sua instância.
E é justamente esse o ponto: a forma, não o número. Uma instância não prova nada em nenhuma direção — é para isso que servem as campanhas e os métodos estatísticos — mas a comparação aqui (mesmo problema validado, mesmos critérios de parada, mesma semente derivada) é exatamente como evidência maior é construída, e uma única execução é exatamente o que ela não é.
3️⃣ Ler os metadados do solver
Cada solver registrado carrega metadados como seu contrato público: objetivos suportados,
rótulos de capacidade, estocasticidade, critérios de parada padrão, e uma citação canônica.
Salve isto como 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))Execute-o com 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, deterministicEstes metadados são sobre o que registry.select filtra e a partir do que o
recomendador de solvers explica. O registro completo —
linhas-base, metaheurísticas, adaptadores exatos, e a família NDSO — está catalogado em
algoritmos e sistema de solvers.
🧪 Tutorial 2: Executar Uma Campanha Pequena E Ler Seus Resultados
Uma campanha é um conjunto configurado de execuções — instâncias de benchmark cruzadas com
solvers e objetivos — executado com sementes, orçamentos, checkpoints explícitos, e um
repositório de resultados em-disco. Este tutorial executa a mesma forma que a receita
rastreada em experiments/configs/smoke-pilot.json: duas instâncias de fumaça, duas
linhas-base determinísticas, um objetivo.
1️⃣ Configurar e executar a campanha
Salve isto como first_campaign.py. Ele declara a campanha, valida-a em um plano
determinístico, estima seu custo com uma execução-seca, então executa-a em um workspace local
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)}")Execute-o com uv run python first_campaign.py:
planned runs: 4
estimated wall time: 2.0s
completed runs: 4
failed attempts: 0Quatro execuções é exatamente o produto cruzado: 2 instâncias de benchmark × 2 solvers × 1
objetivo, com uma execução por célula porque ambos os solvers são determinísticos. A semente
de cada execução deriva de root_seed e da posição da execução, de modo que re-executar o
script reproduz os mesmos registros; o checkpoint deixa uma campanha interrompida retomar sem
repetir trabalho concluído.
2️⃣ Inspecionar o que aterrissou no disco
O runner escreveu um workspace com a mesma disposição que o workspace de experimentos rastreado:
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.jsonUm registro JSON por execução, endereçado por campanha, solver, benchmark, e índice de
réplica — o caminho em si é o índice. Cada registro é hasheado-por-conteúdo sobre sua carga
determinística, que é contra o que o modo replay verifica.
3️⃣ Carregar os resultados para análise
dispatchatlas.analytica lê diretórios de campanha concluídos sem importar o runtime de
campanha. Salve isto como 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}"
)Execute-o com 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 computa estatísticas descritivas por solver e objetivo. Com apenas duas
execuções por solver os métodos inferenciais (testes de significância, intervalos de
confiança) roteiam para a superfície de limitações em vez de produzir resultados
sub-potenciados — o piso de potência-estatística de 30 execuções independentes por célula
estocástica solver-instância é descrito em exportações de análise.
4️⃣ Exportar um pacote de evidência (opcional)
O mesmo diretório de campanha alimenta o comando de exportação, que escreve um pacote de evidência filtrado-por-divulgação de tabelas, figuras, e suplemento:
uv run dispatchatlas export `
--campaign-dir .\my-campaigns\results\first-campaign `
--target-dir .\exports\first-campaign `
--authorized-output-root .\exports `
--tier coreO pacote aterrissa em exports/first-campaign/evidence-bundles/first-campaign-core/. A
página pacotes de evidência explica os quatro níveis e o conteúdo
do pacote.
🎓 Para onde isto leva
- A receita rastreada
experiments/scripts/run_smoke_pilot.pyexecuta esta mesma forma no workspace de experimentos e serializa sua configuração para a interface de linha-de-comandodispatchatlas-lab. - A página motor de campanhas cobre modos de execução, política de retentativa, retomada, verificação de replay, protocolos de parada duais, e garantias de comparação-justa.
- Campanhas de estágio-completo permanecem bloqueadas até que a aprovação do design estatístico seja registrada — ver prontidão de lançamento.