05_02_00_graph_construction - Motor de Construcción del Grafo¶
🎯 Propósito¶
El Graph Construction Engine es el núcleo del subsistema de visualización de dependencias. Transforma datos tabulares del catálogo (módulo A depende de B, C, D) en una estructura de grafo dirigido navegable con índices bidireccionales para queries eficientes.
🏗️ Arquitectura¶
Modelo de Datos¶
Graph
├── nodes: HashMap<UUID, Node> # Acceso O(1) por ID
├── edges: Vec<Edge> # Lista de aristas
├── adjacency_forward: HashMap<UUID, Set> # id → dependencies
└── adjacency_reverse: HashMap<UUID, Set> # id → dependents
Estructuras Principales¶
Node (Nodo)¶
Representa un módulo en el grafo: - UUID id: Identificador único e inmutable - label: Nombre para visualización - level: Nivel jerárquico (L0_KERNEL, L1_ATOM, L2_CELL, L3_ENGINE) - category: Categoría funcional (FILTER, OSC, MATH, etc.) - status: Estado de desarrollo (STABLE, BETA, EXPERIMENTAL, DEPRECATED) - cpu_cycles: Estimación de uso de CPU - latency_samples: Latencia de procesamiento - in_degree / out_degree: Grados calculados
Edge (Arista)¶
Representa una dependencia: - source: Módulo dependiente (quien necesita) - target: Módulo requerido (lo que se necesita) - type: Tipo de dependencia (REQUIRED, OPTIONAL, RUNTIME, BUILD) - version_constraint: Restricción de versión (e.g., ">=1.5.0") - link_strength: Fuerza del acoplamiento (0.0 a 1.0)
Graph (Grafo)¶
Contenedor principal con operaciones:
- add_node(): Añadir nodo
- add_edge(): Añadir arista
- get_dependencies(): Obtener dependencias directas
- get_dependents(): Obtener dependientes directos
- has_edge(): Verificar existencia de arista
- compute_statistics(): Calcular métricas del grafo
🔄 Pipeline de Construcción (5 Etapas)¶
Etapa 1: Data Loading¶
- Lee catálogo desde JSON/YAML - Parser con error handling robusto - Extrae información de módulos y dependenciasFormato de entrada esperado:
{
"modules": [
{
"id": "svf_filter",
"name": "SVF Filter",
"level": "L1_ATOM",
"category": "FILTER",
"status": "STABLE",
"cpu_cycles": 120,
"latency_samples": 3,
"version": "1.5.0",
"author": "Audio Team",
"dependencies": [
{
"module_id": "add_kernel",
"type": "REQUIRED",
"version": ">=1.0.0"
}
]
}
]
}
Etapa 2: Edge Creation¶
- Crea nodos para cada módulo - Procesa dependencias → aristas dirigidas - Valida que target nodes existan - Actualiza degree countsEtapa 3: Property Enrichment¶
- Añade propiedades derivadas - Calcula in_degree / out_degree - Aplica colorización por nivel: - L0_KERNEL →#0066CC (azul)
- L1_ATOM → #00AA44 (verde)
- L2_CELL → #FFAA00 (amarillo)
- L3_ENGINE → #CC0000 (rojo)
- Valida consistencia de grados
Etapa 4: Validation¶
- Detección de ciclos (DFS con colores, O(V+E)) - Validación de jerarquía: - L0 solo puede depender de L0 - L1 puede depender de L0, L1 - L2 puede depender de L0, L1, L2 - L3 puede depender de cualquiera - Detección de nodos aislados (warnings)Etapa 5: Indexing¶
- Construye índices de búsqueda - Prepara estructuras para traversal - Cachea cálculos costosos (transitive closure)📊 Optimizaciones¶
Representación Sparse¶
- Adjacency Lists en vez de matriz de adyacencia
- Memoria: O(V + E) en vez de O(V²)
- Típico DSP: densidad 0.01-0.05 → ahorro masivo
Indexación Bidireccional¶
- Forward index: "qué necesita este módulo" → O(1)
- Reverse index: "quién necesita este módulo" → O(1)
- Permite queries upstream/downstream eficientes
Lazy Evaluation¶
- Transitive closure se calcula solo cuando se necesita
- Cache invalidation selectiva en cambios
- Evita trabajo innecesario
🧪 Testing¶
Cobertura de Tests¶
- ✅ Creación de nodos y aristas
- ✅ Validación de duplicados
- ✅ Queries de dependencias (forward/reverse)
- ✅ Filtrado por nivel/categoría
- ✅ Nodos aislados
- ✅ Estadísticas del grafo
- ✅ Detección de ciclos
- ✅ Validación de jerarquía
- ✅ Performance benchmarks
Ejecutar Tests¶
Benchmarks Esperados¶
| Operación | Target | Resultado |
|---|---|---|
| Crear 100 nodos | <1ms | ✅ |
| Crear 500 nodos | <10ms | ✅ |
| Query dependencies (200 edges) | <1ms | ✅ |
📝 API Examples¶
Construcción Manual¶
#include "graph.hpp"
using namespace audio_lab::dependency_graph;
// Create graph
Graph g;
// Add nodes
auto add_kernel = std::make_shared<Node>(
"add_kernel",
"Add Kernel",
HierarchyLevel::L0_KERNEL
);
add_kernel->cpu_cycles = 10;
g.add_node(add_kernel);
auto filter = std::make_shared<Node>(
"svf_filter",
"SVF Filter",
HierarchyLevel::L1_ATOM
);
filter->cpu_cycles = 120;
filter->latency_samples = 3;
g.add_node(filter);
// Add edge (filter depends on add_kernel)
auto edge = std::make_shared<Edge>(
"svf_filter",
"add_kernel",
DependencyType::REQUIRED
);
g.add_edge(edge);
// Query
auto deps = g.get_dependencies("svf_filter");
// deps contains: {"add_kernel"}
auto dependents = g.get_dependents("add_kernel");
// dependents contains: {"svf_filter"}
Construcción desde Catálogo¶
#include "graph_builder.hpp"
Graph g;
GraphBuilder builder(g);
// Build from JSON catalog
bool success = builder.build_from_catalog("catalog.json");
if (!success) {
for (const auto& error : builder.get_errors()) {
std::cerr << "ERROR: " << error << std::endl;
}
}
for (const auto& warning : builder.get_warnings()) {
std::cout << "WARNING: " << warning << std::endl;
}
// Graph is ready to use
auto stats = g.compute_statistics();
std::cout << "Nodes: " << stats.node_count << std::endl;
std::cout << "Edges: " << stats.edge_count << std::endl;
std::cout << "Density: " << stats.density << std::endl;
Queries Avanzadas¶
// Filtrar por nivel
auto l1_nodes = g.get_nodes_by_level(HierarchyLevel::L1_ATOM);
// Filtrar por categoría
auto filters = g.get_nodes_by_category("FILTER");
// Nodos aislados (sin conexiones)
auto isolated = g.get_isolated_nodes();
// Verificar existencia de arista
if (g.has_edge("module_a", "module_b")) {
// Edge exists
}
// Acceder a propiedades
auto node = g.get_node("svf_filter");
if (node) {
std::cout << "CPU: " << node->cpu_cycles << std::endl;
std::cout << "Color: " << node->get_color() << std::endl;
std::cout << "In-degree: " << node->in_degree << std::endl;
}
🔍 Validación y Diagnósticos¶
Detección de Ciclos¶
El algoritmo DFS con colores detecta ciclos en O(V+E):
WHITE → No visitado
GRAY → En proceso (en stack)
BLACK → Completado
Si encuentras arista a nodo GRAY → CICLO
Ejemplo de output:
Validación de Jerarquía¶
Verifica que niveles superiores no dependan de inferiores:
// VÁLIDO: L1 → L0
svf_filter (L1_ATOM) depends on add_kernel (L0_KERNEL) ✅
// INVÁLIDO: L0 → L1
add_kernel (L0_KERNEL) depends on svf_filter (L1_ATOM) ❌
Output de violación:
Warnings Comunes¶
WARNING: Found 3 isolated nodes
WARNING: Degree mismatch for node: xyz
WARNING: Skipping module with missing required fields
📈 Complejidad Algorítmica¶
| Operación | Tiempo | Espacio |
|---|---|---|
add_node() |
O(1) | O(1) |
add_edge() |
O(1) | O(1) |
get_dependencies() |
O(1) | O(k) donde k = num deps |
get_dependents() |
O(1) | O(k) donde k = num dependents |
has_edge() |
O(1) | O(1) |
get_nodes_by_level() |
O(V) | O(m) donde m = matches |
compute_statistics() |
O(V) | O(1) |
| Cycle detection | O(V+E) | O(V) |
| Hierarchy validation | O(E) | O(1) |
🔗 Dependencias¶
Bibliotecas Externas¶
- nlohmann/json - JSON parsing (install via vcpkg)
- Catch2 - Unit testing framework
Subsistemas Internos¶
../00_catalog_registry/- Fuente de datos de módulos../01_hierarchy_framework/- Reglas de validación
📚 Referencias¶
- Cormen et al. - "Introduction to Algorithms" (Graph Algorithms, Ch 22)
- Tarjan, R. (1972) - "Depth-First Search and Linear Graph Algorithms"
- NetworkX Documentation - Python graph library (design patterns)
🚀 Próximos Pasos¶
- ✅ Core data structures implementadas
- ✅ Pipeline de construcción de 5 etapas
- ✅ Validación y detección de ciclos
- ✅ Tests unitarios completos
- ⏳ Integración con subsistema de visualización
- ⏳ Transitive closure optimization
- ⏳ Métricas avanzadas (betweenness centrality)
Status: ✅ Implementación Core Completa Test Coverage: >90% Performance: Cumple targets (<10s para 500 nodos)