Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

No2 - ODEs y NODEs

Fecha: 13/04/2026

Estas notas cubren la definición de Ecuaciones Diferenciales Ordinarias (ODEs), su aplicación en modelado dinámico, la conversión de otros tipos de ecuaciones a ODEs, y cómo se utilizan para la estimación de parámetros a partir de datos observacionales, culminando con la introducción a las Neural ODEs (NODEs).

Ecuaciones diferenciales ordinarias (ODEs)

Las ODEs describen la evolución de un sistema en función de una única variable independiente, típicamente el tiempo tt.

Definición y Parámetros

Una ODE queda definida de la siguiente manera:

Salvo casos muy particulares, las soluciones a ODEs no son analiticias. En la mayoria de los casos, vamos a recurrir a métodos numéricos para resolver dichas ecuaciones (ver Clase 4).

Ejemplos

El Modelo Dinámico: Lotka-Volterra (Depredador-Presa)

Este modelo describe la dinámica temporal del estado de un sistema compuesto por dos poblaciones: conejos (xx) y lobos (yy).

Intuición de la dinámica:

Las ecuaciones del modelo estan entonces dadas por

dxdt=αxγxy\frac{dx}{dt} = \alpha x - \gamma xy

dydt=βy+ηxy\frac{dy}{dt} = -\beta y + \eta xy

Componentes del sistema:

Inferencia estadística

En la realidad, si uno tuviera conocimiento absoluto de la dinámica, conocería la trayectoria perfecta. Sin embargo, nunca se observan estas trayectorias puras. En su lugar, se observan datos, usualmente en tiempos discretos, que se asemejan a la trayectoria subyacente, pero posiblemente contaminados con ruido aleatorio.

Ecuación del modelo observacional. Vamos a considerar un modelo para los datos de la forma

xiobs=x(ti;θ)+εix_i^{\text{obs}} = x(t_i; \theta) + \varepsilon_i

Donde:

Características del ruido observacional εi\varepsilon_i. Ejemplos comununes incluyen:

Ajuste de Trayectorias

El problema central es: dadas las observaciones xiobsx_i^{\text{obs}} e yiobsy_i^{\text{obs}}, ¿cómo estimamos los parámetros θ\theta que mejor describen la dinámica subyacente? El objetivo es convertir esto en un problema de optimización, buscando minimizar una función de costo L(θ;DATOS)\mathcal{L}(\theta; \text{DATOS}) que compare las observaciones reales con las trayectorias generadas por el modelo x(t;θ)x(t; \theta). Esto se va a realizar mediante el método de cuadrádos mínimos no lineales, el cual en el contexto de este curso llamaremos ajuste de trayectorias (trajectory matching) Ramsay & Hooker (2017):

Ajuste de Trayectorias: A diferencia del fiteo lineal de una recta, acá se compara con una función que depende de θ\theta de manera no trivial.

minθi=1N(xiobsx(ti;θ))2\min_{\theta} \sum_{i=1}^{N} (x_i^{\text{obs}} - x(t_i; \theta))^2

Más adelante veremos que minimizar el cuadrado de los errores asume inherentemente un ruido de naturaleza Gaussiana.

Ejemplo (continuado)

Inferencia en el sistema Lotka-Volterra: Si salimos al campo y medimos las poblaciones de presas (xiobsx_i^{\text{obs}}) y depredadores (yiobsy_i^{\text{obs}}) a lo largo del tiempo, nuestra función de costo (o pérdida) a minimizar será:

minθL(θ)=i=1N[(xiobsx(ti;θ))2+(yiobsy(ti;θ))2]\min_{\theta} \mathcal{L}(\theta) = \sum_{i=1}^{N} \left[ (x_i^{\text{obs}} - x(t_i; \theta))^2 + (y_i^{\text{obs}} - y(t_i; \theta))^2 \right]

Un optimizador resolverá numéricamente las ODEs de Lotka-Volterra en cada iteración, ajustando sistemáticamente el vector θ=(α,γ,β,η)R4\theta = (\alpha, \gamma, \beta, \eta)^\top \in \mathbb{R}^4 hasta que la trayectoria predicha pase lo más cerca posible de nuestros datos ruidosos.

Ecuaciones diferenciales ordinarias neuronales (NODEs)

