Skip to content

05_05_07 - Composition Rules

📋 Descripción

Sistema de reglas de composición y validación de tipos para topologías DSP. Garantiza la corrección semántica, compatibilidad de tipos y cumplimiento de restricciones de rendimiento antes de la generación de código.

🎯 Objetivos

  1. Sistema de tipos: Definir tipos de puerto, dominios de señal y configuraciones de canales
  2. Validación de compatibilidad: Verificar conexiones entre puertos con tipos diferentes
  3. Reglas semánticas: Validar patrones DSP específicos (filtros, feedback, osciladores)
  4. Validación de rendimiento: Estimar y validar presupuestos de CPU/memoria
  5. Integración: Validar compatibilidad con plataformas target (VST3, AU, Web Audio)
  6. Auto-corrección: Sugerir y aplicar correcciones automáticas

🏗️ Arquitectura

Componentes Principales

type_system.hpp/cpp              # Sistema de tipos y compatibilidad
├── DataType                     # Audio/Control/MIDI/Parameter/Trigger
├── SignalDomain                 # Time/Frequency/Modal
├── ChannelConfig               # Mono/Stereo/Variable
├── PortType                    # Especificación completa de puerto
├── TypeChecker                 # Verificación de compatibilidad
└── CompatibilityResult         # Resultado con errores/warnings/conversiones

composition_validator.hpp/cpp    # Validadores semánticos y de rendimiento
├── SemanticValidator           # Reglas DSP específicas
├── PerformanceValidator        # Estimación de CPU/memoria
├── IntegrationValidator        # Validación por plataforma
├── TopologyValidator           # Validador comprehensivo
└── ValidationFixer             # Generación de correcciones

Flujo de Validación

Topology
[Type Checking] → CompatibilityResult
[Constraint Validation] → Structural constraints
[Semantic Validation] → DSP-specific rules
[Performance Validation] → CPU/Memory budgets
[Integration Validation] → Platform requirements
ValidationReport → [Auto-fix] → Corrected Topology

📊 Sistema de Tipos

DataType

Clasificación de datos de señal:

  • Audio: Señal audio-rate (44.1kHz, 48kHz, etc.)
  • Control: Señal control-rate (automation, LFO)
  • MIDI: Eventos MIDI
  • Parameter: Parámetros estáticos
  • Trigger: Gates/triggers
  • Generic: Tipo polimórfico

SignalDomain

Dominio de procesamiento:

  • Time: Dominio temporal (samples)
  • Frequency: Dominio frecuencial (FFT bins)
  • Modal: Síntesis modal
  • Any: Agnóstico al dominio

ChannelConfig

Configuración de canales:

ChannelConfig::mono()        // 1 canal fijo
ChannelConfig::stereo()      // 2 canales fijos
ChannelConfig::channels(n)   // n canales fijos
ChannelConfig::variable()    // Canales variables

PortType

Especificación completa:

struct PortType {
    DataType data_type;
    SignalDomain domain;
    ChannelConfig channels;
    size_t sample_rate;
    size_t buffer_size;
    std::string unit;       // Hz, dB, etc.
    float min_value;
    float max_value;
};

🔍 Type Checker

Compatibilidad de Tipos

// Verificar compatibilidad entre puertos
PortType source_type{
    DataType::Audio,
    SignalDomain::Time,
    ChannelConfig::mono(),
    44100, 512
};

PortType target_type{
    DataType::Audio,
    SignalDomain::Time,
    ChannelConfig::stereo(),
    44100, 512
};

auto result = TypeChecker::check_compatibility(source_type, target_type);

if (result.is_compatible) {
    if (result.requires_conversion) {
        std::cout << "Requiere conversión: " << result.conversion_type << "\n";
    }
} else {
    for (const auto& error : result.errors) {
        std::cout << "ERROR: " << error << "\n";
    }
}

Conversiones Automáticas

El sistema detecta y sugiere conversiones necesarias:

Conversión Tipo Descripción
upsample Control → Audio Interpolación de señal de control
fft Time → Frequency Transformada FFT
ifft Frequency → Time Transformada inversa IFFT
mono_to_multi Mono → Stereo Broadcast de mono a multi-canal
multi_to_mono Stereo → Mono Mix down a mono
resample 44.1kHz → 48kHz Resampling de frecuencia

🛡️ Validadores de Restricciones

Restricciones Estructurales

ConstraintValidator validator;

// Restricciones por defecto:
// - NoDanglingInputs: No entradas desconectadas
// - SourceToSink: Todos los sources llegan a sinks
// - TypeConsistency: Consistencia de tipos
// - MaxNodeCount: Límite de nodos (1000)
// - MaxDepth: Profundidad máxima (100)

