26. arbol diarbol

Manual de Usuario: Diarbol Engine

Diarbol Engine es un ecosistema de visualización y análisis de probabilidad basado en árboles semánticos. No solo dibuja diagramas, sino que permite describir eventos lógicos mediante expresiones JSON, para luego resaltar automáticamente las trayectorias que cumplen ciertas condiciones.

Árboles de probabilidad
Eventos semánticos
DSL en JSON
Visualización educativa

Índice

1. Arquitectura del nodo

2. Referencia de funciones: DiarbolAuto

3. Motor de criterios (JSON DSL)

4. Ejemplos completos de uso

5. Recetario de expresiones frecuentes

6. Extensiones recomendadas del DSL

7. Buenas prácticas de diseño

1. Arquitectura del nodo

Para que el motor funcione correctamente, cada nodo debe contener más que una etiqueta visual. El árbol necesita metadatos semánticos para poder evaluar eventos.

{
  "nodeLabel": "C",
  "edgeLabel": "Cara",
  "value": 1,
  "tipo": "entero",
  "dominio": "moneda",
  "meta": {
    "origen": "auto"
  }
}
Propiedad Rol
nodeLabel Texto que aparece dentro del nodo.
edgeLabel Texto que aparece sobre la rama.
value Valor semántico utilizado en comparaciones y cálculos.
tipo Tipo del dato: entero, numero, racional o texto.
dominio Contexto del experimento: moneda, dado, urna, ruleta, binario, etc.
meta Información adicional opcional que puede viajar con la rama.
Idea clave: el motor visual dibuja ramas, pero el motor semántico interpreta los valores almacenados en cada trayectoria.
Importante: si vas a usar el DSL con sum, eq, gt, lt, gte o lte, cada rama debe llegar al árbol con su propiedad value correctamente definida.

2. Referencia de funciones: DiarbolAuto

El módulo DiarbolAuto es la fachada principal para construir árboles de manera rápida.

Función Descripción
dibujarEvento(container, opts) Renderiza un árbol a partir de una estructura root definida manualmente.
dibujarMoneda(container, opts) Genera automáticamente un árbol de lanzamientos de moneda.
dibujarDado(container, opts) Genera automáticamente un árbol de lanzamientos de dado.
dibujarRuleta(container, opts) Genera árboles basados en sectores o resultados de una ruleta.
dibujarExtraccionConReposicion(...) Modela extracciones donde cada elemento vuelve a la urna.
dibujarExtraccionSinReposicion(...) Modela extracciones donde el espacio muestral cambia en cada nivel.
dibujarBinario(container, opts) Genera árboles con dos ramas por nivel, típicamente éxito/fracaso, usando ramas explícitas como ramaA y ramaB.
dibujarExperimentoCompuesto(container, opts) Genera árboles por etapas, cada una con sus propias ramas y probabilidades.
dibujarCasos(container, opts) Dibuja trayectorias específicas definidas como una colección de casos con su camino.
Observación: los autos modernos deben propagar value, tipo, dominio y meta hasta el árbol final si se quiere usar DiarbolEventos encima.

3. Motor de criterios: JSON DSL

Los eventos se describen mediante expresiones JSON. Estas expresiones se registran con DiarbolEventos.definirEventoDesdeExpresion.

A. Operadores de valor

{ "pos": 1 }
{ "const": 7 }
{ "sum": [ { "pos": 1 }, { "pos": 2 } ] }

B. Operadores de comparación

{ "eq":  [ A, B ] }
{ "gt":  [ A, B ] }
{ "lt":  [ A, B ] }
{ "gte": [ A, B ] }
{ "lte": [ A, B ] }

C. Operadores lógicos

{ "and": [ {regla1}, {regla2} ] }
{ "or":  [ {regla1}, {regla2} ] }
{ "not": {regla} }
Importante: en tu semántica actual, expresiones como “es par” no significan una palabra especial, sino que deben interpretarse como “es múltiplo de 2”. Para eso conviene incorporar un operador como mod.

4. Ejemplos completos de uso

En los siguientes ejemplos se asume que ya están cargados los módulos: diarbol-core.js, diarbol-auto.js y diarbol-eventos.js.

4.1. Ejemplo base HTML

