Programación Genética

Cuando los algoritmos evolucionan sus propias fórmulas

Una clase introductoria para quienes nunca han trabajado con Programación Genética (GP). Recorreremos cada elemento del algoritmo, cómo se representa y cómo, generación tras generación, descubre por sí solo la fórmula x² + x.

Un árbol de expresión que evoluciona En la generación 0 el mejor programa es solo la hoja x, con error medio 4.67. Tras la evolución, en la generación 9, el mejor programa es el árbol más x con un subárbol x por x, es decir x al cuadrado más x, con error 0. x Gen. 0 MAE 4.67 evolución + * x x x (+ (* x x) x) Gen. 9 · MAE 0

El problema de la caja negra

01 — Por qué GP importa hoy

El Deep Learning es extraordinariamente potente, pero sus decisiones quedan repartidas en millones de parámetros que nadie puede leer. En dominios críticos —medicina, finanzas, defensa— no basta con acertar: hay que poder explicar, auditar y justificar cada predicción. Ahí entra GP.

Deep Learning

Una caja negra

Millones de pesos, decisión ininterpretable. Sabemos qué predice, pero no por qué.

w = [0.0213, −1.274, 0.889, 0.0041, −0.3317, 2.106, …] × 10⁶
GP

Una fórmula legible

Ecuación explícita, auditable, explicable. Cada término puede inspeccionarse, simplificarse y discutirse con un experto del dominio.

riesgo = 0.8·edad + presión / 10

«GP no te da una caja negra: te entrega una fórmula que puedes leer.»

¿Qué es GP?

02 — La idea central

GP es un algoritmo evolutivo que genera, combina y selecciona programas de computadora —normalmente fórmulas— imitando la selección natural, hasta encontrar uno que resuelva el problema.

CONCEPTO 1

Individuo = programa (árbol)

Cada solución candidata es un programa completo, representado como un árbol de operaciones y variables.

CONCEPTO 2

Población = muchos programas

Se trabaja con un conjunto de cientos o miles de programas candidatos a la vez, no con una sola solución.

CONCEPTO 3

Evolución = selección + cruce + mutación

Los mejores programas se reproducen y se modifican; generación tras generación, la población mejora.

Árbol de expresión de x² + x El nodo raíz es la suma. Su hijo izquierdo es una multiplicación de x por x y su hijo derecho es la hoja x. Los nodos internos son funciones (azul) y las hojas son terminales (verde). (+ (* x x) x) + * x x x ← raíz: salida del programa función → ← terminal (hoja) = x² + x
Se lee de abajo hacia arriba: primero x*x, luego se le suma x. En notación prefija (estilo Lisp, la de Koza): (+ (* x x) x).

GP evoluciona programas, no pesos.

La sección más importante

03 — Anatomía de GP: elementos y representación

Cada componente, con su representación visual y un ejemplo numérico sobre y = x² + x.

Problema guía: encontrar la fórmula que genera estos datos x = 1, 2, 3 → y = 2, 6, 12 · Funciones {+, −, *, /} · Terminales {x, 1}

3.1 · Individuo

Árbol de expresión

Un individuo es un programa completo, representado como un árbol cuya evaluación produce una salida.

  • Nodos internos (funciones): + − * / sin cos exp log if. Reciben argumentos de sus hijos.
  • Nodos hoja (terminales): variables x, y, z y constantes como 1 o 3.14.
  • Raíz: su valor es la salida del programa.
  • La profundidad mide la longitud del camino más largo desde la raíz; el tamaño, el número de nodos.

Ejemplo numérico · x = 2

Se evalúa desde las hojas hacia la raíz:

x = 2 → (* x x) = 4 → (+ 4 x) = 6 ✓ (y real = 6)

Profundidad = 2 · Tamaño = 5 nodos.

Árbol de x*x + x rotulado y evaluado en x = 2 Profundidad 0: raíz suma, vale 6. Profundidad 1: multiplicación que vale 4 y hoja x que vale 2. Profundidad 2: dos hojas x que valen 2. prof. 0 prof. 1 prof. 2 + * x x x 6 4 2 2 2 raíz = salida x*x + x

3.2 · Población

Un bosque de programas