auto report = validator.validate(topology);

if (report.has_errors()) {
    std::cout << "ERRORS: " << report.error_count() << "\n";
    for (const auto& error : report.errors) {
        std::cout << "  " << error.message << "\n";
    }
}

if (report.warning_count() > 0) {
    std::cout << "WARNINGS: " << report.warning_count() << "\n";
    for (const auto& warning : report.warnings) {
        std::cout << "  " << warning.message << "\n";
    }
}

Restricciones Personalizadas

// Crear restricción personalizada
class MaxFanOutConstraint : public CompositionConstraint {
    size_t max_fanout_;
public:
    explicit MaxFanOutConstraint(size_t max) : max_fanout_(max) {}

    ConstraintResult check(const Topology& topology) const override {
        ConstraintResult result;
        result.level = ConstraintLevel::Warning;
        result.satisfied = true;

        for (const auto& [node_id, node] : topology.nodes()) {
            size_t fanout = 0;
            for (const auto& edge : topology.edges()) {
                if (edge.source_node == node_id) fanout++;
            }

            if (fanout > max_fanout_) {
                result.satisfied = false;
                result.affected_nodes.push_back(node_id);
                result.message += "Node '" + node_id + "' has excessive fanout: " +
                                std::to_string(fanout) + "\n";
            }
        }

        return result;
    }

    std::string name() const override { return "MaxFanOut"; }
    ConstraintLevel level() const override { return ConstraintLevel::Warning; }
};

// Agregar al validador
validator.add_constraint(std::make_unique<MaxFanOutConstraint>(10));

🔬 Validadores Semánticos

Feedback con Delay

// Valida que todos los ciclos contengan delay (causalidad)
auto result = SemanticValidator::validate_feedback_delay(topology);

if (!result.satisfied) {
    std::cout << "ERROR: Feedback loops without delay:\n";
    std::cout << result.message;
}

Estabilidad de Filtros

// Valida parámetros de filtros (Q, fc vs Nyquist)
auto result = SemanticValidator::validate_filter_stability(topology);

if (!result.satisfied) {
    std::cout << "WARNING: Potentially unstable filters:\n";
    std::cout << result.message;
}

Rangos de Modulación

// Valida que moduladores no excedan rangos razonables
auto result = SemanticValidator::validate_modulation_ranges(topology);

Longitudes de Delay

// Valida que delays no excedan límites (10s @ 192kHz)
auto result = SemanticValidator::validate_delay_lengths(topology);

Frecuencias de Osciladores

// Valida que frecuencias estén bajo Nyquist
auto result = SemanticValidator::validate_oscillator_frequencies(topology);

⚡ Validación de Rendimiento

Estimación de Métricas

auto metrics = PerformanceValidator::estimate_performance(topology);

std::cout << "Performance Metrics:\n";
std::cout << "  Nodes: " << metrics.total_nodes << "\n";
std::cout << "  Operations: " << metrics.total_operations << " ops/sample\n";
std::cout << "  Memory: " << metrics.memory_usage / 1024 << " KB\n";
std::cout << "  CPU estimate: " << metrics.cpu_estimate * 100 << "%\n";
std::cout << "  Max latency: " << metrics.max_latency << " samples\n";

Validación de Presupuesto

// Validar contra presupuestos de CPU y memoria
float cpu_budget = 0.5f;              // 50% CPU
size_t memory_budget = 1024 * 1024;   // 1 MB

auto result = PerformanceValidator::validate_performance_budget(
    topology, cpu_budget, memory_budget
);

if (!result.satisfied) {
    std::cout << "Performance budget exceeded:\n";
    std::cout << result.message;
}

Identificación de Hotspots

// Identificar nodos más costosos
auto hotspots = PerformanceValidator::identify_hotspots(topology);

std::cout << "Performance hotspots:\n";
for (const auto& node_id : hotspots) {
    std::cout << "  - " << node_id << "\n";
}

Sugerencias de Optimización

auto suggestions = PerformanceValidator::suggest_optimizations(topology);

std::cout << "Optimization suggestions:\n";
for (const auto& suggestion : suggestions) {
    std::cout << "  • " << suggestion << "\n";
}

🔌 Validación de Integración

VST3

auto result = IntegrationValidator::validate_vst3(topology);
// Requiere: exactamente 1 source y 1 sink

Audio Unit

auto result = IntegrationValidator::validate_audio_unit(topology);
// Requiere: exactamente 1 source y 1 sink

Web Audio API