<script src="diarbol-core.js"></script>
<script src="diarbol-auto.js"></script>
<script src="diarbol-eventos.js"></script>

<div id="arbol-ejemplo"></div>

4.2. Tres monedas: “al menos 2 caras”

Supuesto semántico: Cara = 1 y Sello = 0.

const inst = DiarbolAuto.dibujarMoneda('#arbol-ejemplo', {
  lanzamientos: 3
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'alMenos2Caras', {
  "gte": [
    {
      "sum": [
        { "pos": 1 },
        { "pos": 2 },
        { "pos": 3 }
      ]
    },
    { "const": 2 }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'alMenos2Caras', {
  color: '#27ae60'
});

4.3. Dos dados: “la suma es mayor o igual que 8”

const inst = DiarbolAuto.dibujarDado('#arbol-dados', {
  lanzamientos: 2
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'sumaMayorIgual8', {
  "gte": [
    {
      "sum": [
        { "pos": 1 },
        { "pos": 2 }
      ]
    },
    { "const": 8 }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'sumaMayorIgual8', {
  color: '#ef6c00'
});

4.4. Tres monedas: “la primera es cara y el total de caras es 2”

const inst = DiarbolAuto.dibujarMoneda('#arbol-3monedas', {
  lanzamientos: 3
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'eventoA', {
  "and": [
    { "eq": [ { "pos": 1 }, { "const": 1 } ] },
    {
      "eq": [
        {
          "sum": [
            { "pos": 1 },
            { "pos": 2 },
            { "pos": 3 }
          ]
        },
        { "const": 2 }
      ]
    }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'eventoA', {
  color: '#1565c0'
});

4.5. Extracción con reposición: “ambas son rojas”

const inst = DiarbolAuto.dibujarExtraccionConReposicion('#urna1', {
  extracciones: 2,
  elementos: [
    { label: 'Roja', nodeLabel: 'R', value: 1, count: 3, tipo: 'entero', dominio: 'urna' },
    { label: 'Azul', nodeLabel: 'A', value: 0, count: 2, tipo: 'entero', dominio: 'urna' }
  ]
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'dosRojas', {
  "and": [
    { "eq": [ { "pos": 1 }, { "const": 1 } ] },
    { "eq": [ { "pos": 2 }, { "const": 1 } ] }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'dosRojas', {
  color: '#c62828'
});

4.6. Extracción sin reposición: “al menos una roja”

const inst = DiarbolAuto.dibujarExtraccionSinReposicion('#urna2', {
  extracciones: 2,
  elementos: [
    { label: 'Roja', nodeLabel: 'R', value: 1, count: 2, tipo: 'entero', dominio: 'urna' },
    { label: 'Azul', nodeLabel: 'A', value: 0, count: 1, tipo: 'entero', dominio: 'urna' }
  ]
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'alMenosUnaRoja', {
  "gte": [
    {
      "sum": [
        { "pos": 1 },
        { "pos": 2 }
      ]
    },
    { "const": 1 }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'alMenosUnaRoja', {
  color: '#2e7d32'
});

4.7. Binario: “exactamente 3 éxitos en 4 ensayos”

Supuesto semántico: Éxito = 1 y Fracaso = 0.

const inst = DiarbolAuto.dibujarBinario('#binario', {
  niveles: 4,
  ramaA: {
    label: 'Éxito',
    nodeLabel: 'E',
    value: 1,
    tipo: 'entero',
    dominio: 'binario'
  },
  ramaB: {
    label: 'Fracaso',
    nodeLabel: 'F',
    value: 0,
    tipo: 'entero',
    dominio: 'binario'
  }
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'exactamente3Exitos', {
  "eq": [
    {
      "sum": [
        { "pos": 1 },
        { "pos": 2 },
        { "pos": 3 },
        { "pos": 4 }
      ]
    },
    { "const": 3 }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'exactamente3Exitos', {
  color: '#6a1b9a'
});

4.8. Experimento compuesto: moneda y ruleta

const inst = DiarbolAuto.dibujarExperimentoCompuesto('#compuesto', {
  title: 'Moneda + ruleta',
  caption: 'Experimento compuesto con 2 etapas',
  stageLabels: ['Inicio', 'Moneda', 'Ruleta', 'Resultado final'],
  interaction: {
    mode: 'capture'
  },
  mostrarConteos: false,
  mostrarProbTotal: true,
  crearLineasLeaf: function(path, totalProb) {
    var p1 = path[0] ? String(path[0].label).toLowerCase() : '';
    var p2 = path[1] ? String(path[1].label) : '';
    var lineas = [];

    if (p1 && p2) {
      lineas.push('(' + p1 + ',' + p2 + ')');
    }

    if (totalProb && window.DiarbolCore) {
      lineas.push('P = ' + window.DiarbolCore.formatearRacional(totalProb));
    }

    return lineas;
  },
  etapas: [
    {
      nombre: 'Moneda',
      ramas: [
        { label: 'Cara', nodeLabel: 'C', prob: '1/2', value: 1, tipo: 'entero', dominio: 'moneda' },
        { label: 'Sello', nodeLabel: 'S', prob: '1/2', value: 0, tipo: 'entero', dominio: 'moneda' }
      ]
    },
    {
      nombre: 'Ruleta',
      ramas: [
        { label: '1', nodeLabel: '1', prob: '1/3', value: 1, tipo: 'entero', dominio: 'ruleta' },
        { label: '2', nodeLabel: '2', prob: '1/3', value: 2, tipo: 'entero', dominio: 'ruleta' },
        { label: '3', nodeLabel: '3', prob: '1/3', value: 3, tipo: 'entero', dominio: 'ruleta' }
      ]
    }
  ]
});

DiarbolEventos.definirEventoDesdeExpresion(inst, 'caraYdos', {
  "and": [
    { "eq": [ { "pos": 1 }, { "const": 1 } ] },
    { "eq": [ { "pos": 2 }, { "const": 2 } ] }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'caraYdos', {
  color: '#5e35b1'
});

4.9. Casos manuales

const inst = DiarbolAuto.dibujarCasos('#casos', {
  rootLabel: 'I',
  casos: [
    {
      lines: ['Pan A + Carne X'],
      camino: [
        { label: 'Pan A', nodeLabel: 'A', value: 'A', tipo: 'texto', dominio: 'pan' },
        { label: 'Carne X', nodeLabel: 'X', value: 'X', tipo: 'texto', dominio: 'carne' }
      ]
    },
    {
      lines: ['Pan A + Carne Y'],
      camino: [
        { label: 'Pan A', nodeLabel: 'A', value: 'A', tipo: 'texto', dominio: 'pan' },
        { label: 'Carne Y', nodeLabel: 'Y', value: 'Y', tipo: 'texto', dominio: 'carne' }
      ]
    },
    {
      lines: ['Pan B + Carne X'],
      camino: [
        { label: 'Pan B', nodeLabel: 'B', value: 'B', tipo: 'texto', dominio: 'pan' },
        { label: 'Carne X', nodeLabel: 'X', value: 'X', tipo: 'texto', dominio: 'carne' }
      ]
    }
  ]
});

Este caso sirve cuando el árbol no proviene de un experimento estándar, sino de una colección de trayectorias específicas.

4.10. Árbol manual mediante root

const root = {
  nodeLabel: 'I',
  children: [
    {
      nodeLabel: 'C',
      edgeLabel: 'Cara',
      value: 1,
      tipo: 'entero',
      dominio: 'moneda',
      children: [
        { nodeLabel: 'C', edgeLabel: 'Cara', value: 1, tipo: 'entero', dominio: 'moneda' },
        { nodeLabel: 'S', edgeLabel: 'Sello', value: 0, tipo: 'entero', dominio: 'moneda' }
      ]
    },
    {
      nodeLabel: 'S',
      edgeLabel: 'Sello',
      value: 0,
      tipo: 'entero',
      dominio: 'moneda',
      children: [
        { nodeLabel: 'C', edgeLabel: 'Cara', value: 1, tipo: 'entero', dominio: 'moneda' },
        { nodeLabel: 'S', edgeLabel: 'Sello', value: 0, tipo: 'entero', dominio: 'moneda' }
      ]
    }
  ]
};

const inst = DiarbolAuto.dibujarEvento('#manual', { root });

DiarbolEventos.definirEventoDesdeExpresion(inst, 'unaCaraExacta', {
  "eq": [
    {
      "sum": [
        { "pos": 1 },
        { "pos": 2 }
      ]
    },
    { "const": 1 }
  ]
});

DiarbolEventos.resaltarEvento(inst, 'unaCaraExacta', {
  color: '#00897b'
});

5. Recetario de expresiones frecuentes

El evento 1 es 4

{ "eq": [ { "pos": 1 }, { "const": 4 } ] }

El evento 1 es menor que 3

{ "lt": [ { "pos": 1 }, { "const": 3 } ] }

Evento 2 + evento 3 es 7

{
  "eq": [
    {
      "sum": [
        { "pos": 2 },
        { "pos": 3 }
      ]
    },
    { "const": 7 }
  ]
}

Evento 1 y evento 2 son iguales

{
  "eq": [
    { "pos": 1 },
    { "pos": 2 }
  ]
}

Evento 1 es menor que 3 y evento 2 + evento 3 es par

Esto requiere el operador mod. Sin él, la idea existe, pero no queda expresable de forma natural.

{
  "and": [
    { "lt": [ { "pos": 1 }, { "const": 3 } ] },
    {
      "eq": [
        {
          "mod": [
            {
              "sum": [
                { "pos": 2 },
                { "pos": 3 }
              ]
            },
            { "const": 2 }
          ]
        },
        { "const": 0 }
      ]
    }
  ]
}

Evento 1 y evento 2 son iguales y evento 3 es 5

{
  "and": [
    {
      "eq": [
        { "pos": 1 },
        { "pos": 2 }
      ]
    },
    {
      "eq": [
        { "pos": 3 },
        { "const": 5 }
      ]
    }
  ]
}

6. Extensiones recomendadas del DSL

El DSL actual ya es útil, pero para la semántica que quieres manejar, conviene agregar algunos operadores nuevos.

6.1. Operador mod

Permite representar paridad y residuos.

{
  "eq": [
    {
      "mod": [
        { "pos": 1 },
        { "const": 2 }
      ]
    },
    { "const": 0 }
  ]
}

Interpretación: el evento 1 es par.

6.2. Operador countEq

Cuenta cuántas posiciones son iguales a un valor dado.

{
  "gte": [
    {
      "countEq": {
        "items": [
          { "pos": 1 },
          { "pos": 2 },
          { "pos": 3 }
        ],
        "value": { "const": 1 }
      }
    },
    { "const": 2 }
  ]
}

Interpretación: al menos dos posiciones valen 1.

6.3. Operador allEq

Permite expresar que varios eventos son iguales.

{
  "allEq": [
    { "pos": 1 },
    { "pos": 2 },
    { "pos": 3 }
  ]
}

6.4. Operador between

Permite describir intervalos.

{
  "between": [
    { "pos": 1 },
    { "const": 2 },
    { "const": 5 }
  ]
}

6.5. Operador in

Útil para dominios de texto o pertenencia a conjuntos de valores.

{
  "in": [
    { "pos": 1 },
    [ { "const": 2 }, { "const": 4 }, { "const": 6 } ]
  ]
}
Recomendación de diseño: si quieres que el sistema soporte frases como “evento 1 es par”, “al menos 2 caras”, “todos iguales” o “pertenece a cierto conjunto”, entonces mod, countEq, allEq e in serían extensiones muy naturales.

7. Buenas prácticas de diseño

Separar el dibujo de la semántica

El módulo de render debería encargarse de la visualización, mientras que el módulo de eventos debería encargarse de interpretar reglas y evaluar hojas.

Usar value de forma consistente

Si en monedas defines Cara = 1 y Sello = 0, mantén esa convención en todos los árboles equivalentes.

Distinguir dominio y tipo

El campo tipo describe la naturaleza del valor, mientras que dominio describe el contexto del experimento.

Permitir callbacks en la configuración

Si un auto acepta funciones como crearLineasLeaf, el clonado profundo y la mezcla de configuración deben preservarlas sin romperse.

Construir primero una sintaxis clara

Antes de ampliar el motor, conviene dejar estabilizada la sintaxis interna de las expresiones. Eso evita parches después.

Nota técnica: si el sistema usa un núcleo racional con MCD, entonces las probabilidades pueden mantenerse exactas sin depender del redondeo de punto flotante.
Manual base de referencia para Diarbol Engine.