La población es el conjunto de individuos (árboles) que coexisten en una generación.

  • Tamaño: típicamente de 100 a 10 000 individuos.
  • Profundidad máxima: limita el crecimiento de los árboles (Koza usó 17 tras el cruce).
  • Diversidad: formas y contenidos distintos; sin ella la búsqueda se estanca.
x #1 x
+ x 1 #2 x+1
* x x #3 x*x
+ x x #4 x+x
+ * x x x #5 x*x+x
Una población diminuta de 5 árboles (en la práctica serían cientos). La usaremos en todo el resto de la sección.
Población de ejemplo en la generación 0
IndividuoFórmulaNotación prefijaProfundidadTamaño
#1xx01
#2x + 1(+ x 1)13
#3x * x(* x x)13
#4x + x(+ x x)13
#5x * x + x(+ (* x x) x)25

3.3 · Función de aptitud

Fitness: ¿qué tan bueno es cada programa?

El fitness es un número que mide qué tan bien resuelve el problema cada individuo; es la única «brújula» que guía la evolución.

  • En regresión simbólica se usa un error, p. ej. el MAE (error absoluto medio): MAE = (1/n) · Σ |yᵢ − ŷᵢ|.
  • Para que «más alto sea mejor», lo convertimos: fitness = 1 / (1 + MAE), que vale 1 en el óptimo.

Ejemplo numérico · individuo #3 (x*x)

Predicciones: 1, 4, 9 frente a los valores reales 2, 6, 12.

|2−1| + |6−4| + |12−9| = 1 + 2 + 3 = 6
MAE = 6 / 3 = 2.00 → fitness = 1 / 3 = 0.333
Evaluación de la población sobre x = 1, 2, 3 (y real = 2, 6, 12)
Ind.Fórmulaŷ(1)ŷ(2)ŷ(3)MAEFitness 1/(1+MAE)
#1x1234.670.176
#2x + 12343.670.214
#3x * x1492.000.333
#4x + x2462.670.273
#5x * x + x óptimo26120.001.000

Un mal fitness = GP no converge o sobreajusta. Si la métrica no refleja lo que de verdad importa, la evolución optimizará lo equivocado.

3.4 · Selección

¿Quién se reproduce?

La selección elige qué individuos serán padres de la siguiente generación, favoreciendo a los de mejor fitness.

  • Si la presión es demasiado alta, todos copian al mejor y se pierde diversidad (convergencia prematura).
  • Si es demasiado baja, la búsqueda avanza al azar.

Presión selectiva sin perder diversidad.

Torneo (k = 3)

Se sortean k individuos al azar y gana el de mejor fitness. Con k controlamos la presión.

x#1 · MAE 4.67
x + x#4 · MAE 2.67
x * x#3 · MAE 2.00 · gana