auto result = IntegrationValidator::validate_web_audio(topology);
// Restricciones: no SIMD (SSE/AVX)

Embedded/Hardware

auto result = IntegrationValidator::validate_embedded(topology);
// Límites estrictos: 64KB RAM, 30% CPU

🔧 Validador Comprehensivo

Configuración

ValidationConfig config;
config.check_types = true;
config.check_semantics = true;
config.check_performance = true;
config.check_integration = true;
config.cpu_budget = 0.5f;
config.memory_budget = 1024 * 1024;
config.target_platform = "vst3";

TopologyValidator validator(config);

Validación Completa

auto report = validator.validate(topology);

std::cout << "Validation Report:\n";
std::cout << "  Status: " << (report.passed ? "PASSED" : "FAILED") << "\n";
std::cout << "  Errors: " << report.error_count() << "\n";
std::cout << "  Warnings: " << report.warning_count() << "\n";

if (!report.passed) {
    std::cout << "\nErrors:\n";
    for (const auto& error : report.errors) {
        std::cout << "  [ERROR] " << error.message << "\n";
        if (!error.affected_nodes.empty()) {
            std::cout << "    Affected nodes: ";
            for (const auto& node : error.affected_nodes) {
                std::cout << node << " ";
            }
            std::cout << "\n";
        }
    }
}

if (report.warning_count() > 0) {
    std::cout << "\nWarnings:\n";
    for (const auto& warning : report.warnings) {
        std::cout << "  [WARNING] " << warning.message << "\n";
    }
}

🔨 Auto-Corrección

Generar Correcciones

auto fixes = ValidationFixer::generate_fixes(topology, report);

std::cout << "Available fixes:\n";
for (const auto& fix : fixes) {
    std::cout << "  " << (fix.automatic ? "[AUTO]" : "[MANUAL]")
              << " " << fix.description << "\n";
}

Aplicar Correcciones Automáticas

// Aplicar solo correcciones automáticas
Topology fixed_topology = ValidationFixer::apply_automatic_fixes(topology, fixes);

// Validar nuevamente
auto new_report = validator.validate(fixed_topology);
if (new_report.passed) {
    std::cout << "Topology fixed successfully!\n";
}

Tipos de Correcciones

Corrección Automática Descripción
fix_dangling_inputs Conecta entradas a valores por defecto
fix_type_mismatches Inserta conversores de tipo (requiere decisión)
fix_feedback_delays Inserta delays z^-1 en ciclos

📝 Ejemplo Completo

#include "type_system.hpp"
#include "composition_validator.hpp"
#include "../05_05_00_graph_representation/topology_builder.hpp"

using namespace audiolab::topology;
using namespace audiolab::topology::composition;

int main() {
    // 1. Construir topología
    auto topology = TopologyBuilder()
        .setName("feedback_delay_example")
        .addNode("input", "external_input", NodeType::Source)
        .addNode("gain", "multiply_scalar", NodeType::Processing)
        .addNode("delay", "delay_line", NodeType::Processing)
        .addNode("output", "external_output", NodeType::Sink)
        .connect("input", "out", "gain", "in")
        .connect("gain", "out", "delay", "in")
        .connect("delay", "out", "output", "in")
        .connect("delay", "out", "gain", "feedback")  // Feedback loop
        .setParameter("delay", "delay_samples", 1.0f)
        .setParameter("gain", "value", 0.5f)
        .build();

    // 2. Configurar validador
    ValidationConfig config;
    config.check_types = true;
    config.check_semantics = true;
    config.check_performance = true;
    config.check_integration = true;
    config.cpu_budget = 0.5f;
    config.memory_budget = 1024 * 1024;
    config.target_platform = "vst3";

    TopologyValidator validator(config);

    // 3. Validar
    auto report = validator.validate(topology);

    // 4. Procesar resultados
    if (report.passed) {
        std::cout << "✅ Topology validation PASSED\n";

        // Mostrar métricas
        auto metrics = PerformanceValidator::estimate_performance(topology);
        std::cout << "\nPerformance:\n";
        std::cout << "  CPU: " << metrics.cpu_estimate * 100 << "%\n";
        std::cout << "  Memory: " << metrics.memory_usage / 1024 << " KB\n";

    } else {
        std::cout << "❌ Topology validation FAILED\n";

        // Mostrar errores
        for (const auto& error : report.errors) {
            std::cout << "\n[ERROR] " << error.message;
        }

        // Intentar auto-corrección
        auto fixes = ValidationFixer::generate_fixes(topology, report);
        std::cout << "\nAvailable fixes:\n";
        for (const auto& fix : fixes) {
            std::cout << "  " << (fix.automatic ? "[AUTO]" : "[MANUAL]")
                      << " " << fix.description << "\n";
        }

        // Aplicar correcciones automáticas
        Topology fixed = ValidationFixer::apply_automatic_fixes(topology, fixes);
        auto new_report = validator.validate(fixed);

        if (new_report.passed) {
            std::cout << "\n✅ Topology fixed and validated successfully!\n";
            topology = fixed;
        }
    }

    // 5. Warnings (incluso si pasó)
    if (report.warning_count() > 0) {
        std::cout << "\nWarnings:\n";
        for (const auto& warning : report.warnings) {
            std::cout << "  ⚠️  " << warning.message << "\n";
        }
    }

    return report.passed ? 0 : 1;
}