Las Neural Ordinary Differential Equations (NODEs), introducidas formalmente por Chen et al. (2018), representan un cambio de paradigma al fusionar el aprendizaje profundo con los sistemas dinámicos continuos.

1. Generalización de Modelos Clásicos

Una forma intuitiva de entender las NODEs es viéndolas como una generalización de modelos dinámicos preexistentes. Por ejemplo, en el modelo de Lotka-Volterra, podemos reemplazar o aumentar los términos de interacción utilizando redes neuronales:

dxdt=αxNN1(x,y)\frac{dx}{dt} = \alpha x - \text{NN}_1(x, y)
dydt=βy+NN2(x,y)\frac{dy}{dt} = -\beta y + \text{NN}_2(x, y)

En este caso, los parámetros internos de NN1\text{NN}_1 y NN2\text{NN}_2 (pesos y sesgos) pasan a formar parte del vector global de parámetros a estimar, θ\theta, potencialmente junto con α\alpha y β\beta.

2. Definición Formal

Si generalizamos por completo (sin suponer ningún término de crecimiento o muerte específico predefinido), obtenemos una NODE. En su forma más abstracta, una NODE parametriza la derivada del estado continuo de un sistema directamente a través de una red neuronal:

dudt=NN(u;θ)\frac{du}{dt} = \text{NN}(u; \theta)

Donde uRnu \in \mathbb{R}^n representa el estado del sistema en un tiempo dado, y θ\theta engloba todos los parámetros entrenables de la red neuronal.

3. Propiedades y Consideraciones Clave

Implementación Computacional en Julia

A continuación, implementamos el modelo Lotka-Volterra en Julia.

Nota: El código completo de este ejemplo se puede referenciar acá: 01_LV_forward. Ahí, además de resolver el sistema, simulamos que tenemos datos empíricos con ruido y vemos cómo cambia el paisaje de la función de pérdida (Loss Landscape). Esos detalles quedan disponibles en el enlace para quienes deseen profundizar.

Nosotros vamos a contar brevemente los componentes principales de la resolución numérica.

En Julia, usamos ! en el nombre de la función para indicar que modifica sus argumentos in-place (es decir, no tenemos un return)

using DifferentialEquations
using Plots
using Statistics
using Random

# Definición del sistema de Ecuaciones Diferenciales Ordinarias
function lotka_volterra!(du, u, p, t)
    x, y = u            # x = presas, y = depredadores (esto es lo mismo que hacer u[1], u[2])
    α, β, δ, γ = p      # Desempaquetamos el vector p
    du[1] = α * x - β * x * y
    du[2] = δ * x * y - γ * y
end

# Definimos los parámetros de la ecuación
α = 1.0     # Nacimiento de presas
β = 0.1     # Tasa de depredación
δ = 0.075   # Reproducción del depredador
γ = 1.5     # Muerte del depredador
p_true = [α, β, δ, γ]

# Condiciones iniciales y tiempo
u0 = [10.0, 5.0]
tspan = (0.0, 30.0)

# ODEProblem es una función de Julia para resolver una ecuación diferencial ordinaria, 
# por eso le mandamos la función, la condición inicial y un tiempo para resolver.
prob = ODEProblem(lotka_volterra!, u0, tspan, p_true) # Acá definimos el problema matemático

# Acá resolvemos el problema, elegimos con qué solver (en nuestro caso Tsit5) y cada 
# cuánto se va a guardar, nivel de tolerancia, nivel de error. Se define la parte numérica.
sol = solve(prob, Tsit5(), saveat=0.1) 

# En sol está la solución al problema
print(sol)
References
  1. Ascher, U. M. (2008). Numerical methods for evolutionary differential equations. SIAM.
  2. Ramsay, J., & Hooker, G. (2017). Dynamic data analysis. Springer New York, New York, NY. Doi, 10, 978–1.
  3. Chen, R. T., Rubanova, Y., Bettencourt, J., & Duvenaud, D. K. (2018). Neural ordinary differential equations. Advances in Neural Information Processing Systems, 31.
  4. Goodfellow, I., Bengio, Y., Courville, A., & Bengio, Y. (2016). Deep learning (Vol. 1). MIT press Cambridge.