Observa que el óptimo (#5) no fue sorteado: el torneo deja oportunidades a otros, y eso preserva diversidad.

Ruleta (proporcional al fitness)

La probabilidad de ser elegido es fitness / Σ fitness.

Elitismo

Los mejores pasan intactos a la siguiente generación, para no perderlos por azar. élite (1) → x*x + x se copia tal cual

3.5 · Cruce (crossover)

Intercambiar subárboles

El cruce elige un punto (nodo) en cada padre e intercambia los subárboles que cuelgan de ellos, generando dos hijos.

  • Es el operador principal de GP (suele aplicarse con probabilidad ≈ 0.8–0.9).
  • Los hijos heredan «bloques de construcción» de ambos padres.

Ejemplo numérico

P1 = x*x + x, P2 = x + 1. Se intercambia el subárbol x*x de P1 con la hoja x de P2:

Hijo 1 = x + x → ŷ = 2, 4, 6 (MAE 2.67)
Hijo 2 = x*x + 1 → ŷ = 2, 5, 10 (MAE 1.00)

El cruce explora combinaciones nuevas de bloques que ya funcionaban.

Ver ejemplo visual del cruceOcultar ejemplo visual
+ * x x x Padre 1 · x*x + x
+ x 1 Padre 2 · x + 1
+ x x Hijo 1 · x + x
+ * 1 x x Hijo 2 · x*x + 1

3.6 · Mutación

Pequeños cambios aleatorios

La mutación altera al azar una parte de un solo individuo para introducir material genético nuevo.

  • Evita que la población pierda funciones o terminales útiles.
  • Se aplica con tasa baja: entre 5 % y 10 % de los individuos.

Exploración secundaria; tasa baja (5–10 %).

Ver ejemplo visual de la mutaciónOcultar ejemplo visual
+ * x x x Antes · x*x + x
ŷ = 2, 6, 12
* * x x x Después (punto: + → *) · x*x*x
ŷ = 1, 8, 27
Aquí la mutación empeora al individuo (MAE pasa de 0 a 6). Es normal: la mutación explora, y la selección se encarga de descartar los cambios malos.
Tres tipos de mutación aplicados a x*x + x
TipoQué cambiaCambioResultadoŷ(1, 2, 3)
PuntoUn nodo por otro de la misma aridad (nº de argumentos).+ → *x*x*x1, 8, 27
SubárbolUn subárbol completo por uno nuevo generado al azar.x → (x − 1)x*x + (x − 1)1, 5, 11
HojaUn terminal por otro terminal (variable o constante).x → 2x*x + 23, 6, 11

3.7 · Inicialización

Crear la generación 0

La inicialización construye al azar los árboles de la primera población, respetando una profundidad máxima.

Define el punto de partida de la búsqueda.

Full

Solo funciones hasta llegar a la profundidad máxima; todas las ramas tienen la misma longitud. Árboles «llenos».

Grow

En cada nodo se elige al azar entre funciones y terminales; si sale un terminal, la rama se cierra. Formas irregulares.

Ramped half-and-half

Propuesto por Koza: reparte la población entre varias profundidades y, en cada una, mitad Full y mitad Grow. Maximiza la diversidad inicial.

+ 1 Raíz: función +
+ x 2 Hoja x: la rama se cierra
+ x * 3 Hijo derecho: función *
+ x * y 1 4 Hojas y, 1 → x + y*1
Método Grow con profundidad máxima 2 y conjunto de terminales {x, y, 1}. En el nivel máximo solo pueden elegirse terminales.

3.8 · Criterios de parada

¿Cuándo detener la evolución?

Un criterio de parada decide cuándo termina el bucle y se reporta el mejor individuo encontrado.

En la práctica se combinan varios: el primero que se cumpla detiene la ejecución.

Máximo de generaciones

Se fija un presupuesto de iteraciones.

G = 50 generaciones

Fitness objetivo

Se alcanzó un error suficientemente pequeño.

MAE ≤ 0.01

Estancamiento

El mejor fitness no mejora durante varias generaciones.

20 generaciones sin mejora

Tiempo máximo

Límite de tiempo o de evaluaciones de fitness.

60 s · 10⁵ evaluaciones

3.9 · Control de bloat

Árboles que engordan sin mejorar

El bloat es el crecimiento del tamaño de los árboles sin una mejora correspondiente del fitness.

Aparece código inútil («intrones») que no cambia la salida, pero vuelve los programas lentos de evaluar y difíciles de leer, justo lo contrario de lo que buscamos.

Ejemplo · mismo MAE = 0

x*x + x
5 nodos
x*x + x + (x − x) + 0*x
13 nodos

Límite de profundidad

Se rechazan los hijos que superen una profundidad máxima (Koza usó 17) y se conserva a un padre en su lugar.

Presión de parsimonia

Se penaliza el tamaño dentro del fitness: error + α · tamaño.

α = 0.01 → 0.05 (5 nodos) vs 0.13 (13 nodos)

Cruce size-fair

El subárbol que entra en el cruce debe tener un tamaño similar al que sale, para que los hijos no crezcan sin control.

Todo junto

El bucle completo de GP

Diagrama de flujo de GP Inicialización, luego evaluación del fitness, luego la pregunta ¿parada? Si la respuesta es sí, se entrega el mejor individuo. Si es no, se hace selección, cruce y mutación, se forma la nueva población y se vuelve a la evaluación. Inicialización Evaluación ¿Parada? Mejor individuo Selección Cruce Mutación Nueva población fitness de cada individuo Sí No generación t + 1 Diagrama de flujo de GP (vertical) Inicialización, evaluación, ¿parada? Si no: selección, cruce, mutación, nueva población y regreso a evaluación. Si sí: mejor individuo. Inicialización Evaluación ¿Parada? Selección Cruce Mutación Nueva población Mejor individuo No t+1 Sí

«Cada elemento tiene una representación concreta y un rol preciso en el bucle.»

Historia breve

04 — De Koza a 2026

Cuatro décadas de evolución de una idea: que los programas también pueden evolucionar.

  1. 1985

    Cramer: programas como árboles

    Nichael L. Cramer usa por primera vez una representación en árbol dentro de un algoritmo genético para evolucionar programas sencillos.

  2. 1989–1992

    Koza formaliza GP

    John R. Koza publica sus primeros trabajos y, en 1992, el libro fundacional Genetic Programming (MIT Press), que da nombre y método al campo.

  3. 2000s–2010s

    Consolidación

    Aceleración con GPU, resultados «competitivos con humanos» y regresión simbólica en la industria (p. ej. Eureqa, 2009).

  4. 2024–2026

    GP + LLMs

    Evolutionary Coding Agents que usan LLMs como operadores de variación, regresión simbólica a gran escala y herramientas patentadas como Beagle.

¿Por qué importa en 2026?

05 — Por qué GP es relevante hoy

05 / 01

Explicabilidad (XAI)

Las fórmulas que produce GP pueden auditarse término a término. Eso las hace adecuadas para entornos regulados, donde cada decisión automática debe poder justificarse ante un experto o un auditor.

05 / 02

Simbiosis con IA moderna

Los LLMs proponen mutaciones y cruces «inteligentes» de código; GP aporta la búsqueda evolutiva y la evaluación rigurosa. A esto se suman la simplificación simbólica de modelos y los Evolutionary Coding Agents.

05 / 03

Descubrimiento científico

La regresión simbólica busca la ecuación que explica los datos, no solo una predicción. Se usa para proponer leyes y relaciones en física, biología y finanzas que luego el científico puede validar.

Aplicaciones concretas

06 — Dónde se usa hoy

Industria 4.0

Optimización de procesos industriales

Enfoques de optimización con GP (OGP) en fábricas inteligentes: por ejemplo, evolucionar reglas de despacho para planificar tareas en talleres dinámicos.

Reglas que un ingeniero de planta puede leer, entender y aplicar.

Finanzas y defensa

Modelado financiero y defensa

Herramientas como Beagle aplican regresión simbólica a modelos predictivos; sus desarrolladores reportan una mejora del 61 % en la predicción de precios de vivienda.

Precisión competitiva con una fórmula que se puede auditar.

Agroalimentación y biomedicina

Modelos explicables en ciencias de la vida

Modelos simbólicos para estimar el contenido proteico de alimentos y para el monitoreo de enfermedades a partir de variables clínicas.

El experto del dominio puede validar cada variable del modelo.

Nota: las cifras de casos comerciales (p. ej. el 61 % de Beagle) son las reportadas por sus desarrolladores; conviene contrastarlas con la fuente original antes de citarlas.

Fronteras abiertas

07 — Líneas de investigación activas

Dónde se está trabajando hoy con más fuerza sobre GP. Cada línea incluye una referencia de entrada para quien quiera profundizar o elegir tema de tesis.

07 / 01

Genetic Improvement (GI)

En lugar de evolucionar programas desde cero, GP modifica software ya existente para reparar errores, acelerarlo o reducir su consumo de energía o memoria, sin perder su funcionalidad.

Reparación automáticaRendimientoIngeniería de software

Punto de entrada: Petke, J. et al. (2018). Genetic Improvement of Software: a Comprehensive Survey. IEEE Transactions on Evolutionary Computation, 22(3). · Le Goues, C. et al. (2012). GenProg. IEEE Transactions on Software Engineering, 38(1).

07 / 02

GP para IA explicable (XAI)

GP como modelo interpretable por diseño y también como herramienta para explicar a posteriori modelos de caja negra mediante fórmulas o reglas simbólicas sustitutas.

InterpretabilidadModelos sustitutosEntornos regulados

Punto de entrada: Mei, Y. et al. (2023). Explainable Artificial Intelligence by Genetic Programming: A Survey. IEEE Transactions on Evolutionary Computation, 27(3).

07 / 03

Regresión simbólica a escala

Comparación rigurosa de métodos con benchmarks comunes, mejores optimizadores de constantes y herramientas abiertas que llevan la regresión simbólica al trabajo científico diario.

SRBenchPySRDescubrimiento científico

Punto de entrada: La Cava, W. et al. (2021). Contemporary Symbolic Regression Methods and their Relative Performance. NeurIPS Datasets and Benchmarks. · Cranmer, M. (2023). PySR, arXiv:2305.01582.

07 / 04

GP semántica geométrica (GSGP) y GPU

Operadores que actúan sobre el comportamiento (la semántica) del programa y no solo sobre su sintaxis, con un espacio de búsqueda sin óptimos locales; su costo motiva implementaciones paralelas en GPU.

SemánticaCUDAInvestigación del autor

Punto de entrada: Moraglio, A., Krawiec, K. y Johnson, C. G. (2012). Geometric Semantic Genetic Programming. PPSN XII, LNCS 7491, Springer.

07 / 05

GP + LLMs

Los LLMs actúan como operadores de mutación y cruce sobre código, mientras el bucle evolutivo selecciona con un evaluador automático. Ya ha producido resultados nuevos en matemáticas y algoritmia.

Evolutionary Coding AgentsBúsqueda de programas

Punto de entrada: Romera-Paredes, B. et al. (2024). Mathematical discoveries from program search with large language models. Nature, 625, 468–475.

07 / 06

AutoML e hiperheurísticas

GP diseña pipelines completos de aprendizaje automático o reglas de decisión (por ejemplo, de planificación de producción) que luego se reutilizan en problemas nuevos.

TPOTSchedulingDiseño automático

Punto de entrada: Olson, R. S. et al. (2016). Evaluation of a Tree-based Pipeline Optimization Tool for Automating Data Science. GECCO 2016.

Preguntas transversales en todas estas líneas: controlar el bloat, mantener la diversidad (p. ej. selección lexicase) y reducir el costo de evaluar el fitness.

Lo que aún no está resuelto

08 — Problemas abiertos de GP

Tras más de treinta años, GP sigue teniendo retos fundamentales sin una solución definitiva. Son también buenas preguntas de tesis.

  1. Bloat y simplificación

    Los árboles crecen sin mejorar el fitness. Existen paliativos (límites, parsimonia), pero no una explicación completa de por qué ocurre ni una solución general.

    ¿Cómo obtener programas compactos sin frenar la búsqueda?

  2. Costo de evaluación

    Cada individuo se evalúa sobre todos los datos en cada generación. Con millones de registros o simulaciones costosas, el tiempo de cómputo se dispara.

    ¿Cómo escalar GP a grandes volúmenes de datos sin perder calidad?

  3. Generalización y sobreajuste

    Una fórmula puede ajustar perfectamente los datos de entrenamiento y fallar en datos nuevos. Medir y controlar la generalización de un programa sigue siendo difícil.

    ¿Qué propiedades de un programa predicen que generalizará bien?

  4. Optimización de constantes

    GP encuentra bien la estructura de una fórmula, pero ajustar sus constantes numéricas (p. ej. 3.14 o 0.8) solo con mutación y cruce es ineficiente.

    ¿Cómo combinar búsqueda simbólica y optimización numérica de forma eficiente?

  5. Teoría

    La teoría de GP (esquemas, análisis de tiempo de ejecución) explica casos sencillos, pero aún no predice el comportamiento en problemas realistas.

    ¿Cuándo y por qué converge GP, y en cuántas generaciones?

  6. Interpretabilidad medible

    Una fórmula legible no es automáticamente interpretable: 40 nodos son «legibles» pero nadie los entiende. Falta una medida aceptada de interpretabilidad.

    ¿Cómo cuantificar qué tan comprensible es un modelo para un humano?

  7. Síntesis de programas generales

    GP brilla en fórmulas, pero le cuesta evolucionar programas con bucles, recursión, estructuras de datos y módulos reutilizables.

    ¿Cómo escalar GP de expresiones a software real?

  8. Benchmarks y reproducibilidad

    Muchos estudios usan problemas de prueba demasiado fáciles o distintos entre sí, lo que dificulta comparar métodos de forma justa.

    ¿Qué conjunto de problemas representa de verdad los retos del mundo real?

Para una visión de conjunto: O’Neill, M., Vanneschi, L., Gustafson, S. y Banzhaf, W. (2010). Open issues in genetic programming. Genetic Programming and Evolvable Machines, 11(3–4), 339–363.

Demostración conceptual

09 — Cómo se ve funcionando

El problema

Solo damos a GP los datos y los operadores permitidos {+, −, *, /} con terminales {x, 1}. Nadie le dice que la respuesta es cuadrática.

(1, 2)(2, 6)(3, 12)
x Gen. 0 · x
MAE 4.67
+ x x Gen. 3 · x + x
MAE 2.67
+ * 1 x x Gen. 6 · x*x + 1
MAE 1.00
+ * x x x Gen. 9 · x*x + x
MAE 0 ✓
Mejor individuo de cada generación. En la generación 9, una mutación de hoja (1 → x, en naranja) completa la fórmula exacta.
Mejor individuo por generación (ejecución ilustrativa)
GeneraciónMejor individuoŷ(1, 2, 3)MAEFitness¿Qué ocurrió?
G0x1, 2, 34.670.176Población inicial aleatoria (ramped half-and-half).
G3x + x2, 4, 62.670.273El cruce combina hojas x bajo una suma.
G6x*x + 12, 5, 101.000.500El cruce inserta el bloque x*x (como en 3.5).
G9x*x + x exacto2, 6, 120.001.000Mutación de hoja 1 → x; se cumple el criterio de parada.

«GP descubre la fórmula sola; el humano solo define datos y operadores.»

10 — Cierre

Resumen

Cuatro ideas para recordar al salir de la clase.

  1. GP evoluciona programas representados como árboles de expresión.
  2. Cada elemento —fitness, selección, cruce, mutación, inicialización, parada, bloat— tiene una representación concreta y un rol en el bucle.
  3. GP produce fórmulas explicables, no cajas negras.
  4. En 2026 GP es clave en XAI, descubrimiento científico y sistemas híbridos con LLMs.

Para profundizar

11 — Referencias

Las obras fundacionales de GP y los textos de referencia para seguir estudiando.

  1. 1992
    Libro fundacional

    Koza, J. R. (1992). Genetic Programming: On the Programming of Computers by Means of Natural Selection. Cambridge, MA: MIT Press.

    La obra que da nombre y método al campo: árboles de expresión, cruce de subárboles, ramped half-and-half y decenas de problemas resueltos.

  2. 1989
    Artículo de congreso

    Koza, J. R. (1989). Hierarchical genetic algorithms operating on populations of computer programs. En Proceedings of the 11th International Joint Conference on Artificial Intelligence (IJCAI-89), Vol. 1, pp. 768–774.

    Primera presentación de Koza del enfoque que formalizaría en 1992.

  3. 1985
    Artículo de congreso

    Cramer, N. L. (1985). A representation for the adaptive generation of simple sequential programs. En Proceedings of the First International Conference on Genetic Algorithms and their Applications, pp. 183–187.

    Primer uso de programas representados como árboles dentro de un algoritmo genético.

  4. 1994
    Libro

    Koza, J. R. (1994). Genetic Programming II: Automatic Discovery of Reusable Programs. Cambridge, MA: MIT Press.

    Introduce las funciones definidas automáticamente (ADFs) para reutilizar subprogramas.

  5. 1998
    Libro de texto

    Banzhaf, W., Nordin, P., Keller, R. E. y Francone, F. D. (1998). Genetic Programming: An Introduction. San Francisco: Morgan Kaufmann.

    Introducción académica clásica, útil como texto de curso.

  6. 2002
    Libro

    Langdon, W. B. y Poli, R. (2002). Foundations of Genetic Programming. Berlín: Springer.

    Fundamentos teóricos: teoría de esquemas, espacio de búsqueda y bloat.

  7. 2008
    Libro de acceso abierto

    Poli, R., Langdon, W. B. y McPhee, N. F. (2008). A Field Guide to Genetic Programming. Lulu.com.

    Guía práctica y gratuita; el mejor punto de partida después de esta clase.

  8. 2010
    Artículo de revista

    O’Neill, M., Vanneschi, L., Gustafson, S. y Banzhaf, W. (2010). Open issues in genetic programming. Genetic Programming and Evolvable Machines, 11(3–4), 339–363.

    Revisión de los problemas abiertos del campo, base de la sección 08.

  9. 2009
    Artículo de revista

    Schmidt, M. y Lipson, H. (2009). Distilling free-form natural laws from experimental data. Science, 324(5923), 81–85.

    Regresión simbólica que redescubre leyes físicas a partir de datos (base de Eureqa).