📈 Métricas de Rendimiento

Algoritmos

Algoritmo Complejidad Descripción
Type checking O(E) E = número de edges
Constraint validation O(V + E) V = nodos, E = edges
Semantic validation O(V) Por cada nodo
Performance estimation O(V + E) Suma de costos
BFS/DFS traversal O(V + E) Para análisis estructural

Presupuestos por Defecto

Plataforma CPU Budget Memory Budget
Desktop (VST3/AU) 50% 1 MB
Web Audio 30% 512 KB
Embedded 30% 64 KB
Mobile 40% 256 KB

Costos de Operaciones (ops/sample)

Operación Costo
Add/Multiply 1
Biquad filter 10
SVF filter 15
Sine oscillator 20
Exp/Log 30
FFT (512) 100
Delay 2

🔗 Integración con Otros Subsistemas

Con Graph Representation (05_05_00)

#include "../05_05_00_graph_representation/topology.hpp"

// Validar topología existente
auto result = validator.validate(topology);

Con Causality Validation (05_05_01)

// Semantic validator usa CausalityValidator internamente
auto result = SemanticValidator::validate_feedback_delay(topology);

Con Code Generation (05_05_06)

// Pre-validar antes de generar código
if (validator.validate(topology).passed) {
    auto code = CodeGenerator::generate(topology, ...);
}

🎯 Casos de Uso

1. Validación Pre-Compilación

// Validar antes de generar código
auto report = validator.validate(topology);
if (!report.passed) {
    std::cerr << "Cannot generate code: validation failed\n";
    return;
}

// Proceder con generación
auto code = CodeGenerator::generate(topology, ...);

2. Validación Interactiva (Editor)

// Validar en tiempo real durante edición
void on_topology_changed(const Topology& topology) {
    auto report = validator.validate(topology);

    // Actualizar UI con errores/warnings
    ui->clear_errors();
    for (const auto& error : report.errors) {
        ui->show_error(error.message, error.affected_nodes);
    }
}

3. Optimización Guiada

// Usar validador para optimización
auto metrics = PerformanceValidator::estimate_performance(topology);

if (metrics.cpu_estimate > 0.5f) {
    // Identificar hotspots
    auto hotspots = PerformanceValidator::identify_hotspots(topology);

    // Aplicar optimizaciones
    for (const auto& node_id : hotspots) {
        optimize_node(topology, node_id);
    }
}

4. Testing de Compatibilidad Multi-Plataforma

// Validar para múltiples plataformas
std::vector<std::string> platforms = {"vst3", "au", "web", "embedded"};

for (const auto& platform : platforms) {
    config.target_platform = platform;
    auto report = validator.validate(topology);

    std::cout << "Platform: " << platform << " → "
              << (report.passed ? "✅" : "❌") << "\n";
}

🚀 Estado del Sistema

  • Sistema de tipos: DataType, SignalDomain, ChannelConfig, PortType
  • Type checker: Verificación de compatibilidad con conversiones
  • Constraint validator: 5 restricciones estructurales por defecto
  • Semantic validator: 5 validadores DSP específicos
  • Performance validator: Estimación de CPU/memoria, hotspots
  • Integration validator: VST3, AU, Web Audio, Embedded
  • Topology validator: Validación comprehensiva configurable
  • Validation fixer: Generación y aplicación de correcciones

📚 Referencias

  1. Type Systems: Pierce, B. C. "Types and Programming Languages"
  2. DSP Validation: Smith, J. O. "Physical Audio Signal Processing"
  3. Performance Budgets: Reiss, J. D. "Audio Effects: Theory, Implementation and Application"
  4. Plugin Standards: Steinberg VST3 SDK, Apple Core Audio

Subsistema: 05_MODULES → 05_05_TOPOLOGY_DESIGN → 05_05_07_composition_rules Autor: AudioLab Development Team Versión: 1.0.0 Última actualización: 2025-10-10