Saltar al contenido
DispatchAtlas
Buscar

Tutoriales

Estos tutoriales son recorridos orientados-al-aprendizaje, de extremo-a-extremo: cada paso es un script ejecutable, cada salida mostrada es salida real de los datos de prueba empaquetados, y cada corrida es determinista — las mismas semillas producen los mismos números en tu máquina. Completa primero la instalación y el inicio rápido, luego ejecuta cada script desde el checkout del repositorio con uv run python <file>.py.

🧭 Elige Un Tutorial

TutorialAprenderásPaquetes tocados
Modelar y resolver un primer problema de despachoElegir una instancia de benchmark, ejecutar dos solvers sobre ella, comparar horarios, y leer metadatos de solver.dispatchatlas.core, dispatchatlas.bench, dispatchatlas.solve
Ejecutar una campaña pequeña y leer sus resultadosConfigurar una campaña con-checkpoints, ejecutarla en un workspace, y cargar los resultados para análisis.dispatchatlas.lab, dispatchatlas.analytica

Rutas de referencia más profundas continúan donde los tutoriales terminan: contratos de dominio para el modelo de planificación, modelo de benchmarks para la generación del catálogo, sistema de solvers para el registro completo, motor de campañas para el detalle de orquestación, y exportaciones de análisis para los paquetes de evidencia. Las campañas mayores requieren los gates estadísticos y de lanzamiento descritos en preparación.

🛠️ Tutorial 1: Modelar Y Resolver Un Primer Problema De Despacho

El inicio rápido resolvió un problema con un solver. Este tutorial va un nivel más profundo: eliges una instancia de benchmark específica, ejecutas dos líneas-base de despacho sobre ella, comparas los horarios que construyen, y lees los metadatos que explican cada solver.

1️⃣ Ver qué ofrece el catálogo de prueba

El catálogo de prueba empaquetado materializa dos pequeñas instancias deterministas por familia de planificación desde una única semilla raíz. Guarda esto como list_problems.py y ejecútalo con 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)

Salida:

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

Cada id nombra su familia de planificación y un índice de instancia de-base-cero. El resto del tutorial usa smoke-job-shop-0.

2️⃣ Ejecutar dos solvers sobre una instancia

provider.get_problem toma un ProblemId y devuelve un ValidatedProblem — la especificación del problema más su sello y reporte de validación, de modo que un solver nunca recibe una instancia no-validada. Los dos solvers de abajo son líneas-base de despacho deterministas: earliest-start planifica tareas en orden topológico de entrada, mientras que shortest-processing-time prioriza tareas más cortas. Guarda esto 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}"
    )

Ejecútalo con 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

Ambos horarios son factibles, y en esta instancia gana la regla más sencilla: earliest-start termina en 353.0 mientras que shortest-processing-time, que prioriza las tareas más cortas, termina en 433.0. Vale la pena detenerse aquí, porque shortest-processing-time es una buena regla en general — en un job shop puede posponer una tarea larga que una tarea posterior está esperando, y el horario entero espera con ella. La reputación de una regla no le dice lo que hace en su instancia.

Y de eso se trata: de la forma, no del número. Una instancia no prueba nada en ningún sentido — para eso están las campañas y los métodos estadísticos — pero la comparación aquí (mismo problema validado, mismos criterios de parada, misma semilla derivada) es exactamente cómo se construye evidencia mayor, y una sola ejecución es exactamente lo que no lo es.

3️⃣ Leer los metadatos del solver

Cada solver registrado lleva metadatos como su contrato público: objetivos soportados, etiquetas de capacidad, estocasticidad, criterios de parada por defecto, y una citación canónica. Guarda esto 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))

Ejecútalo con 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

Estos metadatos son sobre lo que registry.select filtra y desde lo que el recomendador de solvers explica. El registro completo — líneas-base, metaheurísticas, adaptadores exactos, y la familia NDSO — está catalogado en algoritmos y sistema de solvers.

🧪 Tutorial 2: Ejecutar Una Campaña Pequeña Y Leer Sus Resultados

Una campaña es un conjunto configurado de corridas — instancias de benchmark cruzadas con solvers y objetivos — ejecutado con semillas, presupuestos, checkpoints explícitos, y un repositorio de resultados en-disco. Este tutorial ejecuta la misma forma que la receta rastreada en experiments/configs/smoke-pilot.json: dos instancias de prueba, dos líneas-base deterministas, un objetivo.

1️⃣ Configurar y ejecutar la campaña

Guarda esto como first_campaign.py. Declara la campaña, la valida en un plan determinista, estima su costo con una corrida-seca, luego la ejecuta en un 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)}")

Ejecútalo con uv run python first_campaign.py:

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

Cuatro corridas es exactamente el producto cruzado: 2 instancias de benchmark × 2 solvers × 1 objetivo, con una corrida por celda porque ambos solvers son deterministas. La semilla de cada corrida deriva de root_seed y la posición de la corrida, de modo que re-ejecutar el script reproduce los mismos registros; el checkpoint deja que una campaña interrumpida reanude sin repetir trabajo completado.

2️⃣ Inspeccionar qué aterrizó en disco

El runner escribió un workspace con la misma disposición que el 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.json

Un registro JSON por corrida, direccionado por campaña, solver, benchmark, e índice de réplica — la ruta misma es el índice. Cada registro está hasheado-por-contenido sobre su carga determinista, que es contra lo que el modo replay verifica.

3️⃣ Cargar los resultados para análisis

dispatchatlas.analytica lee directorios de campaña completados sin importar el runtime de campaña. Guarda esto 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}"
    )

Ejecútalo con 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 computa estadísticas descriptivas por solver y objetivo. Con solo dos corridas por solver los métodos inferenciales (pruebas de significancia, intervalos de confianza) se enrutan a la superficie de limitaciones en vez de producir resultados sub-potenciados — el piso de potencia-estadística de 30 corridas independientes por celda estocástica solver-instancia se describe en exportaciones de análisis.

4️⃣ Exportar un paquete de evidencia (opcional)

El mismo directorio de campaña alimenta el comando de exportación, que escribe un paquete de evidencia filtrado-por-divulgación de tablas, figuras, y suplemento:

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

El paquete aterriza en exports/first-campaign/evidence-bundles/first-campaign-core/. La página de paquetes de evidencia explica los cuatro niveles y el contenido del paquete.

🎓 A dónde lleva esto

  • La receta rastreada experiments/scripts/run_smoke_pilot.py ejecuta esta misma forma en el workspace de experimentos y serializa su configuración para la interfaz de línea-de-comandos dispatchatlas-lab.
  • La página del motor de campañas cubre modos de ejecución, política de reintentos, reanudación, verificación de replay, protocolos de parada duales, y garantías de comparación-justa.
  • Las campañas de etapa-completa permanecen bloqueadas hasta que se registre la aprobación del diseño estadístico — ver preparación de lanzamiento.