La primera aventura de Niell
Buscamos tiempo emergente, encontramos geometría radial
Veinte versiones de causal sets escritos en Niell, el lenguaje que fuimos ajustando en paralelo.
1. El punto de partida
Hay muchas cosas que solemos dar por sentado. También hay cosas en las que no nos detenemos. Tal vez porque no nos parecen tan desconocidas. A veces parece que conocemos las cosas que nos rodean, pero ¿qué tanto es así? En uno de esos tiempos muertos entre medio de la rutina estuve reflexionando sobre la naturaleza del tiempo. ¿Qué es el tiempo? ¿Es algo que existe por sí mismo, o es una propiedad emergente de algo más fundamental? Estas preguntas me llevaron a explorar un modelo del universo basado en eventos y relaciones causales, conocido como causal set.
Imagina un universo hecho solo de eventos. Cada evento es un punto con la propiedad de haber ocurrido, y nada más. Entre dos eventos hay una sola relación posible: uno pudo haber causado al otro, o no hay ninguna conexión causal entre ellos. No hay coordenadas espaciales pre-existentes. No hay un reloj externo. Solo el patrón de quién precede causalmente a quién.
Ese patrón se llama causal set (o causet, para abreviar): un conjunto de eventos con una relación de orden parcial que codifica "el pasado de A incluye a B". Es la unidad mínima de un modelo del universo donde la geometría del espacio-tiempo no se asume desde afuera — se deja emerger desde adentro.
Esa idea es contraintuitiva. En la vida cotidiana damos por sentado que existe un espacio tridimensional con tiempo lineal, y que los eventos ocurren dentro de ese escenario preexistente. Los causal sets invierten la relación: el escenario no está dado desde afuera. Solo hay eventos y relaciones causales entre ellos; la geometría del espacio-tiempo — las distancias, las direcciones, el propio "tiempo" — es lo que aparece cuando el orden causal es suficientemente denso.
La pregunta que motivó este proyecto es directa: ¿el "tiempo" que percibimos en el universo emerge como consecuencia del orden causal, o requiere un eje pre-existente para tener sentido? La hipótesis natural que quisimos poner a prueba es la primera. Si tenemos un causet suficientemente rico, entonces algún operador matemático aplicado al grafo de relaciones causales — un mapa de nodos (eventos) conectados por líneas (relaciones causales) — debería reproducir el "tiempo emergente" como su modo fundamental: el patrón más simple que el operador detecta en el grafo.
Para hacer la pregunta operacional, generamos causets sintéticos con un procedimiento llamado sprinkling: tirar N eventos al azar dentro de un dominio pequeño (una hoja del universo de juguete, en dos dimensiones) y definir la relación causal como "el evento A precede al evento B si B cae dentro del cono de luz futuro de A" — el conjunto de puntos a los que una señal partiendo de A podría llegar viajando a la velocidad de la luz o menos, y que por lo tanto A puede haber influenciado. Sobre el causet resultante podemos aplicar operadores matemáticos y medir si sus modos fundamentales se alinean con la "altura temporal" natural de cada evento — su posición en el orden topológico.
A lo largo de veinte versiones sucesivas corrimos experimentos, refutamos hipótesis y refinamos la pregunta. La respuesta que tenemos hoy no es la que buscábamos al empezar, pero es honesta y publicable — y eso, más que cualquier otra cosa, es lo que el proyecto tenía como objetivo producir.
2. Cómo Niell creció con este proyecto
Antes de contar qué encontramos, hay que contar una cosa complementaria: el proyecto se escribió en Niell, el lenguaje de programación experimental que también desarrollamos en paralelo. Cada versión del explorador de causets ejercitó capacidades del lenguaje y descubrió qué le faltaba y cómo podía crecer. Esa es la función complementaria que el proyecto cumple mientras investiga el tiempo emergente: ser el forcing function que empuja al lenguaje a madurar, un caso de uso real que expone gaps concretos y exige soluciones estructurales.
Dos ejemplos concretos que nacieron durante el trabajo con causets y volvieron al compilador como cambios permanentes:
Gap #22 — la keyword `idiv`. Al principio, el compilador de Niell trataba a/b con enteros como una división flotante silenciosa: 7 / 2 devolvía 3.5, aunque los dos operandos fueran enteros. Escribiendo código de conteo de aristas del causet tropezamos varias veces con el mismo problema: queríamos el cociente entero pero terminábamos con un float que había que redondear después, y el redondeo silencioso ocultaba bugs. La decisión que salió de esa fricción fue estructural: el compilador ahora rechaza int / int con un error de tipo explícito y obliga a usar la keyword nueva idiv cuando la intención es división entera con semántica floor. 7 idiv 2 devuelve 3. 7 / 2 es un error al compilar. La regla implícita es explicit numeric intent: el compilador prefiere que el programador diga lo que quiere en vez de adivinar. El cambio se incorporó en niell-lang v0.3.0 (ver la sección de tipos del lenguaje para la semántica formal del operador idiv).
Gap #25 — sprinkling para N > 25. El sampler nativo de Niell — la función que genera los eventos aleatorios del causet — funcionaba bien para causets chicos (N ≤ 25) pero con N ≥ 80 el programa terminaba abruptamente con un crash (un error de acceso a memoria del que el compilador debería haber protegido al programa). Era un bug del compilador, no del algoritmo del sampler. Diagnosticar el problema nos costó tiempo y nos forzó a un workaround intermedio: migrar temporalmente el sampler a Python para las versiones que necesitaban N grande, y mantener el sampler Niell nativo como referencia canónica. El gap se cerró upstream en niell-lang v0.2.1 con un fix al manejo de arreglos grandes en el compilador. Después de ese fix, el sampler nativo corre sin problemas para causets de cientos de eventos, y en las versiones tardías del proyecto lo usamos como discriminante independiente contra el sampler Python.
El patrón se repite: cada versión del proyecto es un test de estrés del lenguaje, y escribir el explorador nos obligó a decidir cómo debe comportarse Niell en casos que ningún proyecto anterior había expuesto. El compilador salió más estricto. El proyecto salió más honesto. Los dos lados ganaron.
3. El recorrido
El arco pasó por cuatro fases distinguibles. Contarlas rápido ayuda a entender por qué el resultado final combina un hallazgo positivo firme con un caveat honesto sobre lo que quedó fuera del alcance de esta primera aventura.
Fase 1 — Los operadores hermitianos no detectan tiempo (v0.4-v0.7). Empezamos por lo obvio: operadores lineales tradicionales — Laplacianos, Magnetic Laplacians con distintas fases. Calculamos su modo fundamental y medimos si correlacionaba con la altura temporal h(e) de cada evento, usando ensembles de 30 rewires aleatorios como anti-control. Todos los resultados cayeron en zona de ruido — el z-score (cuántas desviaciones estándar separan al resultado del ruido esperado, con |z| < 3 = ruido y |z| > 3 = señal fuerte) quedó pegado a cero para todos los operadores probados. Cerramos la familia hermitiana como refutada.
Fase 2 — Sorkin detecta algo, pero no sabemos qué (v0.8.0-v0.8.1). Pivote al operador de Sorkin, también llamado d'Alembertian discreto: un operador no hermitiano, explícitamente lorentziano, que aproxima el operador de ondas del universo □ = -∂²t + ∇² con pesos ponderados sobre el cono de luz del pasado inmediato de cada evento. Es la contraparte discreta del operador que rige la propagación de ondas en relatividad especial. Aplicamos tres lecturas en paralelo (norma estructural, ecuación de onda, acción local). A N=400 aparecieron z-scores fuertes que sobrevivieron dos anti-controles DAG-preserving. Un anti-control DAG-preserving es un algoritmo que reordena las aristas del grafo sin romper la propiedad de que el resultado siga siendo un orden causal válido — probamos uno por permutación de pares causales y otro por perturbación local de aristas de Hasse. El operador de Sorkin detecta algo real y no trivial sobre el causet sprinkled. Todavía no sabíamos qué.
Fase 3 — Diagnóstico interpretativo: no es tiempo, es geometría (v0.8.2-v0.8.5). Para descubrir qué mide el operador, aplicamos ‖B·φ‖ a cuatro campos candidatos sobre los eventos: φ_const (control trivial), φ_h (el gradient causal — el candidato natural para "tiempo emergente"), φ_radial (control geométrico, distancia al centro del dominio) y φ_random (control negativo). Si el operador de Sorkin detectara tiempo emergente, φ_h debía dar la señal más fuerte. El resultado fue el opuesto exacto: `φ_h` quedó al fondo del ranking, y φ_radial al tope. Cinco spikes adicionales (sensibilidad al centro, descomposición temporal/espacial, cell-by-cell entre dos generadores independientes, derivación analítica del scaling, features locales por evento) confirmaron el hallazgo con precisión cuantitativa: el operador de Sorkin no detecta el orden temporal del DAG — detecta una propiedad geométrica anclada a las coordenadas del sprinkling. Lo que Sorkin detecta es geometría radial (la síntesis técnica tiene el detalle numérico de los cinco spikes).
Fase 4 — El intento de refinamiento (v0.9.x, adelanto). Con la señal identificada como geométrica-radial y no temporal, la siguiente pregunta natural era: si movemos el "centro" del campo radial de un lado del dominio al otro, ¿la señal se mueve acordemente? El intento nos llevó tres versiones y terminó en un lugar que ninguno anticipaba — un resultado del que el Capítulo 5 se ocupa con más calma.
4. Lo que encontramos
El hallazgo central del proyecto es más modesto que la hipótesis original y más sólido que ella. Lo podemos formular en una oración: el operador de Sorkin detecta una propiedad geométrica radial genuina del causet sprinkled, con un scaling asintótico predicho por la geometría continua de Minkowski.
Tres pilares cuantitativos lo sostienen. Primero, el ranking: después de una normalización por baseline empírico, φ_radial queda al tope de |z_normalized| en las 4 de 4 combinaciones de sampler × anti-control probadas, y φ_h_normalized — el candidato natural para "tiempo emergente" — queda al fondo en las mismas 4 de 4. El patrón es robusto entre generadores independientes (Python y sampler nativo Niell).
Segundo, el scaling. La magnitud de la señal crece con el tamaño del causet según un exponente α = 0.6232, medido con error relativo menor al 0.51% sobre N ∈ 500, 1000, 2000. Ese exponente cae dentro del rango teórico [0.5, 0.7] predicho por la geometría continua de Minkowski (el espacio-tiempo plano de la relatividad especial) más la cancelación parcial del kernel discreto del operador de Sorkin. La derivación no es post-hoc: salió de un análisis paper-and-pencil aplicado antes de correr los experimentos que la validaron.
Tercero, la interpretación. Lo que Sorkin detecta no es tiempo emergente sino la distribución radial de eventos alrededor del centro geométrico del dominio. No es "cuándo ocurrió cada evento" — es "qué tan lejos del centro está el evento". El framing importa: no hemos detectado la métrica del universo emergente, hemos detectado que un operador lorentziano discreto responde a la organización radial de los eventos que se le pasan como input. Es un resultado real, publicable, y significativamente menos glamoroso que "tiempo emergente".
Ese hallazgo se estableció con el origen del campo radial fijo en el centro geométrico del dominio, (t₀, x₀) = (0.5, 0.5). Es la variante que sobrevive todo el arco y motiva el capítulo siguiente.
5. Lo que no salió como esperábamos
Si la señal es geométrica y está anclada a coordenadas, mover el "centro" del campo radial de un lado del dominio al otro debería mover el máximo del observable de forma predecible. Esa es la lógica del intento observer-relativity — un enfoque donde cada elección del centro representa a un "observador" distinto sobre el mismo causet, y el hallazgo tiene que ser consistente cross-observador para considerarse genuino.
Lo intentamos en tres versiones sucesivas y descubrimos que el framework no es testeable en este stack.
En v0.9.0 corrimos un grid de 16 orígenes paramétricos sobre 4 tamaños de causet y 2 anti-controles. Las correlaciones cross-block cruzaron cero, sin patrón claro. En v0.9.1 descompusimos el observable entre el interior del cuadrado del sprinkling (bulk) y el borde (boundary): la separación boundary vs bulk — que distingue eventos cerca del perímetro del dominio de eventos interiores — reveló que la señal correlacional vivía casi exclusivamente en el boundary y colapsaba a cero en el bulk. En v0.9.2 descubrimos que el problema era del sampler: sprinkle_2d_hasse (el sampler Python) hacía una operación auxiliar de argsort — una ordenación de los eventos por su coordenada temporal antes de aplicar la regla causal del cono de luz — que introducía un sesgo direccional cross-origen. Con el sampler nativo Niell (que no ordena por t), la correlación near-boundary desaparecía. En v1.0 aislamos la variable escribiendo un flag argsort=True|False sobre el sampler Python: con argsort activo, el patrón v0.9.1 reaparece; sin argsort, colapsa a la magnitud del sampler Niell.
El resultado neto es un resultado negativo genuino. El framework observer-relativity no queda refutado como idea, pero no es testeable con la métrica que definimos y los samplers disponibles. La correlación cross-origen que reportábamos en v0.9.0 era artefacto del stack, no propiedad del modelo. La señal radial fija del Capítulo 4 sigue siendo genuina — lo que falla es el intento de refinamiento.
Comunicar "detectamos la métrica del universo emergente" habría sido una sobreventa fácil. La versión correcta — "detectamos algo real y localizado, y también documentamos un intento de refinamiento que fracasó por razones del stack" — es la que corresponde publicar.
6. Lo que queda
El arco cierra, pero varias líneas quedan vivas.
Forcing function para Niell. El rol continúa post-arco. Nuevas exploraciones sobre causets — variaciones de operadores, extensión a Minkowski 3D, ensembles sobre múltiples sprinklings independientes — probablemente refinarán capacidades del lenguaje. El proyecto es un banco de pruebas persistente.
Reframing conceptual. La pregunta original ("¿existe un operador cuyo modo fundamental sea el tiempo emergente?") tiene respuesta negativa clara. La refinada post-v0.8.2 ("¿qué propiedad detecta Sorkin que los Hermitianos no?") tiene respuesta parcial con caveat. Un reframing productivo para trabajo futuro: ¿existe una familia de operadores lorentzianos sobre causets cuya métrica cross-origen sea invariante al sampling algorithm? — pregunta metodológica más que física.
Dos preguntas que quedan pendientes. Extender el experimento a causets mucho más grandes (N ≥ 500) y afinar la separación entre el interior y el borde del dominio del sprinkling son ideas naturales, pero las dejamos deliberadamente sin ejecutar. Sin un observable que sea invariante al sampler primero, correr con más eventos o más resolución de cutoff solo amplifica lo que ya sabemos — no agrega información nueva.
Cierre
El código completo del proyecto está en github.com/niell-lang/causet — libre de leer, clonar, o replicar los resultados desde cero. Si te interesa probar Niell (o corregir alguno de los bugs que este proyecto todavía no descubrió), la instalación del compilador está documentada en niell.dev/install.
---
Apéndice — referencias técnicas
Documentos técnicos del arco, ordenados por profundidad decreciente:
- `docs/v1.0-sintesis.md` — síntesis técnica del arco completo (v0.4 → v1.0), 5 capítulos + apéndice.
- `docs/v0.8.5-sintesis.md` — síntesis interpretativa v0.4 → v0.8.4, con detalle del hallazgo radial.
- `docs/v0.9-sintesis.md` — contexto v0.9.0 mixed → v0.9.1 boundary → v0.9.2 artifact.
- `docs/v1.0-resultados.md` — reporte mecánico sub-tests A/B/C del sampler Python
sprinkle_2d_hasse.
El apéndice es opcional; el lector casual puede saltarlo.